refactor sample list to separate list in project
This commit is contained in:
parent
e5bc57d7b5
commit
af8cf21f53
255
src/app.zig
255
src/app.zig
@ -166,6 +166,23 @@ fn GenerationalArray(Item: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub const SampleList = struct {
|
||||
samples: std.ArrayListUnmanaged(f64) = .{},
|
||||
graph_min_max_cache: Graph.MinMaxCache = .{},
|
||||
min_sample: ?f64 = null,
|
||||
max_sample: ?f64 = null,
|
||||
|
||||
pub fn clear(self: *SampleList, allocator: Allocator) void {
|
||||
self.deinit(allocator);
|
||||
self.* = .{};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *SampleList, allocator: Allocator) void {
|
||||
self.samples.clearAndFree(allocator);
|
||||
self.graph_min_max_cache.deinit(allocator);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Channel = struct {
|
||||
const Name = std.BoundedArray(u8, NIDaq.max_channel_name_size + 1);
|
||||
const Device = std.BoundedArray(u8, NIDaq.max_device_name_size + 1);
|
||||
@ -176,15 +193,12 @@ pub const Channel = struct {
|
||||
|
||||
// Runtime
|
||||
device: Device = .{},
|
||||
collected_samples_id: Id,
|
||||
allowed_sample_values: ?RangeF64 = null,
|
||||
allowed_sample_rates: ?RangeF64 = null,
|
||||
// TODO: Use a linked list, so the whole list wouldn't need to be reallocated when appending collected samples
|
||||
collected_samples: std.ArrayListUnmanaged(f64) = .{},
|
||||
write_pattern: std.ArrayListUnmanaged(f64) = .{},
|
||||
output_task: ?NIDaq.Task = null,
|
||||
graph_min_max_cache: Graph.MinMaxCache = .{},
|
||||
last_sample_save_at: ?i128 = null,
|
||||
processed_samples_up_to: usize = 0,
|
||||
|
||||
pub fn deinit(self: *Channel, allocator: Allocator) void {
|
||||
self.clear(allocator);
|
||||
@ -200,14 +214,11 @@ pub const Channel = struct {
|
||||
self.allowed_sample_values = null;
|
||||
self.last_sample_save_at = null;
|
||||
|
||||
self.collected_samples.clearAndFree(allocator);
|
||||
self.write_pattern.clearAndFree(allocator);
|
||||
if (self.output_task) |task| {
|
||||
task.clear();
|
||||
self.output_task = null;
|
||||
}
|
||||
|
||||
self.graph_min_max_cache.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn generateSine(
|
||||
@ -247,10 +258,6 @@ pub const Channel = struct {
|
||||
pub fn invalidateSavedSamples(self: *Channel) void {
|
||||
self.last_sample_save_at = null;
|
||||
}
|
||||
|
||||
fn hasNewCollectedSamples(self: *Channel) bool {
|
||||
return self.processed_samples_up_to != self.collected_samples.items.len;
|
||||
}
|
||||
};
|
||||
|
||||
pub const File = struct {
|
||||
@ -258,24 +265,16 @@ pub const File = struct {
|
||||
path: []u8,
|
||||
|
||||
// Runtime
|
||||
samples: ?[]f64 = null,
|
||||
graph_min_max_cache: Graph.MinMaxCache = .{},
|
||||
min_sample: f64 = 0,
|
||||
max_sample: f64 = 0,
|
||||
samples_id: Id,
|
||||
|
||||
pub fn deinit(self: *File, allocator: Allocator) void {
|
||||
self.clear(allocator);
|
||||
self.graph_min_max_cache.deinit(allocator);
|
||||
allocator.free(self.path);
|
||||
}
|
||||
|
||||
pub fn clear(self: *File, allocator: Allocator) void {
|
||||
if (self.samples) |samples| {
|
||||
allocator.free(samples);
|
||||
self.samples = null;
|
||||
}
|
||||
self.min_sample = 0;
|
||||
self.max_sample = 0;
|
||||
_ = self;
|
||||
_ = allocator;
|
||||
}
|
||||
};
|
||||
|
||||
@ -373,12 +372,14 @@ pub const View = struct {
|
||||
graph_opts: Graph.ViewOptions = .{},
|
||||
sync_controls: bool = false,
|
||||
marked_ranges: std.BoundedArray(MarkedRange, 32) = .{},
|
||||
sliding_window: ?f64 = null,
|
||||
|
||||
// Runtime
|
||||
graph_cache: Graph.RenderCache = .{},
|
||||
available_x_range: RangeF64 = RangeF64.init(0, 0),
|
||||
available_y_range: RangeF64 = RangeF64.init(0, 0),
|
||||
unit: ?NIDaq.Unit = .Voltage,
|
||||
transformed_samples: ?[]f64 = null,
|
||||
|
||||
pub fn clear(self: *View) void {
|
||||
self.graph_cache.clear();
|
||||
@ -431,6 +432,7 @@ pub const Project = struct {
|
||||
// TODO: How this to computer local settings, like appdata. Because this option shouldn't be project specific.
|
||||
show_rulers: bool = true,
|
||||
|
||||
sample_lists: GenerationalArray(SampleList) = .{},
|
||||
channels: GenerationalArray(Channel) = .{},
|
||||
files: GenerationalArray(File) = .{},
|
||||
views: GenerationalArray(View) = .{},
|
||||
@ -467,23 +469,88 @@ pub const Project = struct {
|
||||
return result_range;
|
||||
}
|
||||
|
||||
pub fn getViewSampleList(self: *Project, view_id: Id) Id {
|
||||
const view = self.views.get(view_id).?;
|
||||
|
||||
switch (view.reference) {
|
||||
.channel => |channel_id| {
|
||||
const channel = self.channels.get(channel_id).?;
|
||||
return channel.collected_samples_id;
|
||||
},
|
||||
.file => |file_id| {
|
||||
const file = self.files.get(file_id).?;
|
||||
return file.samples_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getViewSamples(self: *Project, view_id: Id) []const f64 {
|
||||
const empty = &[0]f64{};
|
||||
const samples_list_id = self.getViewSampleList(view_id);
|
||||
const samples_list = self.sample_lists.get(samples_list_id).?;
|
||||
|
||||
var result: []const f64 = empty;
|
||||
return samples_list.samples.items;
|
||||
}
|
||||
|
||||
if (self.views.get(view_id)) |view| {
|
||||
switch (view.reference) {
|
||||
.channel => |channel_id| if (self.channels.get(channel_id)) |channel| {
|
||||
result = channel.collected_samples.items;
|
||||
},
|
||||
.file => |file_id| if (self.files.get(file_id)) |file| {
|
||||
result = file.samples orelse empty;
|
||||
pub fn appendSamples(self: *Project, allocator: Allocator, sample_list_id: Id, samples: []const f64) !void {
|
||||
if (samples.len == 0) return;
|
||||
|
||||
const sample_list = self.sample_lists.get(sample_list_id).?;
|
||||
|
||||
const affected_range = RangeF64.init(
|
||||
@floatFromInt(sample_list.samples.items.len),
|
||||
@floatFromInt(sample_list.samples.items.len + samples.len)
|
||||
);
|
||||
|
||||
try sample_list.samples.appendSlice(allocator, samples);
|
||||
|
||||
var min_sample = sample_list.min_sample orelse samples[0];
|
||||
var max_sample = sample_list.max_sample orelse samples[0];
|
||||
for (samples) |sample| {
|
||||
min_sample = @min(min_sample, sample);
|
||||
max_sample = @max(max_sample, sample);
|
||||
}
|
||||
sample_list.min_sample = min_sample;
|
||||
sample_list.max_sample = max_sample;
|
||||
|
||||
try sample_list.graph_min_max_cache.updateLast(allocator, sample_list.samples.items);
|
||||
|
||||
self.refreshMarkedRanges(sample_list_id, affected_range);
|
||||
}
|
||||
|
||||
pub fn clearSamples(self: *Project, allocator: Allocator, sample_list_id: Id) void {
|
||||
const sample_list = self.sample_lists.get(sample_list_id).?;
|
||||
|
||||
const affected_range = RangeF64.init(
|
||||
0,
|
||||
@floatFromInt(sample_list.samples.items.len)
|
||||
);
|
||||
|
||||
sample_list.clear(allocator);
|
||||
|
||||
self.refreshMarkedRanges(sample_list_id, affected_range);
|
||||
}
|
||||
|
||||
fn refreshMarkedRanges(self: *Project, sample_list_id: Id, affected_range: RangeF64) void {
|
||||
const sample_list = self.sample_lists.get(sample_list_id).?;
|
||||
const total_samples = sample_list.samples.items;
|
||||
|
||||
var view_iter = self.views.idIterator();
|
||||
while (view_iter.next()) |view_id| {
|
||||
const view_sample_list_id = self.getViewSampleList(view_id);
|
||||
if (!view_sample_list_id.eql(sample_list_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const view = self.views.get(view_id).?;
|
||||
const marked_ranges: []View.MarkedRange = view.marked_ranges.slice();
|
||||
for (marked_ranges) |*marked_range| {
|
||||
if (marked_range.axis != .X) continue;
|
||||
|
||||
if (affected_range.intersectPositive(marked_range.range).isPositive()) {
|
||||
marked_range.refresh(total_samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Project, allocator: Allocator) void {
|
||||
@ -501,6 +568,12 @@ pub const Project = struct {
|
||||
|
||||
self.views.clear();
|
||||
|
||||
var sample_lists_iter = self.sample_lists.iterator();
|
||||
while (sample_lists_iter.next()) |sample_list| {
|
||||
sample_list.clear(allocator);
|
||||
}
|
||||
self.sample_lists.clear();
|
||||
|
||||
if (self.save_location) |str| {
|
||||
allocator.free(str);
|
||||
self.save_location = null;
|
||||
@ -548,10 +621,14 @@ pub const Project = struct {
|
||||
}
|
||||
errdefer if (saved_collected_samples) |str| allocator.free(str);
|
||||
|
||||
const sample_list_id = try self.sample_lists.insert(.{});
|
||||
errdefer self.sample_lists.remove(sample_list_id);
|
||||
|
||||
const channel = self.channels.get(id).?;
|
||||
channel.* = Channel{
|
||||
.name = try utils.initBoundedStringZ(Channel.Name, channel_name),
|
||||
.saved_collected_samples = saved_collected_samples
|
||||
.saved_collected_samples = saved_collected_samples,
|
||||
.collected_samples_id = sample_list_id
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -566,9 +643,13 @@ pub const Project = struct {
|
||||
const path = try readString(reader, allocator);
|
||||
errdefer allocator.free(path);
|
||||
|
||||
const sample_list_id = try self.sample_lists.insert(.{});
|
||||
errdefer self.sample_lists.remove(sample_list_id);
|
||||
|
||||
const file = self.files.get(id).?;
|
||||
file.* = File{
|
||||
.path = path
|
||||
.path = path,
|
||||
.samples_id = sample_list_id
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -654,7 +735,8 @@ pub const Project = struct {
|
||||
const samples_file = try dir.createFile(path, .{});
|
||||
defer samples_file.close();
|
||||
|
||||
try writeFileF64(samples_file, channel.collected_samples.items);
|
||||
const samples = self.sample_lists.get(channel.collected_samples_id).?.samples.items;
|
||||
try writeFileF64(samples_file, samples);
|
||||
}
|
||||
|
||||
const stat = try dir.statFile(path);
|
||||
@ -848,7 +930,7 @@ pub fn init(self: *App, allocator: Allocator) !void {
|
||||
self.ni_daq = try NIDaq.init(allocator, &self.ni_daq_api.?, .{
|
||||
.max_devices = 4,
|
||||
.max_analog_inputs = 32,
|
||||
.max_analog_outputs = 8,
|
||||
.max_analog_outputs = 16,
|
||||
.max_counter_outputs = 8,
|
||||
.max_counter_inputs = 8,
|
||||
.max_analog_input_voltage_ranges = 4,
|
||||
@ -1012,42 +1094,6 @@ pub fn tick(self: *App) !void {
|
||||
self.collection_samples_mutex.lock();
|
||||
defer self.collection_samples_mutex.unlock();
|
||||
|
||||
{
|
||||
var view_iter = self.project.views.idIterator();
|
||||
while (view_iter.next()) |id| {
|
||||
const view = self.getView(id) orelse continue;
|
||||
if (view.reference != .channel) continue;
|
||||
|
||||
const channel_id = view.reference.channel;
|
||||
const channel = self.getChannel(channel_id) orelse continue;
|
||||
if (!channel.hasNewCollectedSamples()) continue;
|
||||
|
||||
const samples = channel.collected_samples.items;
|
||||
|
||||
const new_samples_range = RangeF64.init(@floatFromInt(channel.processed_samples_up_to), @floatFromInt(samples.len));
|
||||
const marked_ranges: []View.MarkedRange = view.marked_ranges.slice();
|
||||
for (marked_ranges) |*marked_range| {
|
||||
if (marked_range.axis != .X) continue;
|
||||
|
||||
if (new_samples_range.intersectPositive(marked_range.range).isPositive()) {
|
||||
marked_range.refresh(samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update channel min max caches
|
||||
{
|
||||
var channel_iter = self.project.channels.iterator();
|
||||
while (channel_iter.next()) |channel| {
|
||||
if (channel.hasNewCollectedSamples()) {
|
||||
channel.invalidateSavedSamples();
|
||||
try channel.graph_min_max_cache.updateLast(self.allocator, channel.collected_samples.items);
|
||||
channel.processed_samples_up_to = channel.collected_samples.items.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var view_iter = self.project.views.idIterator();
|
||||
while (view_iter.next()) |id| {
|
||||
@ -1199,7 +1245,7 @@ fn startCollection(self: *App) !void {
|
||||
|
||||
for (channels_in_task.constSlice()) |id| {
|
||||
const channel = self.getChannel(id).?;
|
||||
channel.collected_samples.clearAndFree(self.allocator);
|
||||
self.project.clearSamples(self.allocator, channel.collected_samples_id);
|
||||
}
|
||||
|
||||
const read_array = try self.allocator.alloc(f64,
|
||||
@ -1345,7 +1391,7 @@ pub fn collectionThreadCallback(self: *App) void {
|
||||
const channel_id = collection_task.channels.get(channel_index);
|
||||
const channel = self.getChannel(channel_id) orelse continue;
|
||||
|
||||
channel.collected_samples.appendSlice(self.allocator, channel_samples) catch |e| {
|
||||
self.project.appendSamples(self.allocator, channel.collected_samples_id, channel_samples) catch |e| {
|
||||
log.err("Failed to append samples for channel: {}", .{e});
|
||||
continue;
|
||||
};
|
||||
@ -1374,9 +1420,13 @@ pub fn addFile(self: *App, path: []const u8) !Id {
|
||||
const path_dupe = try self.allocator.dupe(u8, path);
|
||||
errdefer self.allocator.free(path_dupe);
|
||||
|
||||
const sample_list_id = try self.project.sample_lists.insert(.{});
|
||||
errdefer self.project.sample_lists.remove(sample_list_id);
|
||||
|
||||
const file = self.project.files.get(id).?;
|
||||
file.* = File{
|
||||
.path = path_dupe
|
||||
.path = path_dupe,
|
||||
.samples_id = sample_list_id
|
||||
};
|
||||
|
||||
self.loadFile(id) catch |e| {
|
||||
@ -1423,7 +1473,10 @@ fn writeFileF64(file: std.fs.File, data: []const f64) !void {
|
||||
|
||||
pub fn loadFile(self: *App, id: Id) !void {
|
||||
const file = self.getFile(id) orelse return;
|
||||
const sample_list = self.project.sample_lists.get(file.samples_id) orelse return;
|
||||
|
||||
file.clear(self.allocator);
|
||||
sample_list.clear(self.allocator);
|
||||
|
||||
const cwd = std.fs.cwd();
|
||||
|
||||
@ -1431,18 +1484,9 @@ pub fn loadFile(self: *App, id: Id) !void {
|
||||
defer samples_file.close();
|
||||
|
||||
const samples = try readFileF64(self.allocator, samples_file);
|
||||
file.samples = samples;
|
||||
defer self.allocator.free(samples);
|
||||
|
||||
if (samples.len > 0) {
|
||||
file.min_sample = samples[0];
|
||||
file.max_sample = samples[0];
|
||||
for (samples) |sample| {
|
||||
file.min_sample = @min(file.min_sample, sample);
|
||||
file.max_sample = @max(file.max_sample, sample);
|
||||
}
|
||||
|
||||
try file.graph_min_max_cache.updateAll(self.allocator, samples);
|
||||
}
|
||||
try self.project.appendSamples(self.allocator, file.samples_id, samples);
|
||||
}
|
||||
|
||||
// ---------------- Channels --------------------------------- //
|
||||
@ -1455,9 +1499,13 @@ pub fn addChannel(self: *App, channel_name: []const u8) !Id {
|
||||
const id = try self.project.channels.insertUndefined();
|
||||
errdefer self.project.channels.remove(id);
|
||||
|
||||
const sample_list_id = try self.project.sample_lists.insert(.{});
|
||||
errdefer self.project.sample_lists.remove(sample_list_id);
|
||||
|
||||
const channel = self.project.channels.get(id).?;
|
||||
channel.* = Channel{
|
||||
.name = try utils.initBoundedStringZ(Channel.Name, channel_name),
|
||||
.collected_samples_id = sample_list_id
|
||||
};
|
||||
|
||||
if (self.project.save_location) |project_file_path| {
|
||||
@ -1527,7 +1575,9 @@ fn loadSavedSamples(self: *App, channel_id: Id) !void {
|
||||
defer samples_file.close();
|
||||
|
||||
const samples = try readFileF64(self.allocator, samples_file);
|
||||
channel.collected_samples = std.ArrayListUnmanaged(f64).fromOwnedSlice(samples);
|
||||
defer self.allocator.free(samples);
|
||||
|
||||
try self.project.appendSamples(self.allocator, channel.collected_samples_id, samples);
|
||||
|
||||
const stat = try dir.statFile(saved_samples_location);
|
||||
channel.last_sample_save_at = stat.mtime;
|
||||
@ -1601,36 +1651,29 @@ pub fn getViewSamples(self: *App, id: Id) []const f64 {
|
||||
}
|
||||
|
||||
pub fn getViewMinMaxCache(self: *App, id: Id) Graph.MinMaxCache {
|
||||
const view = self.getView(id).?;
|
||||
const samples_list_id = self.project.getViewSampleList(id);
|
||||
const sample_list = self.project.sample_lists.get(samples_list_id).?;
|
||||
|
||||
switch (view.reference) {
|
||||
.channel => |channel_id| {
|
||||
const channel = self.getChannel(channel_id).?;
|
||||
return channel.graph_min_max_cache;
|
||||
},
|
||||
.file => |file_id| {
|
||||
const file = self.getFile(file_id).?;
|
||||
return file.graph_min_max_cache;
|
||||
}
|
||||
}
|
||||
return sample_list.graph_min_max_cache;
|
||||
}
|
||||
|
||||
fn refreshViewAvailableXYRanges(self: *App, id: Id) void {
|
||||
const view = self.getView(id) orelse return;
|
||||
const sample_list_id = self.project.getViewSampleList(id);
|
||||
const sample_list = self.project.sample_lists.get(sample_list_id).?;
|
||||
|
||||
view.available_x_range = RangeF64.init(0, @floatFromInt(sample_list.samples.items.len));
|
||||
|
||||
switch (view.reference) {
|
||||
.channel => |channel_id| if (self.getChannel(channel_id)) |channel| {
|
||||
const allowed_sample_values = channel.allowed_sample_values orelse RangeF64.init(0, 0);
|
||||
const samples = channel.collected_samples.items;
|
||||
|
||||
view.available_x_range = RangeF64.init(0, @floatFromInt(samples.len));
|
||||
view.available_y_range = RangeF64.init(allowed_sample_values.upper, allowed_sample_values.lower);
|
||||
},
|
||||
.file => |file_id| if (self.getFile(file_id)) |file| {
|
||||
const samples = file.samples orelse &[_]f64{ };
|
||||
|
||||
view.available_x_range = RangeF64.init(0, @floatFromInt(samples.len));
|
||||
view.available_y_range = RangeF64.init(file.max_sample, file.min_sample);
|
||||
.file => {
|
||||
const min_sample = sample_list.min_sample orelse 0;
|
||||
const max_sample = sample_list.max_sample orelse 1;
|
||||
view.available_y_range = RangeF64.init(max_sample, min_sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
src/assets/checkbox-mark.ase
Normal file
BIN
src/assets/checkbox-mark.ase
Normal file
Binary file not shown.
BIN
src/assets/cross.ase
Normal file
BIN
src/assets/cross.ase
Normal file
Binary file not shown.
BIN
src/assets/file.ase
Normal file
BIN
src/assets/file.ase
Normal file
Binary file not shown.
@ -240,11 +240,20 @@ fn showToolbar(ctx: Context, view_id: Id) void {
|
||||
.size_x = UI.Sizing.initGrowFull()
|
||||
});
|
||||
|
||||
_ = ui.createBox(.{
|
||||
.size_x = UI.Sizing.initFixedPixels(ui.rem(1))
|
||||
});
|
||||
|
||||
const label = ui.label("{s}", .{text});
|
||||
label.size.y = UI.Sizing.initGrowFull();
|
||||
label.alignment.x = .center;
|
||||
label.alignment.y = .center;
|
||||
label.padding = UI.Padding.horizontal(ui.rem(1));
|
||||
// TODO:
|
||||
// label.padding = UI.Padding.horizontal(ui.rem(1));
|
||||
|
||||
_ = ui.createBox(.{
|
||||
.size_x = UI.Sizing.initFixedPixels(ui.rem(1))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,6 +191,50 @@ pub const RenderCache = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const SampleIterator = struct {
|
||||
samples: []const f64,
|
||||
to: f64,
|
||||
step: f64,
|
||||
i: f64,
|
||||
next_i: f64,
|
||||
|
||||
pub fn init(samples: []const f64, from: f64, to: f64, step: f64) SampleIterator {
|
||||
return SampleIterator{
|
||||
.samples = samples,
|
||||
.i = from,
|
||||
.next_i = from + step,
|
||||
.to = to,
|
||||
.step = step
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initRange(samples: []const f64, range: RangeF64, step: f64) SampleIterator {
|
||||
return SampleIterator.init(samples, range.lower, range.upper, step);
|
||||
}
|
||||
|
||||
pub fn next(self: *SampleIterator) ?[]const f64 {
|
||||
if (self.next_i < self.to) {
|
||||
self.i = self.next_i;
|
||||
self.next_i = self.i + self.step;
|
||||
|
||||
const i_usize: usize = @intFromFloat(self.i);
|
||||
const next_i_usize: usize = @intFromFloat(self.next_i);
|
||||
if (next_i_usize >= self.samples.len) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const samples = self.samples[i_usize..next_i_usize];
|
||||
if (samples.len == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return samples;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn drawSamplesExact(draw_rect: rl.Rectangle, options: ViewOptions, samples: []const f64) void {
|
||||
rl.beginScissorMode(
|
||||
@intFromFloat(draw_rect.x),
|
||||
@ -241,20 +285,15 @@ fn drawSamplesApproximate(draw_rect: rl.Rectangle, options: ViewOptions, samples
|
||||
const i_range = options.x_range.intersectPositive(
|
||||
RangeF64.init(0, @max(@as(f64, @floatFromInt(samples.len)) - 1, 0))
|
||||
);
|
||||
if (i_range.lower > i_range.upper) {
|
||||
if (i_range.isNegative()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const samples_per_column = options.x_range.size() / draw_x_range.size();
|
||||
assert(samples_per_column >= 1);
|
||||
|
||||
var i = i_range.lower;
|
||||
while (i < i_range.upper - samples_per_column) : (i += samples_per_column) {
|
||||
const column_start: usize = @intFromFloat(i);
|
||||
const column_end: usize = @intFromFloat(i + samples_per_column);
|
||||
const column_samples = samples[column_start..column_end];
|
||||
if (column_samples.len == 0) continue;
|
||||
|
||||
var iter = SampleIterator.init(samples, i_range.lower, i_range.upper, samples_per_column);
|
||||
while (iter.next()) |column_samples| {
|
||||
var column_min = column_samples[0];
|
||||
var column_max = column_samples[0];
|
||||
|
||||
@ -263,7 +302,7 @@ fn drawSamplesApproximate(draw_rect: rl.Rectangle, options: ViewOptions, samples
|
||||
column_max = @max(column_max, sample);
|
||||
}
|
||||
|
||||
const x = options.x_range.remapTo(draw_x_range, i);
|
||||
const x = options.x_range.remapTo(draw_x_range, iter.i);
|
||||
const y_min = options.y_range.remapTo(draw_y_range, column_min);
|
||||
const y_max = options.y_range.remapTo(draw_y_range, column_max);
|
||||
|
||||
@ -290,7 +329,7 @@ fn drawSamplesMinMax(draw_rect: rl.Rectangle, options: ViewOptions, min_max_cach
|
||||
const i_range = options.x_range.intersectPositive(
|
||||
RangeF64.init(0, @max(@as(f64, @floatFromInt(min_max_cache.sample_count)) - 1, 0))
|
||||
);
|
||||
if (i_range.lower > i_range.upper) {
|
||||
if (i_range.isNegative()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
36
src/main.zig
36
src/main.zig
@ -112,29 +112,29 @@ pub fn main() !void {
|
||||
app.project.save_location = save_location;
|
||||
app.project.sample_rate = 5000;
|
||||
|
||||
_ = try app.addView(.{
|
||||
.channel = try app.addChannel("Dev1/ai0")
|
||||
});
|
||||
// _ = try app.addView(.{
|
||||
// .channel = try app.addChannel("Dev1/ai0")
|
||||
// });
|
||||
|
||||
_ = try app.addView(.{
|
||||
.file = try app.addFile("./samples-5k.bin")
|
||||
});
|
||||
// _ = try app.addView(.{
|
||||
// .file = try app.addFile("./samples-5k.bin")
|
||||
// });
|
||||
|
||||
_ = try app.addView(.{
|
||||
.file = try app.addFile("./samples-50k.bin")
|
||||
});
|
||||
// _ = try app.addView(.{
|
||||
// .file = try app.addFile("./samples-50k.bin")
|
||||
// });
|
||||
|
||||
_ = try app.addView(.{
|
||||
.file = try app.addFile("./samples-300k.bin")
|
||||
});
|
||||
// _ = try app.addView(.{
|
||||
// .file = try app.addFile("./samples-300k.bin")
|
||||
// });
|
||||
|
||||
_ = try app.addView(.{
|
||||
.file = try app.addFile("./samples-9m.bin")
|
||||
});
|
||||
// _ = try app.addView(.{
|
||||
// .file = try app.addFile("./samples-9m.bin")
|
||||
// });
|
||||
|
||||
_ = try app.addView(.{
|
||||
.file = try app.addFile("./samples-18m.bin")
|
||||
});
|
||||
// _ = try app.addView(.{
|
||||
// .file = try app.addFile("./samples-18m.bin")
|
||||
// });
|
||||
}
|
||||
|
||||
var profiler: ?Profiler = null;
|
||||
|
@ -36,6 +36,7 @@ sample_rate_input: UI.TextInputStorage,
|
||||
parsed_sample_rate: ?f64 = null,
|
||||
|
||||
// View settings
|
||||
sliding_window_input: UI.TextInputStorage,
|
||||
channel_save_file_picker: ?Platform.FilePickerId = null,
|
||||
file_save_file_picker: ?Platform.FilePickerId = null,
|
||||
|
||||
@ -47,7 +48,8 @@ pub fn init(app: *App) !MainScreen {
|
||||
.frequency_input = UI.TextInputStorage.init(allocator),
|
||||
.amplitude_input = UI.TextInputStorage.init(allocator),
|
||||
.sample_rate_input = UI.TextInputStorage.init(allocator),
|
||||
.view_controls = ViewControlsSystem.init(&app.project)
|
||||
.view_controls = ViewControlsSystem.init(&app.project),
|
||||
.sliding_window_input = UI.TextInputStorage.init(allocator)
|
||||
};
|
||||
|
||||
try self.frequency_input.setText("10");
|
||||
@ -317,13 +319,13 @@ fn showViewSettings(self: *MainScreen, view_id: Id) !void {
|
||||
.label = "Sync controls"
|
||||
});
|
||||
|
||||
var sample_count: ?usize = null;
|
||||
const samples = project.getViewSamples(view_id);
|
||||
|
||||
switch (view.reference) {
|
||||
.channel => |channel_id| {
|
||||
const channel = project.channels.get(channel_id).?;
|
||||
const channel_name = utils.getBoundedStringZ(&channel.name);
|
||||
const channel_type = NIDaq.getChannelType(channel_name);
|
||||
const samples = channel.collected_samples.items;
|
||||
|
||||
_ = ui.label("Channel: {s}", .{ channel_name });
|
||||
|
||||
@ -346,16 +348,10 @@ fn showViewSettings(self: *MainScreen, view_id: Id) !void {
|
||||
|
||||
channel.saved_collected_samples = path;
|
||||
}
|
||||
|
||||
sample_count = samples.len;
|
||||
},
|
||||
.file => |file_id| {
|
||||
const file = project.files.get(file_id).?;
|
||||
|
||||
if (file.samples) |samples| {
|
||||
sample_count = samples.len;
|
||||
}
|
||||
|
||||
if (ui.fileInput(.{
|
||||
.key = ui.keyFromString("Filename"),
|
||||
.allocator = self.app.allocator,
|
||||
@ -370,18 +366,21 @@ fn showViewSettings(self: *MainScreen, view_id: Id) !void {
|
||||
|
||||
}
|
||||
|
||||
if (sample_count != null) {
|
||||
_ = ui.label("Samples: {d}", .{ sample_count.? });
|
||||
_ = ui.label("Samples: {d}", .{ samples.len });
|
||||
|
||||
var duration_str: []const u8 = "-";
|
||||
if (sample_rate != null) {
|
||||
const duration = @as(f64, @floatFromInt(sample_count.?)) / sample_rate.?;
|
||||
if (utils.formatDuration(ui.frameAllocator(), duration)) |str| {
|
||||
duration_str = str;
|
||||
} else |_| {}
|
||||
}
|
||||
_ = ui.label("Duration: {s}", .{ duration_str });
|
||||
var duration_str: []const u8 = "-";
|
||||
if (sample_rate != null) {
|
||||
const duration = @as(f64, @floatFromInt(samples.len)) / sample_rate.?;
|
||||
if (utils.formatDuration(ui.frameAllocator(), duration)) |str| {
|
||||
duration_str = str;
|
||||
} else |_| {}
|
||||
}
|
||||
_ = ui.label("Duration: {s}", .{ duration_str });
|
||||
|
||||
_ = try ui.numberInput(f64, .{
|
||||
.key = ui.keyFromString("Sliding window"),
|
||||
.storage = &self.sliding_window_input
|
||||
});
|
||||
}
|
||||
|
||||
fn showMarkedRange(self: *MainScreen, view_id: Id, index: usize) void {
|
||||
|
Loading…
Reference in New Issue
Block a user