add preview for sample generation
This commit is contained in:
parent
31d0af0a5c
commit
c588956226
25
src/app.zig
25
src/app.zig
@ -42,7 +42,7 @@ const FileChannel = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const DeviceChannel = struct {
|
||||
pub const DeviceChannel = struct {
|
||||
const ChannelName = std.BoundedArray(u8, NIDaq.max_channel_name_size + 1); // +1 for null byte
|
||||
const Direction = enum { input, output };
|
||||
|
||||
@ -76,6 +76,20 @@ const DeviceChannel = struct {
|
||||
pub fn getChannelName(self: *DeviceChannel) [:0]const u8 {
|
||||
return utils.getBoundedStringZ(&self.channel_name);
|
||||
}
|
||||
|
||||
pub fn generateSine(samples: *std.ArrayList(f64), sample_rate: f64, frequency: f64, amplitude: f64) !void {
|
||||
samples.clearRetainingCapacity();
|
||||
|
||||
const sample_count: usize = @intFromFloat(@ceil(sample_rate));
|
||||
assert(sample_count >= 1);
|
||||
try samples.ensureTotalCapacity(sample_count);
|
||||
|
||||
for (0..sample_count) |i| {
|
||||
const i_f64: f64 = @floatFromInt(i);
|
||||
const sample = std.math.sin(i_f64 / sample_rate * frequency * std.math.pi * 2) * amplitude;
|
||||
samples.appendAssumeCapacity(sample);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const ChannelView = struct {
|
||||
@ -445,7 +459,7 @@ pub fn activateDeviceChannel(self: *App, channel_view: *ChannelView) !void {
|
||||
const task = try ni_daq.createTask(null);
|
||||
errdefer task.clear();
|
||||
|
||||
const sample_rate = device_channel.max_sample_rate;
|
||||
const sample_rate = channel_view.sample_rate.?;
|
||||
try task.createAIVoltageChannel(.{
|
||||
.channel = device_channel.getChannelName(),
|
||||
.min_value = device_channel.min_value,
|
||||
@ -468,13 +482,10 @@ pub fn activateDeviceChannel(self: *App, channel_view: *ChannelView) !void {
|
||||
const task = try ni_daq.createTask(null);
|
||||
errdefer task.clear();
|
||||
|
||||
device_channel.write_pattern.clearAndFree();
|
||||
try device_channel.write_pattern.appendNTimes(2, 1000);
|
||||
|
||||
const write_pattern = device_channel.write_pattern.items;
|
||||
const samples_per_channel: u32 = @intCast(write_pattern.len);
|
||||
|
||||
const sample_rate = 5000; // device_channel.max_sample_rate;
|
||||
const sample_rate = channel_view.sample_rate.?;
|
||||
try task.createAOVoltageChannel(.{
|
||||
.channel = device_channel.getChannelName(),
|
||||
.min_value = device_channel.min_value,
|
||||
@ -648,7 +659,7 @@ pub fn appendChannelFromDevice(self: *App, channel_name: []const u8) !void {
|
||||
.x_range = RangeF64.init(0, 0),
|
||||
.y_range = RangeF64.init(max_value, min_value),
|
||||
.source = .{ .device = device_channel_index },
|
||||
.sample_rate = max_sample_rate,
|
||||
.sample_rate = @min(max_sample_rate, 5000),
|
||||
.unit = unit
|
||||
});
|
||||
errdefer _ = self.channel_views.pop();
|
||||
|
@ -47,6 +47,9 @@ protocol_modal: ?*ChannelView = null,
|
||||
frequency_input: UI.TextInputStorage,
|
||||
amplitude_input: UI.TextInputStorage,
|
||||
protocol_error_message: ?[]const u8 = null,
|
||||
protocol_graph_cache: Graph.Cache = .{},
|
||||
preview_samples: std.ArrayList(f64),
|
||||
preview_samples_y_range: RangeF64 = RangeF64.init(0, 0),
|
||||
|
||||
pub fn init(app: *App) !MainScreen {
|
||||
const allocator = app.allocator;
|
||||
@ -54,10 +57,11 @@ pub fn init(app: *App) !MainScreen {
|
||||
var self = MainScreen{
|
||||
.app = app,
|
||||
.frequency_input = UI.TextInputStorage.init(allocator),
|
||||
.amplitude_input = UI.TextInputStorage.init(allocator)
|
||||
.amplitude_input = UI.TextInputStorage.init(allocator),
|
||||
.preview_samples = std.ArrayList(f64).init(allocator)
|
||||
};
|
||||
|
||||
try self.frequency_input.setText("1000");
|
||||
try self.frequency_input.setText("10");
|
||||
try self.amplitude_input.setText("10");
|
||||
|
||||
return self;
|
||||
@ -66,6 +70,7 @@ pub fn init(app: *App) !MainScreen {
|
||||
pub fn deinit(self: *MainScreen) void {
|
||||
self.frequency_input.deinit();
|
||||
self.amplitude_input.deinit();
|
||||
self.preview_samples.deinit();
|
||||
|
||||
self.clearProtocolErrorMessage();
|
||||
}
|
||||
@ -697,6 +702,7 @@ fn showChannelView(self: *MainScreen, channel_view: *ChannelView, height: UI.Siz
|
||||
|
||||
fn openProtocolModal(self: *MainScreen, channel_view: *ChannelView) !void {
|
||||
self.protocol_modal = channel_view;
|
||||
self.protocol_graph_cache.deinit();
|
||||
}
|
||||
|
||||
fn closeModal(self: *MainScreen) void {
|
||||
@ -706,6 +712,8 @@ fn closeModal(self: *MainScreen) void {
|
||||
pub fn showProtocolModal(self: *MainScreen, channel_view: *ChannelView) !void {
|
||||
var ui = &self.app.ui;
|
||||
|
||||
const device_channel = self.app.getChannelSourceDevice(channel_view).?;
|
||||
|
||||
const container = ui.createBox(.{
|
||||
.key = ui.keyFromString("Protocol modal"),
|
||||
.background = srcery.black,
|
||||
@ -719,6 +727,25 @@ pub fn showProtocolModal(self: *MainScreen, channel_view: *ChannelView) !void {
|
||||
container.beginChildren();
|
||||
defer container.endChildren();
|
||||
|
||||
{
|
||||
const protocol_view = ui.createBox(.{
|
||||
.key = ui.keyFromString("Protocol view"),
|
||||
.size_x = UI.Sizing.initGrowFull(),
|
||||
.size_y = UI.Sizing.initFixedPixels(ui.rem(4)),
|
||||
.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 })
|
||||
});
|
||||
|
||||
const samples = self.preview_samples.items;
|
||||
const view_rect = Graph.ViewOptions{
|
||||
.x_range = RangeF64.init(0, @floatFromInt(samples.len)),
|
||||
.y_range = self.preview_samples_y_range
|
||||
};
|
||||
Graph.drawCached(&self.protocol_graph_cache, protocol_view.persistent.size, view_rect, samples);
|
||||
if (self.protocol_graph_cache.texture) |texture| {
|
||||
protocol_view.texture = texture.texture;
|
||||
}
|
||||
}
|
||||
|
||||
const FormInput = struct {
|
||||
name: []const u8,
|
||||
storage: *UI.TextInputStorage,
|
||||
@ -740,6 +767,7 @@ pub fn showProtocolModal(self: *MainScreen, channel_view: *ChannelView) !void {
|
||||
.value = &litude
|
||||
},
|
||||
};
|
||||
var any_input_modified = false;
|
||||
|
||||
for (form_inputs) |form_input| {
|
||||
const label = form_input.name;
|
||||
@ -760,6 +788,35 @@ pub fn showProtocolModal(self: *MainScreen, channel_view: *ChannelView) !void {
|
||||
label_box.alignment.y = .center;
|
||||
|
||||
try ui.textInput(ui.keyFromString("Text input"), text_input_storage);
|
||||
|
||||
any_input_modified = any_input_modified or text_input_storage.modified;
|
||||
}
|
||||
|
||||
if (any_input_modified) {
|
||||
self.clearProtocolErrorMessage();
|
||||
|
||||
for (form_inputs) |form_input| {
|
||||
const label = form_input.name;
|
||||
const text_input_storage: *UI.TextInputStorage = form_input.storage;
|
||||
|
||||
const number = std.fmt.parseFloat(f32, text_input_storage.buffer.items) catch {
|
||||
try self.setProtocolErrorMessage("ERROR: {s} must be a number", .{ label });
|
||||
continue;
|
||||
};
|
||||
|
||||
if (number <= 0) {
|
||||
try self.setProtocolErrorMessage("ERROR: {s} must be positive", .{ label });
|
||||
continue;
|
||||
}
|
||||
|
||||
form_input.value.* = number;
|
||||
}
|
||||
}
|
||||
|
||||
if (self.protocol_error_message == null and any_input_modified) {
|
||||
try App.DeviceChannel.generateSine(&self.preview_samples, channel_view.sample_rate.?, frequency, amplitude);
|
||||
self.preview_samples_y_range = RangeF64.init(-amplitude*1.1, amplitude*1.1);
|
||||
self.protocol_graph_cache.invalidate();
|
||||
}
|
||||
|
||||
if (self.protocol_error_message) |message| {
|
||||
@ -792,26 +849,9 @@ pub fn showProtocolModal(self: *MainScreen, channel_view: *ChannelView) !void {
|
||||
btn.borders = UI.Borders.all(.{ .color = srcery.green, .size = 4 });
|
||||
|
||||
if (ui.signal(btn).clicked() or ui.isKeyboardPressed(.key_enter)) {
|
||||
self.clearProtocolErrorMessage();
|
||||
|
||||
for (form_inputs) |form_input| {
|
||||
const label = form_input.name;
|
||||
const text_input_storage: *UI.TextInputStorage = form_input.storage;
|
||||
|
||||
const number = std.fmt.parseFloat(f32, text_input_storage.buffer.items) catch {
|
||||
try self.setProtocolErrorMessage("ERROR: {s} must be a number", .{ label });
|
||||
continue;
|
||||
};
|
||||
|
||||
if (number <= 0) {
|
||||
try self.setProtocolErrorMessage("ERROR: {s} must be positive", .{ label });
|
||||
continue;
|
||||
}
|
||||
|
||||
form_input.value.* = number;
|
||||
}
|
||||
|
||||
if (self.protocol_error_message == null) {
|
||||
try App.DeviceChannel.generateSine(&device_channel.write_pattern, channel_view.sample_rate.?, frequency, amplitude);
|
||||
|
||||
self.app.deferred_actions.appendAssumeCapacity(.{
|
||||
.activate = channel_view
|
||||
});
|
||||
|
13
src/ui.zig
13
src/ui.zig
@ -2014,6 +2014,7 @@ pub fn isCtrlDown(self: *UI) bool {
|
||||
pub const TextInputStorage = struct {
|
||||
buffer: std.ArrayList(u8),
|
||||
|
||||
modified: bool = false,
|
||||
editing: bool = false,
|
||||
last_pressed_at_ns: i128 = 0,
|
||||
cursor_start: usize = 0,
|
||||
@ -2032,11 +2033,15 @@ pub const TextInputStorage = struct {
|
||||
}
|
||||
|
||||
pub fn setText(self: *TextInputStorage, text: []const u8) !void {
|
||||
self.modified = true;
|
||||
|
||||
self.buffer.clearAndFree();
|
||||
try self.buffer.appendSlice(text);
|
||||
}
|
||||
|
||||
pub fn insertSingle(self: *TextInputStorage, index: usize, symbol: u8) !void {
|
||||
self.modified = true;
|
||||
|
||||
try self.buffer.insert(index, symbol);
|
||||
|
||||
if (self.cursor_start >= index) {
|
||||
@ -2058,6 +2063,8 @@ pub const TextInputStorage = struct {
|
||||
const to_clamped = std.math.clamp(to , 0, self.buffer.items.len);
|
||||
if (from_clamped == to_clamped) return;
|
||||
|
||||
self.modified = true;
|
||||
|
||||
const lower = @min(from_clamped, to_clamped);
|
||||
const upper = @max(from_clamped, to_clamped);
|
||||
assert(lower < upper);
|
||||
@ -2172,6 +2179,8 @@ pub const TextInputStorage = struct {
|
||||
fn insertMany(self: *TextInputStorage, index: usize, text: []const u8) !void {
|
||||
if (index > self.buffer.items.len) return;
|
||||
|
||||
self.modified = true;
|
||||
|
||||
try self.buffer.insertSlice(index, text);
|
||||
|
||||
if (self.cursor_start >= index) {
|
||||
@ -2184,6 +2193,10 @@ pub const TextInputStorage = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const TextInputResult = struct {
|
||||
changed: bool = false
|
||||
};
|
||||
|
||||
pub fn mouseTooltip(self: *UI) *Box {
|
||||
const tooltip = self.getBoxByKey(mouse_tooltip_box_key).?;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user