add basic markers to the sides of a channel view
This commit is contained in:
parent
e33ab321d0
commit
a6a66d99fd
504
src/app.zig
504
src/app.zig
@ -8,9 +8,10 @@ const Graph = @import("./graph.zig");
|
|||||||
const NIDaq = @import("ni-daq/root.zig");
|
const NIDaq = @import("ni-daq/root.zig");
|
||||||
const rect_utils = @import("./rect-utils.zig");
|
const rect_utils = @import("./rect-utils.zig");
|
||||||
const TaskPool = @import("ni-daq/task-pool.zig");
|
const TaskPool = @import("ni-daq/task-pool.zig");
|
||||||
|
const utils = @import("./utils.zig");
|
||||||
|
|
||||||
const remap = @import("./utils.zig").remap;
|
const remap = utils.remap;
|
||||||
const lerpColor = @import("./utils.zig").lerpColor;
|
const lerpColor = utils.lerpColor;
|
||||||
const log = std.log.scoped(.app);
|
const log = std.log.scoped(.app);
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const clamp = std.math.clamp;
|
const clamp = std.math.clamp;
|
||||||
@ -53,7 +54,7 @@ const ChannelView = struct {
|
|||||||
view_rect: Graph.ViewOptions,
|
view_rect: Graph.ViewOptions,
|
||||||
follow: bool = false,
|
follow: bool = false,
|
||||||
|
|
||||||
height: f32 = 200,
|
height: f32 = 300,
|
||||||
|
|
||||||
default_from: f32,
|
default_from: f32,
|
||||||
default_to: f32,
|
default_to: f32,
|
||||||
@ -498,7 +499,7 @@ fn showChannelViewSlider(self: *App, view_rect: *Graph.ViewOptions, sample_count
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn showChannelView(self: *App, channel_view: *ChannelView) !void {
|
fn showChannelViewGraph(self: *App, channel_view: *ChannelView) !void {
|
||||||
const source = self.getChannelSource(channel_view) orelse return;
|
const source = self.getChannelSource(channel_view) orelse return;
|
||||||
const samples = source.samples();
|
const samples = source.samples();
|
||||||
source.lockSamples();
|
source.lockSamples();
|
||||||
@ -506,11 +507,218 @@ fn showChannelView(self: *App, channel_view: *ChannelView) !void {
|
|||||||
|
|
||||||
var channel_rect_opts: *Graph.ViewOptions = &channel_view.view_rect;
|
var channel_rect_opts: *Graph.ViewOptions = &channel_view.view_rect;
|
||||||
|
|
||||||
|
const graph_box = self.ui.newBoxFromString("Graph");
|
||||||
|
graph_box.flags.insert(.clickable);
|
||||||
|
graph_box.flags.insert(.draggable);
|
||||||
|
graph_box.background = srcery.black;
|
||||||
|
graph_box.size.x = UI.Size.percent(1, 0);
|
||||||
|
graph_box.size.y = UI.Size.pixels(channel_view.height, 1);
|
||||||
|
self.ui.pushParent(graph_box);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
|
const graph_rect = graph_box.computedRect();
|
||||||
|
|
||||||
|
const signal = self.ui.signalFromBox(graph_box);
|
||||||
|
|
||||||
|
var axis = UI.Axis.X;
|
||||||
|
var zooming: bool = false;
|
||||||
|
var start_sample: ?f64 = null;
|
||||||
|
var stop_sample: ?f64 = null;
|
||||||
|
|
||||||
|
if (signal.hot) {
|
||||||
|
if (signal.shift_modifier) {
|
||||||
|
axis = UI.Axis.Y;
|
||||||
|
} else {
|
||||||
|
axis = UI.Axis.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.graph_start_sample) |graph_start_sample| {
|
||||||
|
axis = graph_start_sample.axis;
|
||||||
|
zooming = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mouse_sample: f64 = undefined;
|
||||||
|
if (axis == .X) {
|
||||||
|
const mouse_sample_index = channel_rect_opts.mapSampleXToIndex(0, graph_rect.width, signal.relative_mouse.x);
|
||||||
|
mouse_sample = mouse_sample_index;
|
||||||
|
} else if (axis == .Y) {
|
||||||
|
const mouse_sample_value = channel_rect_opts.mapSampleYToValue(0, graph_rect.height, signal.relative_mouse.y);
|
||||||
|
mouse_sample = mouse_sample_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_sample = mouse_sample;
|
||||||
|
|
||||||
|
if (signal.flags.contains(.right_pressed)) {
|
||||||
|
self.graph_start_sample = .{
|
||||||
|
.value = mouse_sample,
|
||||||
|
.axis = axis
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.graph_start_sample) |graph_start_sample| {
|
||||||
|
start_sample = graph_start_sample.value;
|
||||||
|
stop_sample = mouse_sample;
|
||||||
|
zooming = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zooming) {
|
||||||
|
if (axis == .X) {
|
||||||
|
graph_box.active_cursor = .mouse_cursor_resize_ew;
|
||||||
|
} else {
|
||||||
|
graph_box.active_cursor = .mouse_cursor_resize_ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.flags.contains(.right_released)) {
|
||||||
|
self.graph_start_sample = null;
|
||||||
|
|
||||||
|
if (start_sample != null and stop_sample != null) {
|
||||||
|
const lower_sample: f64 = @min(start_sample.?, stop_sample.?);
|
||||||
|
const higher_sample: f64 = @max(start_sample.?, stop_sample.?);
|
||||||
|
|
||||||
|
if (axis == .X) {
|
||||||
|
if (higher_sample - lower_sample > 1) {
|
||||||
|
channel_rect_opts.from = @floatCast(lower_sample);
|
||||||
|
channel_rect_opts.to = @floatCast(higher_sample);
|
||||||
|
} else {
|
||||||
|
// TODO: Show error message that selected range is too small
|
||||||
|
}
|
||||||
|
} else if (axis == .Y) {
|
||||||
|
if (higher_sample - lower_sample > 0.001) {
|
||||||
|
channel_rect_opts.min_value = lower_sample;
|
||||||
|
channel_rect_opts.max_value = higher_sample;
|
||||||
|
} else {
|
||||||
|
// TODO: Show error message that selected range is too small
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start_sample = null;
|
||||||
|
stop_sample = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_sample != null and stop_sample != null) {
|
||||||
|
|
||||||
|
const fill = self.ui.newBox(UI.Key.initNil());
|
||||||
|
fill.background = srcery.green.alpha(0.5);
|
||||||
|
|
||||||
|
if (axis == .X) {
|
||||||
|
const start_x = channel_rect_opts.mapSampleIndexToX(graph_rect.x, graph_rect.width, start_sample.?);
|
||||||
|
const stop_x = channel_rect_opts.mapSampleIndexToX(graph_rect.x, graph_rect.width, stop_sample.?);
|
||||||
|
|
||||||
|
fill.setFixedRect(.{
|
||||||
|
.x = @floatCast(@min(start_x, stop_x)),
|
||||||
|
.y = graph_rect.y,
|
||||||
|
.width = @floatCast(@abs(start_x - stop_x)),
|
||||||
|
.height = graph_rect.height
|
||||||
|
});
|
||||||
|
} else if (axis == .Y) {
|
||||||
|
const start_y = channel_rect_opts.mapSampleValueToY(graph_rect.y, graph_rect.height, start_sample.?);
|
||||||
|
const stop_y = channel_rect_opts.mapSampleValueToY(graph_rect.y, graph_rect.height, stop_sample.?);
|
||||||
|
|
||||||
|
fill.setFixedRect(.{
|
||||||
|
.x = graph_rect.x,
|
||||||
|
.y = @floatCast(@min(start_y, stop_y)),
|
||||||
|
.width = graph_rect.width,
|
||||||
|
.height = @floatCast(@abs(start_y - stop_y)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_sample) |sample| {
|
||||||
|
const marker = self.ui.newBox(UI.Key.initNil());
|
||||||
|
marker.background = srcery.green;
|
||||||
|
|
||||||
|
if (axis == .X) {
|
||||||
|
const value = samples[@intFromFloat(sample)];
|
||||||
|
marker.setFmtText(.text, "{d:0.2} | {d:0.6}", .{sample, value});
|
||||||
|
marker.setFixedRect(UI.Rect{
|
||||||
|
.x = @floatCast(channel_rect_opts.mapSampleIndexToX(graph_rect.x, graph_rect.width, sample)),
|
||||||
|
.y = graph_rect.y,
|
||||||
|
.width = 1,
|
||||||
|
.height = graph_rect.height
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (axis == .Y) {
|
||||||
|
marker.setFmtText(.text, "{d:0.2}", .{sample});
|
||||||
|
marker.setFixedRect(UI.Rect{
|
||||||
|
.x = graph_rect.x,
|
||||||
|
.y = @floatCast(channel_rect_opts.mapSampleValueToY(graph_rect.y, graph_rect.height, sample)),
|
||||||
|
.width = graph_rect.width,
|
||||||
|
.height = 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stop_sample) |sample| {
|
||||||
|
const marker = self.ui.newBox(UI.Key.initNil());
|
||||||
|
marker.background = srcery.green;
|
||||||
|
|
||||||
|
marker.setFmtText(.text, "{d:0.2}", .{sample});
|
||||||
|
if (axis == .X) {
|
||||||
|
marker.setFixedRect(.{
|
||||||
|
.x = @floatCast(channel_rect_opts.mapSampleIndexToX(graph_rect.x, graph_rect.width, sample)),
|
||||||
|
.y = graph_rect.y,
|
||||||
|
.width = 1,
|
||||||
|
.height = graph_rect.height
|
||||||
|
});
|
||||||
|
} else if (axis == .Y) {
|
||||||
|
marker.setFixedRect(.{
|
||||||
|
.x = graph_rect.x,
|
||||||
|
.y = @floatCast(channel_rect_opts.mapSampleValueToY(graph_rect.y, graph_rect.height, sample)),
|
||||||
|
.width = graph_rect.width,
|
||||||
|
.height = 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (signal.dragged()) {
|
||||||
|
const middle_mouse_drag = signal.flags.contains(.middle_dragging);
|
||||||
|
var x_offset: f64 = 0;
|
||||||
|
var y_offset: f64 = 0;
|
||||||
|
|
||||||
|
if (signal.shift_modifier or middle_mouse_drag) {
|
||||||
|
y_offset = remap(
|
||||||
|
f64,
|
||||||
|
0, graph_rect.height,
|
||||||
|
0, channel_rect_opts.max_value - channel_rect_opts.min_value,
|
||||||
|
signal.drag.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!signal.shift_modifier or middle_mouse_drag) {
|
||||||
|
x_offset = remap(
|
||||||
|
f64,
|
||||||
|
0, graph_rect.width,
|
||||||
|
0, channel_rect_opts.to - channel_rect_opts.from,
|
||||||
|
signal.drag.x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
channel_rect_opts.from -= @floatCast(x_offset);
|
||||||
|
channel_rect_opts.to -= @floatCast(x_offset);
|
||||||
|
channel_rect_opts.max_value += @floatCast(y_offset);
|
||||||
|
channel_rect_opts.min_value += @floatCast(y_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Graph.drawCached(&channel_view.view_cache, graph_box.persistent.size, channel_rect_opts.*, samples);
|
||||||
|
if (channel_view.view_cache.texture) |texture| {
|
||||||
|
graph_box.texture = texture.texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn showChannelView(self: *App, channel_view: *ChannelView) !void {
|
||||||
|
const source = self.getChannelSource(channel_view) orelse return;
|
||||||
|
|
||||||
|
var channel_rect_opts: *Graph.ViewOptions = &channel_view.view_rect;
|
||||||
|
|
||||||
const channel_box = self.ui.newBoxFromPtr(channel_view);
|
const channel_box = self.ui.newBoxFromPtr(channel_view);
|
||||||
channel_box.background = rl.Color.blue;
|
channel_box.background = rl.Color.blue;
|
||||||
channel_box.layout_axis = .Y;
|
channel_box.layout_axis = .Y;
|
||||||
channel_box.size.x = UI.Size.percent(1, 0);
|
channel_box.size.x = UI.Size.percent(1, 0);
|
||||||
channel_box.size.y = UI.Size.childrenSum(1);
|
channel_box.size.y = UI.Size.pixels(channel_view.height, 1);
|
||||||
self.ui.pushParent(channel_box);
|
self.ui.pushParent(channel_box);
|
||||||
defer self.ui.popParent();
|
defer self.ui.popParent();
|
||||||
|
|
||||||
@ -589,207 +797,127 @@ fn showChannelView(self: *App, channel_view: *ChannelView) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const channel_box = self.ui.newBox(UI.Key.initNil());
|
||||||
|
// channel_box.layout_axis = .Y;
|
||||||
|
// channel_box.size.x = UI.Size.percent(1, 0);
|
||||||
|
// channel_box.size.y = UI.Size.childrenSum(1);
|
||||||
|
// self.ui.pushParent(channel_box);
|
||||||
|
// defer self.ui.popParent();
|
||||||
|
|
||||||
|
var y_axis_markers: *UI.Box = undefined;
|
||||||
|
var x_axis_markers: *UI.Box = undefined;
|
||||||
|
|
||||||
{
|
{
|
||||||
const graph_box = self.ui.newBoxFromString("Graph");
|
const y_axis_container = self.ui.newBox(UI.Key.initNil());
|
||||||
graph_box.flags.insert(.clickable);
|
y_axis_container.layout_axis = .X;
|
||||||
graph_box.flags.insert(.draggable);
|
y_axis_container.size.x = UI.Size.percent(1, 0);
|
||||||
graph_box.background = srcery.black;
|
y_axis_container.size.y = UI.Size.percent(1, 0);
|
||||||
graph_box.size.x = UI.Size.percent(1, 0);
|
self.ui.pushParent(y_axis_container);
|
||||||
graph_box.size.y = UI.Size.pixels(channel_view.height, 1);
|
|
||||||
self.ui.pushParent(graph_box);
|
|
||||||
defer self.ui.popParent();
|
defer self.ui.popParent();
|
||||||
|
|
||||||
const graph_rect = graph_box.computedRect();
|
y_axis_markers = self.ui.newBoxFromString("Y axis markers");
|
||||||
|
y_axis_markers.background = rl.Color.blue;
|
||||||
|
y_axis_markers.size.x = UI.Size.pixels(64, 1);
|
||||||
|
y_axis_markers.size.y = UI.Size.percent(1, 0);
|
||||||
|
|
||||||
const signal = self.ui.signalFromBox(graph_box);
|
try self.showChannelViewGraph(channel_view);
|
||||||
|
}
|
||||||
|
|
||||||
var axis = UI.Axis.X;
|
|
||||||
var zooming: bool = false;
|
|
||||||
var start_sample: ?f64 = null;
|
|
||||||
var stop_sample: ?f64 = null;
|
|
||||||
|
|
||||||
if (signal.hot) {
|
{
|
||||||
if (signal.shift_modifier) {
|
const horizontal_container = self.ui.newBox(UI.Key.initNil());
|
||||||
axis = UI.Axis.Y;
|
horizontal_container.layout_axis = .X;
|
||||||
} else {
|
horizontal_container.size.x = UI.Size.childrenSum(0);
|
||||||
axis = UI.Axis.X;
|
horizontal_container.size.y = UI.Size.childrenSum(1);
|
||||||
}
|
self.ui.pushParent(horizontal_container);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
if (self.graph_start_sample) |graph_start_sample| {
|
const fullscreen_button = self.ui.newBoxFromString("Fullscreen");
|
||||||
axis = graph_start_sample.axis;
|
fullscreen_button.background = rl.Color.red;
|
||||||
zooming = true;
|
fullscreen_button.size.x = UI.Size.pixels(y_axis_markers.computedRect().width, 1);
|
||||||
}
|
fullscreen_button.size.y = UI.Size.pixels(32, 1);
|
||||||
|
|
||||||
var mouse_sample: f64 = undefined;
|
x_axis_markers = self.ui.newBoxFromString("X axis markers");
|
||||||
if (axis == .X) {
|
x_axis_markers.background = rl.Color.pink;
|
||||||
const mouse_sample_index = channel_rect_opts.mapSampleXToIndex(0, graph_rect.width, signal.relative_mouse.x);
|
x_axis_markers.size.x = UI.Size.percent(1, 0);
|
||||||
mouse_sample = mouse_sample_index;
|
x_axis_markers.size.y = UI.Size.pixels(32, 1);
|
||||||
} else if (axis == .Y) {
|
self.ui.pushParent(x_axis_markers);
|
||||||
const mouse_sample_value = channel_rect_opts.mapSampleYToValue(0, graph_rect.height, signal.relative_mouse.y);
|
defer self.ui.popParent();
|
||||||
mouse_sample = mouse_sample_value;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
start_sample = mouse_sample;
|
{
|
||||||
|
self.ui.pushParent(y_axis_markers);
|
||||||
|
defer self.ui.popParent();
|
||||||
|
|
||||||
if (signal.flags.contains(.right_pressed)) {
|
const y_axis_rect = y_axis_markers.computedRect();
|
||||||
self.graph_start_sample = .{
|
|
||||||
.value = mouse_sample,
|
|
||||||
.axis = axis
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.graph_start_sample) |graph_start_sample| {
|
const min_gap_between_markers = 8;
|
||||||
start_sample = graph_start_sample.value;
|
|
||||||
stop_sample = mouse_sample;
|
|
||||||
zooming = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zooming) {
|
const y_range = channel_rect_opts.max_value - channel_rect_opts.min_value;
|
||||||
if (axis == .X) {
|
|
||||||
graph_box.active_cursor = .mouse_cursor_resize_ew;
|
|
||||||
} else {
|
|
||||||
graph_box.active_cursor = .mouse_cursor_resize_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signal.flags.contains(.right_released)) {
|
var axis_marker_size = min_gap_between_markers / y_axis_rect.height * y_range;
|
||||||
self.graph_start_sample = null;
|
axis_marker_size = @ceil(axis_marker_size);
|
||||||
|
|
||||||
if (start_sample != null and stop_sample != null) {
|
var marker = utils.roundNearestUp(f64, @max(channel_rect_opts.min_value, channel_view.default_min_value), axis_marker_size);
|
||||||
const lower_sample: f64 = @min(start_sample.?, stop_sample.?);
|
while (marker < @min(channel_rect_opts.max_value, channel_view.default_max_value)) : (marker += axis_marker_size) {
|
||||||
const higher_sample: f64 = @max(start_sample.?, stop_sample.?);
|
const marker_box = self.ui.newBox(UI.Key.initNil());
|
||||||
|
marker_box.background = rl.Color.yellow;
|
||||||
|
marker_box.setFixedRect(.{
|
||||||
|
.width = y_axis_rect.width/5,
|
||||||
|
.height = 1,
|
||||||
|
.x = y_axis_rect.x + y_axis_rect.width/5*4,
|
||||||
|
.y = @floatCast(remap(
|
||||||
|
f64,
|
||||||
|
channel_rect_opts.max_value, channel_rect_opts.min_value,
|
||||||
|
y_axis_rect.y, y_axis_rect.y + y_axis_rect.height,
|
||||||
|
marker
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
if (axis == .X) {
|
const label_box = self.ui.newBox(UI.Key.initNil());
|
||||||
if (higher_sample - lower_sample > 1) {
|
label_box.setFmtText(.text, "{d:.03}", .{marker});
|
||||||
channel_rect_opts.from = @floatCast(lower_sample);
|
label_box.size.x = UI.Size.text(0, 1);
|
||||||
channel_rect_opts.to = @floatCast(higher_sample);
|
label_box.size.y = UI.Size.text(0, 1);
|
||||||
} else {
|
label_box.flags.insert(.text_left_align);
|
||||||
// TODO: Show error message that selected range is too small
|
label_box.setFixedRect(.{
|
||||||
}
|
.width = y_axis_rect.width,
|
||||||
} else if (axis == .Y) {
|
.height = 1,
|
||||||
if (higher_sample - lower_sample > 0.01) {
|
.x = y_axis_rect.x,
|
||||||
channel_rect_opts.min_value = lower_sample;
|
.y = @floatCast(remap(
|
||||||
channel_rect_opts.max_value = higher_sample;
|
f64,
|
||||||
} else {
|
channel_rect_opts.max_value, channel_rect_opts.min_value,
|
||||||
// TODO: Show error message that selected range is too small
|
y_axis_rect.y, y_axis_rect.y + y_axis_rect.height,
|
||||||
}
|
marker
|
||||||
}
|
))
|
||||||
}
|
});
|
||||||
|
|
||||||
start_sample = null;
|
|
||||||
stop_sample = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start_sample != null and stop_sample != null) {
|
|
||||||
|
|
||||||
const fill = self.ui.newBox(UI.Key.initNil());
|
|
||||||
fill.background = srcery.green.alpha(0.5);
|
|
||||||
|
|
||||||
if (axis == .X) {
|
|
||||||
const start_x = channel_rect_opts.mapSampleIndexToX(graph_rect.x, graph_rect.width, start_sample.?);
|
|
||||||
const stop_x = channel_rect_opts.mapSampleIndexToX(graph_rect.x, graph_rect.width, stop_sample.?);
|
|
||||||
|
|
||||||
fill.setFixedRect(.{
|
|
||||||
.x = @floatCast(@min(start_x, stop_x)),
|
|
||||||
.y = graph_rect.y,
|
|
||||||
.width = @floatCast(@abs(start_x - stop_x)),
|
|
||||||
.height = graph_rect.height
|
|
||||||
});
|
|
||||||
} else if (axis == .Y) {
|
|
||||||
const start_y = channel_rect_opts.mapSampleValueToY(graph_rect.y, graph_rect.height, start_sample.?);
|
|
||||||
const stop_y = channel_rect_opts.mapSampleValueToY(graph_rect.y, graph_rect.height, stop_sample.?);
|
|
||||||
|
|
||||||
fill.setFixedRect(.{
|
|
||||||
.x = graph_rect.x,
|
|
||||||
.y = @floatCast(@min(start_y, stop_y)),
|
|
||||||
.width = graph_rect.width,
|
|
||||||
.height = @floatCast(@abs(start_y - stop_y)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start_sample) |sample| {
|
|
||||||
const marker = self.ui.newBox(UI.Key.initNil());
|
|
||||||
marker.background = srcery.green;
|
|
||||||
|
|
||||||
if (axis == .X) {
|
|
||||||
const value = samples[@intFromFloat(sample)];
|
|
||||||
marker.setFmtText(.text, "{d:0.2} | {d:0.6}", .{sample, value});
|
|
||||||
marker.setFixedRect(UI.Rect{
|
|
||||||
.x = @floatCast(channel_rect_opts.mapSampleIndexToX(graph_rect.x, graph_rect.width, sample)),
|
|
||||||
.y = graph_rect.y,
|
|
||||||
.width = 1,
|
|
||||||
.height = graph_rect.height
|
|
||||||
});
|
|
||||||
|
|
||||||
} else if (axis == .Y) {
|
|
||||||
marker.setFmtText(.text, "{d:0.2}", .{sample});
|
|
||||||
marker.setFixedRect(UI.Rect{
|
|
||||||
.x = graph_rect.x,
|
|
||||||
.y = @floatCast(channel_rect_opts.mapSampleValueToY(graph_rect.y, graph_rect.height, sample)),
|
|
||||||
.width = graph_rect.width,
|
|
||||||
.height = 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stop_sample) |sample| {
|
|
||||||
const marker = self.ui.newBox(UI.Key.initNil());
|
|
||||||
marker.background = srcery.green;
|
|
||||||
|
|
||||||
marker.setFmtText(.text, "{d:0.2}", .{sample});
|
|
||||||
if (axis == .X) {
|
|
||||||
marker.setFixedRect(.{
|
|
||||||
.x = @floatCast(channel_rect_opts.mapSampleIndexToX(graph_rect.x, graph_rect.width, sample)),
|
|
||||||
.y = graph_rect.y,
|
|
||||||
.width = 1,
|
|
||||||
.height = graph_rect.height
|
|
||||||
});
|
|
||||||
} else if (axis == .Y) {
|
|
||||||
marker.setFixedRect(.{
|
|
||||||
.x = graph_rect.x,
|
|
||||||
.y = @floatCast(channel_rect_opts.mapSampleValueToY(graph_rect.y, graph_rect.height, sample)),
|
|
||||||
.width = graph_rect.width,
|
|
||||||
.height = 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (signal.dragged()) {
|
|
||||||
if (signal.shift_modifier) {
|
|
||||||
const drag_offset = remap(
|
|
||||||
f64,
|
|
||||||
0, graph_rect.height,
|
|
||||||
0, channel_rect_opts.max_value - channel_rect_opts.min_value,
|
|
||||||
signal.drag.y
|
|
||||||
);
|
|
||||||
|
|
||||||
channel_rect_opts.max_value += @floatCast(drag_offset);
|
|
||||||
channel_rect_opts.min_value += @floatCast(drag_offset);
|
|
||||||
} else {
|
|
||||||
const drag_offset = remap(
|
|
||||||
f64,
|
|
||||||
0, graph_rect.width,
|
|
||||||
0, channel_rect_opts.to - channel_rect_opts.from,
|
|
||||||
signal.drag.x
|
|
||||||
);
|
|
||||||
|
|
||||||
channel_rect_opts.from -= @floatCast(drag_offset);
|
|
||||||
channel_rect_opts.to -= @floatCast(drag_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Graph.drawCached(&channel_view.view_cache, graph_box.persistent.size, channel_rect_opts.*, samples);
|
|
||||||
if (channel_view.view_cache.texture) |texture| {
|
|
||||||
graph_box.texture = texture.texture;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.showChannelViewSlider(
|
{
|
||||||
&channel_view.view_rect,
|
self.ui.pushParent(x_axis_markers);
|
||||||
@floatFromInt(samples.len)
|
defer self.ui.popParent();
|
||||||
);
|
|
||||||
|
const y_axis_rect = x_axis_markers.computedRect();
|
||||||
|
|
||||||
|
const axis_marker_size = 100000;
|
||||||
|
|
||||||
|
var marker = utils.roundNearestUp(f64, @max(channel_rect_opts.from, channel_view.default_from), axis_marker_size);
|
||||||
|
while (marker < @min(channel_rect_opts.to, channel_view.default_to)) : (marker += axis_marker_size) {
|
||||||
|
const marker_box = self.ui.newBox(UI.Key.initNil());
|
||||||
|
marker_box.background = rl.Color.yellow;
|
||||||
|
marker_box.setFixedRect(.{
|
||||||
|
.width = 1,
|
||||||
|
.height = y_axis_rect.height/2,
|
||||||
|
.x = @floatCast(remap(
|
||||||
|
f64,
|
||||||
|
channel_rect_opts.from, channel_rect_opts.to,
|
||||||
|
y_axis_rect.x, y_axis_rect.x + y_axis_rect.width,
|
||||||
|
marker
|
||||||
|
)),
|
||||||
|
.y = y_axis_rect.y,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn showChannelsWindow(self: *App) !void {
|
fn showChannelsWindow(self: *App) !void {
|
||||||
|
@ -158,7 +158,7 @@ pub fn main() !void {
|
|||||||
if (builtin.mode == .Debug) {
|
if (builtin.mode == .Debug) {
|
||||||
// try app.appendChannelFromDevice("Dev1/ai0");
|
// try app.appendChannelFromDevice("Dev1/ai0");
|
||||||
try app.appendChannelFromFile("samples/HeLa Cx37_ 40nM GFX + 35uM Propofol_18-Sep-2024_0003_I.bin");
|
try app.appendChannelFromFile("samples/HeLa Cx37_ 40nM GFX + 35uM Propofol_18-Sep-2024_0003_I.bin");
|
||||||
try app.appendChannelFromFile("samples/HeLa Cx37_ 40nM GFX + 35uM Propofol_18-Sep-2024_0003_IjStim.bin");
|
// try app.appendChannelFromFile("samples/HeLa Cx37_ 40nM GFX + 35uM Propofol_18-Sep-2024_0003_IjStim.bin");
|
||||||
// try app.appendChannelFromFile("samples/HeLa Cx37_ 40nM GFX + 35uM Propofol_18-Sep-2024_0003_IjStim.bin");
|
// try app.appendChannelFromFile("samples/HeLa Cx37_ 40nM GFX + 35uM Propofol_18-Sep-2024_0003_IjStim.bin");
|
||||||
// try app.appendChannelFromFile("samples/HeLa Cx37_ 40nM GFX + 35uM Propofol_18-Sep-2024_0003_IjStim.bin");
|
// try app.appendChannelFromFile("samples/HeLa Cx37_ 40nM GFX + 35uM Propofol_18-Sep-2024_0003_IjStim.bin");
|
||||||
}
|
}
|
||||||
|
23
src/ui.zig
23
src/ui.zig
@ -16,7 +16,7 @@ const clamp = std.math.clamp;
|
|||||||
const UI = @This();
|
const UI = @This();
|
||||||
|
|
||||||
const debug = false;
|
const debug = false;
|
||||||
const max_boxes = 512;
|
const max_boxes = 5120;
|
||||||
const max_events = 256;
|
const max_events = 256;
|
||||||
|
|
||||||
const RectFormatted = struct {
|
const RectFormatted = struct {
|
||||||
@ -176,15 +176,19 @@ pub const Event = union(enum) {
|
|||||||
pub const Signal = struct {
|
pub const Signal = struct {
|
||||||
pub const Flag = enum {
|
pub const Flag = enum {
|
||||||
left_pressed,
|
left_pressed,
|
||||||
|
middle_pressed,
|
||||||
right_pressed,
|
right_pressed,
|
||||||
|
|
||||||
left_released,
|
left_released,
|
||||||
|
middle_released,
|
||||||
right_released,
|
right_released,
|
||||||
|
|
||||||
left_clicked,
|
left_clicked,
|
||||||
|
middle_clicked,
|
||||||
right_clicked,
|
right_clicked,
|
||||||
|
|
||||||
left_dragging,
|
left_dragging,
|
||||||
|
middle_dragging,
|
||||||
right_dragging,
|
right_dragging,
|
||||||
|
|
||||||
scrolled
|
scrolled
|
||||||
@ -199,11 +203,11 @@ pub const Signal = struct {
|
|||||||
shift_modifier: bool = false,
|
shift_modifier: bool = false,
|
||||||
|
|
||||||
pub fn clicked(self: Signal) bool {
|
pub fn clicked(self: Signal) bool {
|
||||||
return self.flags.contains(.left_clicked) or self.flags.contains(.right_clicked);
|
return self.flags.contains(.left_clicked) or self.flags.contains(.right_clicked) or self.flags.contains(.middle_clicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dragged(self: Signal) bool {
|
pub fn dragged(self: Signal) bool {
|
||||||
return self.flags.contains(.left_dragging) or self.flags.contains(.right_dragging);
|
return self.flags.contains(.left_dragging) or self.flags.contains(.right_dragging) or self.flags.contains(.middle_dragging);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scrolled(self: Signal) bool {
|
pub fn scrolled(self: Signal) bool {
|
||||||
@ -215,6 +219,8 @@ pub const Signal = struct {
|
|||||||
self.flags.insert(.left_pressed);
|
self.flags.insert(.left_pressed);
|
||||||
} else if (mouse_button == .mouse_button_right) {
|
} else if (mouse_button == .mouse_button_right) {
|
||||||
self.flags.insert(.right_pressed);
|
self.flags.insert(.right_pressed);
|
||||||
|
} else if (mouse_button == .mouse_button_middle) {
|
||||||
|
self.flags.insert(.middle_pressed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,6 +229,8 @@ pub const Signal = struct {
|
|||||||
self.flags.insert(.left_released);
|
self.flags.insert(.left_released);
|
||||||
} else if (mouse_button == .mouse_button_right) {
|
} else if (mouse_button == .mouse_button_right) {
|
||||||
self.flags.insert(.right_released);
|
self.flags.insert(.right_released);
|
||||||
|
} else if (mouse_button == .mouse_button_middle) {
|
||||||
|
self.flags.insert(.middle_released);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,6 +239,8 @@ pub const Signal = struct {
|
|||||||
self.flags.insert(.left_clicked);
|
self.flags.insert(.left_clicked);
|
||||||
} else if (mouse_button == .mouse_button_right) {
|
} else if (mouse_button == .mouse_button_right) {
|
||||||
self.flags.insert(.right_clicked);
|
self.flags.insert(.right_clicked);
|
||||||
|
} else if (mouse_button == .mouse_button_middle) {
|
||||||
|
self.flags.insert(.middle_clicked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,6 +249,8 @@ pub const Signal = struct {
|
|||||||
self.flags.insert(.left_dragging);
|
self.flags.insert(.left_dragging);
|
||||||
} else if (mouse_button == .mouse_button_right) {
|
} else if (mouse_button == .mouse_button_right) {
|
||||||
self.flags.insert(.right_dragging);
|
self.flags.insert(.right_dragging);
|
||||||
|
} else if (mouse_button == .mouse_button_middle) {
|
||||||
|
self.flags.insert(.middle_dragging);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -856,8 +868,6 @@ fn drawBox(self: *UI, box: *Box) void {
|
|||||||
text_rect.y += (box_rect.height - text_size.y) / 2;
|
text_rect.y += (box_rect.height - text_size.y) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// text_rect.y += box.persistent.active * text_rect.height*0.1;
|
|
||||||
|
|
||||||
const text_color = utils.shiftColorInHSV(text.color, value_shift);
|
const text_color = utils.shiftColorInHSV(text.color, value_shift);
|
||||||
font.drawText(text.content, .{ .x = text_rect.x, .y = text_rect.y }, text_color);
|
font.drawText(text.content, .{ .x = text_rect.x, .y = text_rect.y }, text_color);
|
||||||
|
|
||||||
@ -1387,7 +1397,8 @@ pub fn signalFromBox(self: *UI, box: *Box) Signal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (draggable and self.mouse_delta.equals(Vec2.zero()) == 0) {
|
if (draggable and self.mouse_delta.equals(Vec2.zero()) == 0) {
|
||||||
inline for (.{ rl.MouseButton.mouse_button_left, rl.MouseButton.mouse_button_right }) |mouse_button| {
|
const mouse_buttons = [_]rl.MouseButton{ .mouse_button_left, .mouse_button_right, .mouse_button_middle };
|
||||||
|
inline for (mouse_buttons) |mouse_button| {
|
||||||
const active_box = self.active_box_keys.get(mouse_button);
|
const active_box = self.active_box_keys.get(mouse_button);
|
||||||
|
|
||||||
if (active_box != null and active_box.?.eql(key)) {
|
if (active_box != null and active_box.?.eql(key)) {
|
||||||
|
@ -53,3 +53,11 @@ pub fn remap(comptime T: type, from_min: T, from_max: T, to_min: T, to_max: T, v
|
|||||||
const t = (value - from_min) / (from_max - from_min);
|
const t = (value - from_min) / (from_max - from_min);
|
||||||
return std.math.lerp(to_min, to_max, t);
|
return std.math.lerp(to_min, to_max, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn roundNearestUp(comptime T: type, value: T, multiple: T) T {
|
||||||
|
return @ceil(value / multiple) * multiple;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn roundNearestDown(comptime T: type, value: T, multiple: T) T {
|
||||||
|
return @floor(value / multiple) * multiple;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user