add proportional views
This commit is contained in:
parent
6099da25de
commit
68448d1aa3
592
src/app.zig
592
src/app.zig
@ -194,7 +194,9 @@ const SumRingBuffer = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Transform = struct {
|
pub const Transformation = struct {
|
||||||
|
const Block = SampleList.Block;
|
||||||
|
|
||||||
const max_gain_changes = 32;
|
const max_gain_changes = 32;
|
||||||
pub const GainChanges = std.BoundedArray(GainChange, max_gain_changes);
|
pub const GainChanges = std.BoundedArray(GainChange, max_gain_changes);
|
||||||
pub const GainChange = struct {
|
pub const GainChange = struct {
|
||||||
@ -202,45 +204,74 @@ pub const Transform = struct {
|
|||||||
sample: f64,
|
sample: f64,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
destination: Id, // Sample list ID
|
||||||
|
source: Id, // Sample list ID
|
||||||
gain_changes: GainChanges = .{},
|
gain_changes: GainChanges = .{},
|
||||||
|
divider: ?Id = null, // Sample list ID
|
||||||
|
|
||||||
pub fn applySlice(self: Transform, index: usize, source: []f64, destination: []f64) void {
|
pub fn apply(self: Transformation, project: *Project, from_block: Block.Id, block_count: usize) !void {
|
||||||
assert(source.len == destination.len);
|
const source_list = project.sample_lists.get(self.source) orelse return error.NoSource;
|
||||||
assert(source.len <= SampleList.Block.capacity);
|
const destination_list = project.sample_lists.get(self.destination) orelse return error.NoDestination;
|
||||||
|
|
||||||
const gain_changes = self.gain_changes.constSlice();
|
var divider: ?*SampleList = null;
|
||||||
|
if (self.divider) |divider_id| {
|
||||||
const gain_segment = Transform.findGainSegment(gain_changes, index);
|
divider = project.sample_lists.get(divider_id);
|
||||||
if (gain_segment.to > @as(f64, @floatFromInt(index+source.len))) {
|
|
||||||
for (0..source.len) |i| {
|
|
||||||
destination[i] = source[i] * (100 / gain_segment.gain);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
const gain_per_sample = Transform.createGainLookup(gain_changes, index, source.len);
|
|
||||||
|
|
||||||
for (0..source.len) |i| {
|
|
||||||
destination[i] = source[i] * (100 / gain_per_sample[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn applyBlock(self: Transform, index: usize, source: *SampleList.Block, destination: *SampleList.Block) void {
|
try destination_list.ensureTotalBlocks(@min(from_block + block_count, source_list.blocks.items.len));
|
||||||
destination.clear();
|
|
||||||
destination.len = source.len;
|
|
||||||
|
|
||||||
self.applySlice(index, source.samplesSlice(), destination.samplesSlice());
|
var empty_block_buffer: Block.Buffer = undefined;
|
||||||
destination.recomputeMinMax();
|
var empty_block = Block{ .buffer = &empty_block_buffer };
|
||||||
}
|
|
||||||
|
|
||||||
pub fn applySampleList(self: Transform, from_block_index: SampleList.Block.Id, block_count: usize, source: *SampleList, destination: *SampleList) void {
|
|
||||||
for (0..block_count) |block_offset| {
|
for (0..block_count) |block_offset| {
|
||||||
const block_id = from_block_index + block_offset;
|
const block_id = from_block + block_offset;
|
||||||
|
const index = block_id * SampleList.Block.capacity;
|
||||||
|
|
||||||
const source_block = source.getBlock(block_id).?;
|
const destination_block = destination_list.getBlock(block_id) orelse continue;
|
||||||
const destination_block = destination.getBlock(block_id).?;
|
destination_block.clear();
|
||||||
|
|
||||||
self.applyBlock(block_id * SampleList.Block.capacity, source_block, destination_block);
|
const source_block = source_list.getBlock(block_id) orelse continue;
|
||||||
|
destination_block.len = source_block.len;
|
||||||
|
|
||||||
|
const source_slice = source_block.samplesSlice();
|
||||||
|
const destination_slice = destination_block.samplesSlice();
|
||||||
|
|
||||||
|
assert(source_block.len == destination_block.len);
|
||||||
|
assert(source_block.len <= SampleList.Block.capacity);
|
||||||
|
|
||||||
|
var divider_block = &empty_block;
|
||||||
|
if (divider) |sample_list| {
|
||||||
|
divider_block = sample_list.getBlock(block_id) orelse &empty_block;
|
||||||
|
}
|
||||||
|
const divider_slice = divider_block.samplesSlice();
|
||||||
|
|
||||||
|
const gain_changes = self.gain_changes.constSlice();
|
||||||
|
|
||||||
|
const gain_segment = Transformation.findGainSegment(gain_changes, index);
|
||||||
|
if (gain_segment.to > @as(f64, @floatFromInt(index+source_slice.len))) {
|
||||||
|
for (0..source_slice.len) |i| {
|
||||||
|
destination_slice[i] = source_slice[i] * (100 / gain_segment.gain);
|
||||||
|
|
||||||
|
const divider_sample = if (i < divider_slice.len) divider_slice[i] else 1;
|
||||||
|
if (@abs(divider_sample) > 0.03) {
|
||||||
|
destination_slice[i] /= divider_sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const gain_per_sample = Transformation.createGainLookup(gain_changes, index, source_slice.len);
|
||||||
|
|
||||||
|
for (0..source_slice.len) |i| {
|
||||||
|
destination_slice[i] = source_slice[i] * (100 / gain_per_sample[i]);
|
||||||
|
|
||||||
|
const divider_sample = if (i < divider_slice.len) divider_slice[i] else 1;
|
||||||
|
if (@abs(divider_sample) > 0.03) {
|
||||||
|
destination_slice[i] /= divider_sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destination_block.recomputeMinMax();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,11 +340,6 @@ pub const Transform = struct {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eql(self: Transform, other: Transform) bool {
|
|
||||||
_ = self;
|
|
||||||
_ = other;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SampleList = struct {
|
pub const SampleList = struct {
|
||||||
@ -577,7 +603,7 @@ pub const SampleList = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply(self: *SampleList, source: *SampleList, from_block_index: Block.Id, block_count: usize, transform: Transform) void {
|
pub fn apply(self: *SampleList, source: *SampleList, from_block_index: Block.Id, block_count: usize, transform: Transformation) void {
|
||||||
transform.applySampleList(from_block_index, block_count, source, self);
|
transform.applySampleList(from_block_index, block_count, source, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,6 +754,7 @@ pub const File = struct {
|
|||||||
|
|
||||||
pub const View = struct {
|
pub const View = struct {
|
||||||
pub const Reference = union(enum) {
|
pub const Reference = union(enum) {
|
||||||
|
mirror: Id, // Sample list ID
|
||||||
file: Id,
|
file: Id,
|
||||||
channel: Id
|
channel: Id
|
||||||
};
|
};
|
||||||
@ -749,8 +776,8 @@ pub const View = struct {
|
|||||||
unit: ?NIDaq.Unit = .Voltage,
|
unit: ?NIDaq.Unit = .Voltage,
|
||||||
|
|
||||||
transformed_samples: Id,
|
transformed_samples: Id,
|
||||||
computed_transform: Transform = .{},
|
transformation: Transformation,
|
||||||
invalidated_transform_ranges: std.BoundedArray(RangeF64, 16) = .{},
|
dirty_transformation_ranges: std.BoundedArray(RangeF64, 16) = .{},
|
||||||
|
|
||||||
pub fn clear(self: *View) void {
|
pub fn clear(self: *View) void {
|
||||||
self.graph_cache.clear();
|
self.graph_cache.clear();
|
||||||
@ -801,7 +828,7 @@ pub const Project = struct {
|
|||||||
|
|
||||||
protocol_events: std.BoundedArray(ProtocolEvent, 32) = .{},
|
protocol_events: std.BoundedArray(ProtocolEvent, 32) = .{},
|
||||||
solutions: std.ArrayListUnmanaged(Solution) = .{},
|
solutions: std.ArrayListUnmanaged(Solution) = .{},
|
||||||
gain_changes: Transform.GainChanges = .{},
|
gain_changes: Transformation.GainChanges = .{},
|
||||||
|
|
||||||
sample_lists: GenerationalArray(SampleList) = .{},
|
sample_lists: GenerationalArray(SampleList) = .{},
|
||||||
channels: GenerationalArray(Channel) = .{},
|
channels: GenerationalArray(Channel) = .{},
|
||||||
@ -851,6 +878,9 @@ pub const Project = struct {
|
|||||||
.file => |file_id| {
|
.file => |file_id| {
|
||||||
const file = self.files.get(file_id).?;
|
const file = self.files.get(file_id).?;
|
||||||
return file.samples_id;
|
return file.samples_id;
|
||||||
|
},
|
||||||
|
.mirror => |sample_list_id| {
|
||||||
|
return sample_list_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -887,10 +917,19 @@ pub const Project = struct {
|
|||||||
return try self.sample_lists.insert(SampleList.init(allocator));
|
return try self.sample_lists.insert(SampleList.init(allocator));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn removeSampleList(self: *Project, sample_list_id: Id) void {
|
pub fn removeSampleList(self: *Project, allocator: std.mem.Allocator, sample_list_id: Id) void {
|
||||||
const sample_list = self.sample_lists.get(sample_list_id) orelse return;
|
const sample_list = self.sample_lists.get(sample_list_id) orelse return;
|
||||||
sample_list.deinit();
|
sample_list.deinit();
|
||||||
self.sample_lists.remove(sample_list_id);
|
self.sample_lists.remove(sample_list_id);
|
||||||
|
|
||||||
|
|
||||||
|
var view_iter = self.views.idIterator();
|
||||||
|
while (view_iter.next()) |view_id| {
|
||||||
|
const view = self.views.get(view_id).?;
|
||||||
|
if (view.reference == .mirror and view.reference.mirror.eql(sample_list_id)) {
|
||||||
|
self.removeView(allocator, view_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn readF64SliceFromFile(self: *Project, allocator: Allocator, file: std.fs.File) ![]f64 {
|
pub fn readF64SliceFromFile(self: *Project, allocator: Allocator, file: std.fs.File) ![]f64 {
|
||||||
@ -948,7 +987,7 @@ pub const Project = struct {
|
|||||||
pub fn removeFile(self: *Project, allocator: std.mem.Allocator, file_id: Id) void {
|
pub fn removeFile(self: *Project, allocator: std.mem.Allocator, file_id: Id) void {
|
||||||
const file = self.files.get(file_id) orelse return;
|
const file = self.files.get(file_id) orelse return;
|
||||||
|
|
||||||
self.removeSampleList(file.samples_id);
|
self.removeSampleList(allocator, file.samples_id);
|
||||||
file.deinit(allocator);
|
file.deinit(allocator);
|
||||||
|
|
||||||
self.files.remove(file_id);
|
self.files.remove(file_id);
|
||||||
@ -958,7 +997,7 @@ pub const Project = struct {
|
|||||||
pub fn removeChannel(self: *Project, allocator: std.mem.Allocator, channel_id: Id) void {
|
pub fn removeChannel(self: *Project, allocator: std.mem.Allocator, channel_id: Id) void {
|
||||||
const channel = self.channels.get(channel_id) orelse return;
|
const channel = self.channels.get(channel_id) orelse return;
|
||||||
|
|
||||||
self.removeSampleList(channel.collected_samples_id);
|
self.removeSampleList(allocator, channel.collected_samples_id);
|
||||||
|
|
||||||
channel.deinit(allocator);
|
channel.deinit(allocator);
|
||||||
self.channels.remove(channel_id);
|
self.channels.remove(channel_id);
|
||||||
@ -968,7 +1007,7 @@ pub const Project = struct {
|
|||||||
pub fn removeView(self: *Project, allocator: std.mem.Allocator, view_id: Id) void {
|
pub fn removeView(self: *Project, allocator: std.mem.Allocator, view_id: Id) void {
|
||||||
const view = self.views.get(view_id) orelse return;
|
const view = self.views.get(view_id) orelse return;
|
||||||
|
|
||||||
self.removeSampleList(view.transformed_samples);
|
self.removeSampleList(allocator, view.transformed_samples);
|
||||||
|
|
||||||
switch (view.reference) {
|
switch (view.reference) {
|
||||||
.file => |file_id| {
|
.file => |file_id| {
|
||||||
@ -976,6 +1015,9 @@ pub const Project = struct {
|
|||||||
},
|
},
|
||||||
.channel => |channel_id| {
|
.channel => |channel_id| {
|
||||||
self.removeChannel(allocator, channel_id);
|
self.removeChannel(allocator, channel_id);
|
||||||
|
},
|
||||||
|
.mirror => {
|
||||||
|
// Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1031,13 +1073,20 @@ pub const Project = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getViewTransform(self: *Project, view_id: Id) Transform {
|
pub fn getViewTransform(self: *Project, view_id: Id) Transformation {
|
||||||
_ = view_id;
|
_ = view_id;
|
||||||
return Transform{
|
return Transformation{
|
||||||
.gain_changes = self.gain_changes
|
.gain_changes = self.gain_changes
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn invalidateRangeOnAllViews(self: *Project, range: RangeF64) void {
|
||||||
|
var view_iter = self.views.iterator();
|
||||||
|
while (view_iter.next()) |view| {
|
||||||
|
view.dirty_transformation_ranges.append(range) catch continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn removeGainChange(self: *Project, index: usize) void {
|
pub fn removeGainChange(self: *Project, index: usize) void {
|
||||||
const gain_change = self.gain_changes.orderedRemove(index);
|
const gain_change = self.gain_changes.orderedRemove(index);
|
||||||
|
|
||||||
@ -1049,14 +1098,11 @@ pub const Project = struct {
|
|||||||
|
|
||||||
const invalidate_range = RangeF64.init(invalidate_lower_bound, invalidate_upper_bound);
|
const invalidate_range = RangeF64.init(invalidate_lower_bound, invalidate_upper_bound);
|
||||||
|
|
||||||
var view_iter = self.views.iterator();
|
self.invalidateRangeOnAllViews(invalidate_range);
|
||||||
while (view_iter.next()) |view| {
|
|
||||||
view.invalidated_transform_ranges.append(invalidate_range) catch continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn updateGainChange(self: *Project, index: usize, sample_index: f64) void {
|
pub fn updateGainChange(self: *Project, index: usize, sample_index: f64) void {
|
||||||
const gain_change: *Transform.GainChange = &self.gain_changes.slice()[index];
|
const gain_change: *Transformation.GainChange = &self.gain_changes.slice()[index];
|
||||||
|
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
if (self.gain_changes.buffer[index - 1].sample + SampleList.Block.capacity > sample_index) {
|
if (self.gain_changes.buffer[index - 1].sample + SampleList.Block.capacity > sample_index) {
|
||||||
@ -1078,10 +1124,7 @@ pub const Project = struct {
|
|||||||
@max(previous_sample_index, sample_index)
|
@max(previous_sample_index, sample_index)
|
||||||
);
|
);
|
||||||
|
|
||||||
var view_iter = self.views.iterator();
|
self.invalidateRangeOnAllViews(invalidated_range);
|
||||||
while (view_iter.next()) |view| {
|
|
||||||
view.invalidated_transform_ranges.append(invalidated_range) catch continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------- Serialization ------------------ //
|
// ------------------- Serialization ------------------ //
|
||||||
@ -1128,7 +1171,7 @@ pub const Project = struct {
|
|||||||
defer allocator.free(channel_name);
|
defer allocator.free(channel_name);
|
||||||
|
|
||||||
const sample_list_id = try self.addSampleList(allocator);
|
const sample_list_id = try self.addSampleList(allocator);
|
||||||
errdefer self.removeSampleList(sample_list_id);
|
errdefer self.removeSampleList(allocator, sample_list_id);
|
||||||
|
|
||||||
const channel = self.channels.get(id).?;
|
const channel = self.channels.get(id).?;
|
||||||
channel.* = Channel{
|
channel.* = Channel{
|
||||||
@ -1149,7 +1192,7 @@ pub const Project = struct {
|
|||||||
errdefer allocator.free(path);
|
errdefer allocator.free(path);
|
||||||
|
|
||||||
const sample_list_id = try self.addSampleList(allocator);
|
const sample_list_id = try self.addSampleList(allocator);
|
||||||
errdefer self.removeSampleList(sample_list_id);
|
errdefer self.removeSampleList(allocator, sample_list_id);
|
||||||
|
|
||||||
const file = self.files.get(id).?;
|
const file = self.files.get(id).?;
|
||||||
file.* = File{
|
file.* = File{
|
||||||
@ -1170,27 +1213,41 @@ pub const Project = struct {
|
|||||||
defer allocator.free(name);
|
defer allocator.free(name);
|
||||||
|
|
||||||
const reference_tag = try readInt(reader, u8);
|
const reference_tag = try readInt(reader, u8);
|
||||||
|
var reference_sample_list_id: Id = undefined;
|
||||||
|
var divider: ?Id = null;
|
||||||
var reference: View.Reference = undefined;
|
var reference: View.Reference = undefined;
|
||||||
if (reference_tag == @intFromEnum(View.Reference.file)) {
|
if (reference_tag == @intFromEnum(View.Reference.file)) {
|
||||||
reference = .{
|
const file_id = try readId(reader);
|
||||||
.file = try readId(reader)
|
reference = .{ .file = file_id };
|
||||||
};
|
reference_sample_list_id = self.files.get(file_id).?.samples_id;
|
||||||
} else if (reference_tag == @intFromEnum(View.Reference.channel)) {
|
} else if (reference_tag == @intFromEnum(View.Reference.channel)) {
|
||||||
reference = .{
|
const channel_id = try readId(reader);
|
||||||
.channel = try readId(reader)
|
reference = .{ .channel = channel_id };
|
||||||
};
|
reference_sample_list_id = self.channels.get(channel_id).?.collected_samples_id;
|
||||||
|
} else if (reference_tag == @intFromEnum(View.Reference.mirror)) {
|
||||||
|
const sample_list_id = try readId(reader);
|
||||||
|
reference = .{ .mirror = sample_list_id };
|
||||||
|
reference_sample_list_id = sample_list_id;
|
||||||
|
|
||||||
|
divider = try readId(reader);
|
||||||
} else {
|
} else {
|
||||||
return error.InvalidReferenceTag;
|
return error.InvalidReferenceTag;
|
||||||
}
|
}
|
||||||
|
assert(self.sample_lists.get(reference_sample_list_id) != null);
|
||||||
|
|
||||||
const sample_list_id = try self.addSampleList(allocator);
|
const sample_list_id = try self.addSampleList(allocator);
|
||||||
errdefer self.removeSampleList(sample_list_id);
|
errdefer self.removeSampleList(allocator, sample_list_id);
|
||||||
|
|
||||||
const view = self.views.get(id).?;
|
const view = self.views.get(id).?;
|
||||||
view.* = View{
|
view.* = View{
|
||||||
.reference = reference,
|
.reference = reference,
|
||||||
.transformed_samples = sample_list_id,
|
.transformed_samples = sample_list_id,
|
||||||
.name = try View.Name.fromSlice(name)
|
.name = try View.Name.fromSlice(name),
|
||||||
|
.transformation = Transformation{
|
||||||
|
.destination = sample_list_id,
|
||||||
|
.source = reference_sample_list_id,
|
||||||
|
.divider = divider
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1243,6 +1300,10 @@ pub const Project = struct {
|
|||||||
},
|
},
|
||||||
.file => |file_id| {
|
.file => |file_id| {
|
||||||
try writeInt(writer, u32, file_id.asInt());
|
try writeInt(writer, u32, file_id.asInt());
|
||||||
|
},
|
||||||
|
.mirror => |sample_list_id| {
|
||||||
|
try writeId(writer, sample_list_id);
|
||||||
|
try writeId(writer, view.transformation.divider.?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1351,119 +1412,144 @@ pub const CollectionTask = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const WorkJob = struct {
|
const WorkJob = struct {
|
||||||
const Stage = enum {
|
|
||||||
init,
|
|
||||||
launch_threads,
|
|
||||||
finished
|
|
||||||
};
|
|
||||||
|
|
||||||
view_id: Id,
|
transformation: Transformation,
|
||||||
stage: Stage = .init,
|
from_block: SampleList.Block.Id,
|
||||||
transform: Transform = .{},
|
block_count: usize,
|
||||||
|
running_threads: std.Thread.WaitGroup = .{},
|
||||||
|
|
||||||
mutex: std.Thread.Mutex = .{},
|
pub fn init(transformation: Transformation, from_block: SampleList.Block.Id, block_count: usize) WorkJob {
|
||||||
running_thread_jobs: std.ArrayListUnmanaged(SampleList.Block.Id) = .{},
|
return WorkJob{
|
||||||
|
.transformation = transformation,
|
||||||
processed_up_to: u32 = 0,
|
.from_block = from_block,
|
||||||
|
.block_count = block_count,
|
||||||
pub fn appendRunningThread(self: *WorkJob, allocator: Allocator, block_id: SampleList.Block.Id) !void {
|
};
|
||||||
self.mutex.lock();
|
|
||||||
defer self.mutex.unlock();
|
|
||||||
|
|
||||||
try self.running_thread_jobs.append(allocator, block_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn removeRunningThread(self: *WorkJob, block_id: SampleList.Block.Id) void {
|
|
||||||
self.mutex.lock();
|
|
||||||
defer self.mutex.unlock();
|
|
||||||
|
|
||||||
if (std.mem.indexOfScalar(SampleList.Block.Id, self.running_thread_jobs.items, block_id)) |index| {
|
|
||||||
_ = self.running_thread_jobs.swapRemove(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getRunningThreadCount(self: *WorkJob) usize {
|
|
||||||
self.mutex.lock();
|
|
||||||
defer self.mutex.unlock();
|
|
||||||
|
|
||||||
return self.running_thread_jobs.items.len;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn containsRunningThread(self: *WorkJob, block_id: SampleList.Block.Id) bool {
|
|
||||||
self.mutex.lock();
|
|
||||||
defer self.mutex.unlock();
|
|
||||||
|
|
||||||
return std.mem.indexOfScalar(SampleList.Block.Id, self.running_thread_jobs.items, block_id) != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *WorkJob, allocator: Allocator) void {
|
pub fn deinit(self: *WorkJob, allocator: Allocator) void {
|
||||||
self.running_thread_jobs.deinit(allocator);
|
_ = self;
|
||||||
|
_ = allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(self: *WorkJob, app: *App) !bool {
|
pub fn tick(self: *WorkJob) void {
|
||||||
if (self.stage == .finished) {
|
_ = self;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const project = &app.project;
|
|
||||||
const view = project.views.get(self.view_id) orelse return true;
|
|
||||||
|
|
||||||
const sample_list_id = project.getViewReferenceSampleListId(self.view_id);
|
|
||||||
const sample_list = project.sample_lists.get(sample_list_id).?;
|
|
||||||
|
|
||||||
if (!Transform.eql(project.getViewTransform(self.view_id), self.transform)) {
|
|
||||||
// Transforms changed, job needs to be cancelled.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (self.stage) {
|
|
||||||
.init => {
|
|
||||||
const transformed_samples = project.sample_lists.get(view.transformed_samples).?;
|
|
||||||
try transformed_samples.reserveEmptyBlocks(sample_list.blocks.items.len);
|
|
||||||
|
|
||||||
self.stage = .launch_threads;
|
|
||||||
},
|
|
||||||
.launch_threads => {
|
|
||||||
const max_block_to_process = 256;
|
|
||||||
|
|
||||||
while (self.getRunningThreadCount() < app.work_thread_pool.threads.len and self.processed_up_to < sample_list.blocks.items.len) {
|
|
||||||
const block_id = self.processed_up_to;
|
|
||||||
const block_count = @min(sample_list.blocks.items.len - self.processed_up_to, max_block_to_process);
|
|
||||||
self.processed_up_to += block_count;
|
|
||||||
|
|
||||||
try app.work_thread_pool.spawn(workThread, .{ self, project, block_id, block_count });
|
|
||||||
try self.appendRunningThread(app.allocator, block_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.processed_up_to == sample_list.blocks.items.len) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.finished => unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn workThread(self: *WorkJob, project: *Project, from_block_id: SampleList.Block.Id, block_count: SampleList.Block.Len) void {
|
|
||||||
defer self.removeRunningThread(from_block_id);
|
|
||||||
|
|
||||||
// var timer = std.time.Timer.start() catch unreachable;
|
|
||||||
// defer {
|
|
||||||
// const duration = timer.read();
|
|
||||||
// std.debug.print("finished {d:.5}ms\n", .{ @as(f64, @floatFromInt(duration)) / std.time.ns_per_ms });
|
|
||||||
// }
|
|
||||||
|
|
||||||
const view = project.views.get(self.view_id) orelse return;
|
|
||||||
const transformed_samples = project.sample_lists.get(view.transformed_samples) orelse return;
|
|
||||||
|
|
||||||
const sample_list_id = project.getViewReferenceSampleListId(self.view_id);
|
|
||||||
const sample_list = project.sample_lists.get(sample_list_id) orelse return;
|
|
||||||
|
|
||||||
transformed_samples.apply(sample_list, from_block_id, block_count, self.transform);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// const WorkJob = struct {
|
||||||
|
// const Stage = enum {
|
||||||
|
// init,
|
||||||
|
// launch_threads,
|
||||||
|
// finished
|
||||||
|
// };
|
||||||
|
|
||||||
|
// view_id: Id,
|
||||||
|
// stage: Stage = .init,
|
||||||
|
// transform: Transform = .{},
|
||||||
|
|
||||||
|
// mutex: std.Thread.Mutex = .{},
|
||||||
|
// running_thread_jobs: std.ArrayListUnmanaged(SampleList.Block.Id) = .{},
|
||||||
|
|
||||||
|
// processed_up_to: u32 = 0,
|
||||||
|
|
||||||
|
// pub fn appendRunningThread(self: *WorkJob, allocator: Allocator, block_id: SampleList.Block.Id) !void {
|
||||||
|
// self.mutex.lock();
|
||||||
|
// defer self.mutex.unlock();
|
||||||
|
|
||||||
|
// try self.running_thread_jobs.append(allocator, block_id);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn removeRunningThread(self: *WorkJob, block_id: SampleList.Block.Id) void {
|
||||||
|
// self.mutex.lock();
|
||||||
|
// defer self.mutex.unlock();
|
||||||
|
|
||||||
|
// if (std.mem.indexOfScalar(SampleList.Block.Id, self.running_thread_jobs.items, block_id)) |index| {
|
||||||
|
// _ = self.running_thread_jobs.swapRemove(index);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn getRunningThreadCount(self: *WorkJob) usize {
|
||||||
|
// self.mutex.lock();
|
||||||
|
// defer self.mutex.unlock();
|
||||||
|
|
||||||
|
// return self.running_thread_jobs.items.len;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn containsRunningThread(self: *WorkJob, block_id: SampleList.Block.Id) bool {
|
||||||
|
// self.mutex.lock();
|
||||||
|
// defer self.mutex.unlock();
|
||||||
|
|
||||||
|
// return std.mem.indexOfScalar(SampleList.Block.Id, self.running_thread_jobs.items, block_id) != null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn deinit(self: *WorkJob, allocator: Allocator) void {
|
||||||
|
// self.running_thread_jobs.deinit(allocator);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn update(self: *WorkJob, app: *App) !bool {
|
||||||
|
// if (self.stage == .finished) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const project = &app.project;
|
||||||
|
// const view = project.views.get(self.view_id) orelse return true;
|
||||||
|
|
||||||
|
// const sample_list_id = project.getViewReferenceSampleListId(self.view_id);
|
||||||
|
// const sample_list = project.sample_lists.get(sample_list_id).?;
|
||||||
|
|
||||||
|
// if (!Transform.eql(project.getViewTransform(self.view_id), self.transform)) {
|
||||||
|
// // Transforms changed, job needs to be cancelled.
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// switch (self.stage) {
|
||||||
|
// .init => {
|
||||||
|
// const transformed_samples = project.sample_lists.get(view.transformed_samples).?;
|
||||||
|
// try transformed_samples.reserveEmptyBlocks(sample_list.blocks.items.len);
|
||||||
|
|
||||||
|
// self.stage = .launch_threads;
|
||||||
|
// },
|
||||||
|
// .launch_threads => {
|
||||||
|
// const max_block_to_process = 256;
|
||||||
|
|
||||||
|
// while (self.getRunningThreadCount() < app.work_thread_pool.threads.len and self.processed_up_to < sample_list.blocks.items.len) {
|
||||||
|
// const block_id = self.processed_up_to;
|
||||||
|
// const block_count = @min(sample_list.blocks.items.len - self.processed_up_to, max_block_to_process);
|
||||||
|
// self.processed_up_to += block_count;
|
||||||
|
|
||||||
|
// try app.work_thread_pool.spawn(workThread, .{ self, project, block_id, block_count });
|
||||||
|
// try self.appendRunningThread(app.allocator, block_id);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (self.processed_up_to == sample_list.blocks.items.len) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// .finished => unreachable
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn workThread(self: *WorkJob, project: *Project, from_block_id: SampleList.Block.Id, block_count: SampleList.Block.Len) void {
|
||||||
|
// defer self.removeRunningThread(from_block_id);
|
||||||
|
|
||||||
|
// // var timer = std.time.Timer.start() catch unreachable;
|
||||||
|
// // defer {
|
||||||
|
// // const duration = timer.read();
|
||||||
|
// // std.debug.print("finished {d:.5}ms\n", .{ @as(f64, @floatFromInt(duration)) / std.time.ns_per_ms });
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// const view = project.views.get(self.view_id) orelse return;
|
||||||
|
// const transformed_samples = project.sample_lists.get(view.transformed_samples) orelse return;
|
||||||
|
|
||||||
|
// const sample_list_id = project.getViewReferenceSampleListId(self.view_id);
|
||||||
|
// const sample_list = project.sample_lists.get(sample_list_id) orelse return;
|
||||||
|
|
||||||
|
// transformed_samples.apply(sample_list, from_block_id, block_count, self.transform);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
ui: UI,
|
ui: UI,
|
||||||
double_pass_ui: bool = true,
|
double_pass_ui: bool = true,
|
||||||
@ -1857,37 +1943,78 @@ pub fn tick(self: *App) !void {
|
|||||||
try self.showUI();
|
try self.showUI();
|
||||||
|
|
||||||
{
|
{
|
||||||
const block_size = SampleList.Block.capacity;
|
|
||||||
|
|
||||||
var view_iter = self.project.views.idIterator();
|
var view_iter = self.project.views.idIterator();
|
||||||
while (view_iter.next()) |view_id| {
|
while (view_iter.next()) |view_id| {
|
||||||
const view = self.getView(view_id).?;
|
const view = self.getView(view_id).?;
|
||||||
|
view.transformation.gain_changes = self.project.gain_changes;
|
||||||
|
|
||||||
const referenced_samples_id = self.project.getViewReferenceSampleListId(view_id);
|
const source_id = view.transformation.source;
|
||||||
const referenced_samples = self.project.sample_lists.get(referenced_samples_id).?;
|
const source = self.project.sample_lists.get(source_id).?;
|
||||||
const computed_samples_id = view.transformed_samples;
|
const source_len = source.getLength();
|
||||||
const computed_samples = self.project.sample_lists.get(computed_samples_id).?;
|
|
||||||
|
|
||||||
const transform = self.project.getViewTransform(view_id);
|
const destination_id = view.transformation.destination;
|
||||||
const computed_len = computed_samples.getLength();
|
const destination = self.project.sample_lists.get(destination_id).?;
|
||||||
const reference_len = referenced_samples.getLength();
|
const destination_len = destination.getLength();
|
||||||
|
|
||||||
if (reference_len > computed_len) {
|
if (source_len != destination_len) {
|
||||||
view.invalidated_transform_ranges.append(RangeF64.init(@floatFromInt(computed_len), @floatFromInt(reference_len))) catch {};
|
if (view.transformation.divider) |divider| {
|
||||||
|
const divider_list = self.project.sample_lists.get(divider).?;
|
||||||
|
if (divider_list.getLength() < source_len) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const source_len_f64: f64 = @max(0, @as(f64, @floatFromInt(source_len)) - 1);
|
||||||
|
const destination_len_f64: f64 = @max(0, @as(f64, @floatFromInt(destination_len)) - 1);
|
||||||
|
const range = RangeF64.init(@min(source_len_f64, destination_len_f64), @max(source_len_f64, destination_len_f64));
|
||||||
|
view.dirty_transformation_ranges.append(range) catch {};
|
||||||
|
|
||||||
|
// var other_view_iter = self.project.views.idIterator();
|
||||||
|
// while (other_view_iter.next()) |other_view_id| {
|
||||||
|
// const other_view = self.project.views.get(other_view_id).?;
|
||||||
|
// if (other_view.reference == .mirror and other_view.reference.mirror.eql(source_id)) {
|
||||||
|
// view.dirty_transformation_ranges.append(range) catch {};
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const block_size = SampleList.Block.capacity;
|
||||||
|
|
||||||
|
var view_iter = self.project.views.iterator();
|
||||||
|
while (view_iter.next()) |view| {
|
||||||
|
const source_id = view.transformation.source;
|
||||||
|
const source = self.project.sample_lists.get(source_id).?;
|
||||||
|
const source_len = source.getLength();
|
||||||
|
|
||||||
|
var last_source_block: usize = 0;
|
||||||
|
if (source_len > 0) {
|
||||||
|
last_source_block = source_len - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (view.invalidated_transform_ranges.constSlice()) |range| {
|
for (view.dirty_transformation_ranges.constSlice()) |range| {
|
||||||
const from_block: usize = @intFromFloat(@divFloor(range.lower, block_size));
|
var from_block = last_source_block;
|
||||||
const to_block: usize = @intFromFloat(@divFloor(@min(range.upper, @as(f64, @floatFromInt(reference_len)) - 1), block_size));
|
if (!std.math.isInf(range.lower) and range.lower >= 0) {
|
||||||
if (from_block > to_block) continue;
|
from_block = @intFromFloat(@divFloor(range.lower, block_size));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var to_block = last_source_block;
|
||||||
|
if (!std.math.isInf(range.upper) and range.upper >= 0) {
|
||||||
|
to_block = @intFromFloat(@divFloor(range.upper, block_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from_block > to_block) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const block_count = to_block - from_block + 1;
|
const block_count = to_block - from_block + 1;
|
||||||
|
try view.transformation.apply(&self.project, from_block, block_count);
|
||||||
try computed_samples.ensureTotalBlocks(from_block + block_count);
|
view.graph_cache.invalidate();
|
||||||
|
|
||||||
transform.applySampleList(@intCast(from_block), block_count, referenced_samples, computed_samples);
|
|
||||||
view.graph_cache.invalidateRange(range);
|
|
||||||
}
|
}
|
||||||
view.invalidated_transform_ranges.len = 0;
|
view.dirty_transformation_ranges.len = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1954,43 +2081,43 @@ pub fn tick(self: *App) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
// {
|
||||||
var view_iter = self.project.views.idIterator();
|
// var view_iter = self.project.views.idIterator();
|
||||||
while (view_iter.next()) |view_id| {
|
// while (view_iter.next()) |view_id| {
|
||||||
const view = self.project.views.get(view_id).?;
|
// const view = self.project.views.get(view_id).?;
|
||||||
_ = view;
|
// _ = view;
|
||||||
|
|
||||||
// TODO:
|
// // TODO:
|
||||||
// if (Transform.eqlSlice(view.computed_transforms.constSlice(), view.transforms.constSlice())) {
|
// // if (Transform.eqlSlice(view.computed_transforms.constSlice(), view.transforms.constSlice())) {
|
||||||
// continue;
|
// // continue;
|
||||||
// }
|
// // }
|
||||||
|
|
||||||
// _ = try self.work_jobs.insert(WorkJob{
|
// // _ = try self.work_jobs.insert(WorkJob{
|
||||||
// .transforms = view.transforms,
|
// // .transforms = view.transforms,
|
||||||
// .view_id = view_id
|
// // .view_id = view_id
|
||||||
// });
|
// // });
|
||||||
|
|
||||||
// view.computed_transforms.len = 0;
|
// // view.computed_transforms.len = 0;
|
||||||
// view.computed_transforms.appendSliceAssumeCapacity(view.transforms.constSlice());
|
// // view.computed_transforms.appendSliceAssumeCapacity(view.transforms.constSlice());
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
var work_job_iter = self.work_jobs.idIterator();
|
// var work_job_iter = self.work_jobs.idIterator();
|
||||||
while (work_job_iter.next()) |work_job_id| {
|
// while (work_job_iter.next()) |work_job_id| {
|
||||||
const work_job = self.work_jobs.get(work_job_id).?;
|
// const work_job = self.work_jobs.get(work_job_id).?;
|
||||||
const job_done = try work_job.update(self);
|
// const job_done = try work_job.update(self);
|
||||||
|
|
||||||
if (job_done) {
|
// if (job_done) {
|
||||||
work_job.stage = .finished;
|
// work_job.stage = .finished;
|
||||||
if (work_job.getRunningThreadCount() == 0) {
|
// if (work_job.getRunningThreadCount() == 0) {
|
||||||
std.debug.print("job done {}\n", .{work_job_id});
|
// std.debug.print("job done {}\n", .{work_job_id});
|
||||||
work_job.deinit(self.allocator);
|
// work_job.deinit(self.allocator);
|
||||||
self.work_jobs.remove(work_job_id);
|
// self.work_jobs.remove(work_job_id);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pushCommand(self: *App, command: Command) void {
|
pub fn pushCommand(self: *App, command: Command) void {
|
||||||
@ -2268,7 +2395,7 @@ pub fn addFile(self: *App, path: []const u8) !Id {
|
|||||||
errdefer self.allocator.free(path_dupe);
|
errdefer self.allocator.free(path_dupe);
|
||||||
|
|
||||||
const sample_list_id = try self.project.addSampleList(self.allocator);
|
const sample_list_id = try self.project.addSampleList(self.allocator);
|
||||||
errdefer self.project.removeSampleList(sample_list_id);
|
errdefer self.project.removeSampleList(self.allocator, sample_list_id);
|
||||||
|
|
||||||
const file = self.project.files.get(id).?;
|
const file = self.project.files.get(id).?;
|
||||||
file.* = File{
|
file.* = File{
|
||||||
@ -2314,7 +2441,7 @@ pub fn addChannel(self: *App, channel_name: []const u8) !Id {
|
|||||||
errdefer self.project.channels.remove(id);
|
errdefer self.project.channels.remove(id);
|
||||||
|
|
||||||
const sample_list_id = try self.project.addSampleList(self.allocator);
|
const sample_list_id = try self.project.addSampleList(self.allocator);
|
||||||
errdefer self.project.removeSampleList(sample_list_id);
|
errdefer self.project.removeSampleList(self.allocator, sample_list_id);
|
||||||
|
|
||||||
const channel = self.project.channels.get(id).?;
|
const channel = self.project.channels.get(id).?;
|
||||||
channel.* = Channel{
|
channel.* = Channel{
|
||||||
@ -2412,12 +2539,23 @@ pub fn addView(self: *App, reference: View.Reference) !Id {
|
|||||||
errdefer self.project.views.remove(id);
|
errdefer self.project.views.remove(id);
|
||||||
|
|
||||||
const sample_list_id = try self.project.addSampleList(self.allocator);
|
const sample_list_id = try self.project.addSampleList(self.allocator);
|
||||||
errdefer self.project.removeSampleList(sample_list_id);
|
errdefer self.project.removeSampleList(self.allocator, sample_list_id);
|
||||||
|
|
||||||
|
const reference_sample_list_id = switch (reference) {
|
||||||
|
.channel => |channel_id| self.project.channels.get(channel_id).?.collected_samples_id,
|
||||||
|
.file => |file_id| self.project.files.get(file_id).?.samples_id,
|
||||||
|
.mirror => |list_id| list_id,
|
||||||
|
};
|
||||||
|
assert(self.project.sample_lists.get(reference_sample_list_id) != null);
|
||||||
|
|
||||||
const view = self.project.views.get(id).?;
|
const view = self.project.views.get(id).?;
|
||||||
view.* = View{
|
view.* = View{
|
||||||
.reference = reference,
|
.reference = reference,
|
||||||
.transformed_samples = sample_list_id
|
.transformed_samples = sample_list_id,
|
||||||
|
.transformation = .{
|
||||||
|
.source = reference_sample_list_id,
|
||||||
|
.destination = sample_list_id,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.loadView(id) catch |e| {
|
self.loadView(id) catch |e| {
|
||||||
@ -2433,6 +2571,9 @@ fn refreshViewAvailableXYRanges(self: *App, id: Id) void {
|
|||||||
const sample_list = self.project.sample_lists.get(sample_list_id).?;
|
const sample_list = self.project.sample_lists.get(sample_list_id).?;
|
||||||
|
|
||||||
view.available_x_range = RangeF64.init(0, @floatFromInt(sample_list.getLength()));
|
view.available_x_range = RangeF64.init(0, @floatFromInt(sample_list.getLength()));
|
||||||
|
if (view.available_x_range.size() == 0) {
|
||||||
|
view.available_x_range = RangeF64.init(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
switch (view.reference) {
|
switch (view.reference) {
|
||||||
.channel => |channel_id| if (self.getChannel(channel_id)) |channel| {
|
.channel => |channel_id| if (self.getChannel(channel_id)) |channel| {
|
||||||
@ -2444,6 +2585,9 @@ fn refreshViewAvailableXYRanges(self: *App, id: Id) void {
|
|||||||
const min_sample = sample_list.min orelse 0;
|
const min_sample = sample_list.min orelse 0;
|
||||||
const max_sample = sample_list.max orelse 1;
|
const max_sample = sample_list.max orelse 1;
|
||||||
view.available_y_range = RangeF64.init(max_sample, min_sample);
|
view.available_y_range = RangeF64.init(max_sample, min_sample);
|
||||||
|
},
|
||||||
|
.mirror => {
|
||||||
|
view.available_y_range = RangeF64.init(sample_list.max orelse 10, sample_list.min orelse -10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,62 +163,67 @@ fn showToolbar(ctx: Context, view_id: Id) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (view.reference == .channel) {
|
switch (view.reference) {
|
||||||
const channel_id = view.reference.channel;
|
.channel => |channel_id| {
|
||||||
const channel = ctx.app.getChannel(channel_id).?;
|
const channel = ctx.app.getChannel(channel_id).?;
|
||||||
const channel_name = utils.getBoundedStringZ(&channel.name);
|
const channel_name = utils.getBoundedStringZ(&channel.name);
|
||||||
// const channel_type = NIDaq.getChannelType(channel_name).?;
|
// const channel_type = NIDaq.getChannelType(channel_name).?;
|
||||||
|
|
||||||
{
|
{
|
||||||
const follow = ui.textButton("Follow");
|
const follow = ui.textButton("Follow");
|
||||||
follow.background = srcery.hard_black;
|
follow.background = srcery.hard_black;
|
||||||
if (view.follow) {
|
if (view.follow) {
|
||||||
follow.borders = UI.Borders.bottom(.{
|
follow.borders = UI.Borders.bottom(.{
|
||||||
.color = srcery.green,
|
.color = srcery.green,
|
||||||
.size = 4
|
.size = 4
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (ui.signal(follow).clicked()) {
|
if (ui.signal(follow).clicked()) {
|
||||||
view.follow = !view.follow;
|
view.follow = !view.follow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (channel_type == .analog_output) {
|
||||||
|
// const button = ui.button(ui.keyFromString("Output generation"));
|
||||||
|
// button.texture = Assets.output_generation;
|
||||||
|
// button.size.y = UI.Sizing.initGrowFull();
|
||||||
|
|
||||||
|
// const signal = ui.signal(button);
|
||||||
|
// if (signal.clicked()) {
|
||||||
|
// if (ctx.app.isChannelOutputing(channel_id)) {
|
||||||
|
// ctx.app.pushCommand(.{
|
||||||
|
// .stop_output = channel_id
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// ctx.view_controls.view_protocol_modal = view_id;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var color = rl.Color.white;
|
||||||
|
// if (ctx.app.isChannelOutputing(channel_id)) {
|
||||||
|
// color = srcery.red;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (signal.active) {
|
||||||
|
// button.texture_color = color.alpha(0.6);
|
||||||
|
// } else if (signal.hot) {
|
||||||
|
// button.texture_color = color.alpha(0.8);
|
||||||
|
// } else {
|
||||||
|
// button.texture_color = color;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
view_name = channel_name;
|
||||||
|
},
|
||||||
|
.file => |file_id| {
|
||||||
|
const file = ctx.app.getFile(file_id).?;
|
||||||
|
|
||||||
|
view_name = std.fs.path.stem(file.path);
|
||||||
|
},
|
||||||
|
.mirror => |sample_list_id| {
|
||||||
|
_ = sample_list_id;
|
||||||
|
view_name = "Proportional";
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (channel_type == .analog_output) {
|
|
||||||
// const button = ui.button(ui.keyFromString("Output generation"));
|
|
||||||
// button.texture = Assets.output_generation;
|
|
||||||
// button.size.y = UI.Sizing.initGrowFull();
|
|
||||||
|
|
||||||
// const signal = ui.signal(button);
|
|
||||||
// if (signal.clicked()) {
|
|
||||||
// if (ctx.app.isChannelOutputing(channel_id)) {
|
|
||||||
// ctx.app.pushCommand(.{
|
|
||||||
// .stop_output = channel_id
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// ctx.view_controls.view_protocol_modal = view_id;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var color = rl.Color.white;
|
|
||||||
// if (ctx.app.isChannelOutputing(channel_id)) {
|
|
||||||
// color = srcery.red;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (signal.active) {
|
|
||||||
// button.texture_color = color.alpha(0.6);
|
|
||||||
// } else if (signal.hot) {
|
|
||||||
// button.texture_color = color.alpha(0.8);
|
|
||||||
// } else {
|
|
||||||
// button.texture_color = color;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
view_name = channel_name;
|
|
||||||
} else if (view.reference == .file) {
|
|
||||||
const file_id = view.reference.file;
|
|
||||||
const file = ctx.app.getFile(file_id).?;
|
|
||||||
|
|
||||||
view_name = std.fs.path.stem(file.path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (view.name.len > 0) {
|
if (view.name.len > 0) {
|
||||||
|
@ -12,7 +12,7 @@ const raylib_h = @cImport({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const log = std.log;
|
const log = std.log;
|
||||||
const profiler_enabled = builtin.mode == .Debug;
|
const profiler_enabled = true; //builtin.mode == .Debug;
|
||||||
|
|
||||||
// TODO: Maybe move this to a config.zig or options.zig file.
|
// TODO: Maybe move this to a config.zig or options.zig file.
|
||||||
// Have all of the contstants in a single file.
|
// Have all of the contstants in a single file.
|
||||||
|
@ -73,6 +73,10 @@ view_name_input: UI.TextInputStorage,
|
|||||||
channel_save_file_picker: ?Platform.FilePickerId = null,
|
channel_save_file_picker: ?Platform.FilePickerId = null,
|
||||||
file_save_file_picker: ?Platform.FilePickerId = null,
|
file_save_file_picker: ?Platform.FilePickerId = null,
|
||||||
|
|
||||||
|
// Proportional modal
|
||||||
|
proportion_view1: ?Id = null, // View ID
|
||||||
|
proportion_view2: ?Id = null, // View ID
|
||||||
|
|
||||||
pub fn init(app: *App) !MainScreen {
|
pub fn init(app: *App) !MainScreen {
|
||||||
const allocator = app.allocator;
|
const allocator = app.allocator;
|
||||||
|
|
||||||
@ -422,7 +426,7 @@ fn showProportionalAdd(self: *MainScreen) !void {
|
|||||||
.key = ui.keyFromString("Proportional add modal"),
|
.key = ui.keyFromString("Proportional add modal"),
|
||||||
.background = srcery.black,
|
.background = srcery.black,
|
||||||
.size_x = UI.Sizing.initGrowUpTo(.{ .pixels = 300 }),
|
.size_x = UI.Sizing.initGrowUpTo(.{ .pixels = 300 }),
|
||||||
.size_y = UI.Sizing.initGrowUpTo(.{ .pixels = 300 }),
|
.size_y = UI.Sizing.initGrowUpTo(.{ .pixels = 200 }),
|
||||||
.layout_direction = .top_to_bottom,
|
.layout_direction = .top_to_bottom,
|
||||||
.padding = UI.Padding.all(ui.rem(1.5)),
|
.padding = UI.Padding.all(ui.rem(1.5)),
|
||||||
.flags = &.{ .clickable },
|
.flags = &.{ .clickable },
|
||||||
@ -431,6 +435,169 @@ fn showProportionalAdd(self: *MainScreen) !void {
|
|||||||
container.beginChildren();
|
container.beginChildren();
|
||||||
defer container.endChildren();
|
defer container.endChildren();
|
||||||
defer _ = ui.signal(container);
|
defer _ = ui.signal(container);
|
||||||
|
|
||||||
|
if (self.proportion_view1 != null and self.app.project.views.get(self.proportion_view1.?) == null) {
|
||||||
|
self.proportion_view1 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.proportion_view2 != null and self.app.project.views.get(self.proportion_view2.?) == null) {
|
||||||
|
self.proportion_view2 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var arena = std.heap.ArenaAllocator.init(self.app.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
|
||||||
|
var available_views = std.ArrayList(struct { view_id: App.Id, name: []const u8 }).init(arena.allocator());
|
||||||
|
|
||||||
|
{
|
||||||
|
var view_iter = self.app.project.views.idIterator();
|
||||||
|
while (view_iter.next()) |view_id| {
|
||||||
|
const view = self.app.project.views.get(view_id).?;
|
||||||
|
if (view.reference != .channel) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const channel_id = view.reference.channel;
|
||||||
|
const channel = self.app.project.channels.get(channel_id).?;
|
||||||
|
const view_name = utils.getBoundedStringZ(&channel.name);
|
||||||
|
|
||||||
|
try available_views.append(.{ .view_id = view_id, .name = view_name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var row = ui.createBox(.{
|
||||||
|
.size_x = UI.Sizing.initGrowFull(),
|
||||||
|
.size_y = UI.Sizing.initFitChildren(),
|
||||||
|
.layout_direction = .left_to_right,
|
||||||
|
.layout_gap = ui.rem(0.5),
|
||||||
|
});
|
||||||
|
row.beginChildren();
|
||||||
|
defer row.endChildren();
|
||||||
|
|
||||||
|
{
|
||||||
|
const select = ui.textButton("Select dividend");
|
||||||
|
if (self.proportion_view1) |view_id| {
|
||||||
|
const view = self.app.project.views.get(view_id).?;
|
||||||
|
assert(view.reference == .channel);
|
||||||
|
const channel = self.app.project.channels.get(view.reference.channel).?;
|
||||||
|
select.setFmtText("Dividend: {s}", .{utils.getBoundedStringZ(&channel.name)});
|
||||||
|
}
|
||||||
|
|
||||||
|
select.borders = UI.Borders.all(.{ .color = srcery.blue, .size = 4 });
|
||||||
|
const signal = ui.signal(select);
|
||||||
|
if (signal.clicked()) {
|
||||||
|
select.persistent.open = !select.persistent.open;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (select.persistent.open) {
|
||||||
|
const popup = ui.createBox(.{
|
||||||
|
.key = ui.keyFromString("Transform 1 popup"),
|
||||||
|
.size_x = UI.Sizing.initFixedPixels(ui.rem(10)),
|
||||||
|
.size_y = UI.Sizing.initFitChildren(),
|
||||||
|
.flags = &.{ .clickable, .scrollable },
|
||||||
|
.layout_direction = .top_to_bottom,
|
||||||
|
.float_relative_to = select,
|
||||||
|
.background = srcery.black,
|
||||||
|
.borders = UI.Borders.all(.{ .color = srcery.bright_black, .size = 4 }),
|
||||||
|
.draw_on_top = true
|
||||||
|
});
|
||||||
|
popup.setFloatPosition(0, select.persistent.size.y);
|
||||||
|
popup.beginChildren();
|
||||||
|
defer popup.endChildren();
|
||||||
|
defer _ = ui.signal(popup);
|
||||||
|
|
||||||
|
for (available_views.items) |option| {
|
||||||
|
const select_option = ui.textButton(option.name);
|
||||||
|
select_option.alignment.x = .start;
|
||||||
|
select_option.size.x = UI.Sizing.initGrowFull();
|
||||||
|
select_option.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 });
|
||||||
|
select_option.background = srcery.black;
|
||||||
|
|
||||||
|
if (ui.signal(select_option).clicked()) {
|
||||||
|
select.persistent.open = false;
|
||||||
|
self.proportion_view1 = option.view_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.clicked_outside) {
|
||||||
|
select.persistent.open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const select = ui.textButton("Select divisor");
|
||||||
|
if (self.proportion_view2) |view_id| {
|
||||||
|
const view = self.app.project.views.get(view_id).?;
|
||||||
|
assert(view.reference == .channel);
|
||||||
|
const channel = self.app.project.channels.get(view.reference.channel).?;
|
||||||
|
select.setFmtText("Divisor: {s}", .{utils.getBoundedStringZ(&channel.name)});
|
||||||
|
}
|
||||||
|
|
||||||
|
select.borders = UI.Borders.all(.{ .color = srcery.blue, .size = 4 });
|
||||||
|
const signal = ui.signal(select);
|
||||||
|
if (signal.clicked()) {
|
||||||
|
select.persistent.open = !select.persistent.open;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (select.persistent.open) {
|
||||||
|
const popup = ui.createBox(.{
|
||||||
|
.key = ui.keyFromString("Transform 1 popup"),
|
||||||
|
.size_x = UI.Sizing.initFixedPixels(ui.rem(10)),
|
||||||
|
.size_y = UI.Sizing.initFitChildren(),
|
||||||
|
.flags = &.{ .clickable, .scrollable },
|
||||||
|
.layout_direction = .top_to_bottom,
|
||||||
|
.float_relative_to = select,
|
||||||
|
.background = srcery.black,
|
||||||
|
.borders = UI.Borders.all(.{ .color = srcery.bright_black, .size = 4 }),
|
||||||
|
.draw_on_top = true
|
||||||
|
});
|
||||||
|
popup.setFloatPosition(0, select.persistent.size.y);
|
||||||
|
popup.beginChildren();
|
||||||
|
defer popup.endChildren();
|
||||||
|
defer _ = ui.signal(popup);
|
||||||
|
|
||||||
|
for (available_views.items) |option| {
|
||||||
|
const select_option = ui.textButton(option.name);
|
||||||
|
select_option.alignment.x = .start;
|
||||||
|
select_option.size.x = UI.Sizing.initGrowFull();
|
||||||
|
select_option.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 2 });
|
||||||
|
select_option.background = srcery.black;
|
||||||
|
|
||||||
|
if (ui.signal(select_option).clicked()) {
|
||||||
|
select.persistent.open = false;
|
||||||
|
self.proportion_view2 = option.view_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.clicked_outside) {
|
||||||
|
select.persistent.open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const btn = ui.textButton("Confirm");
|
||||||
|
btn.borders = UI.Borders.all(.{ .color = srcery.hard_black, .size = 4 });
|
||||||
|
if (self.proportion_view1 != null and self.proportion_view2 != null) {
|
||||||
|
btn.borders = UI.Borders.all(.{ .color = srcery.green, .size = 4 });
|
||||||
|
|
||||||
|
if (ui.signal(btn).clicked()) {
|
||||||
|
self.modal = null;
|
||||||
|
const view1 = self.app.project.views.get(self.proportion_view1.?).?;
|
||||||
|
const channel1 = self.app.project.channels.get(view1.reference.channel).?;
|
||||||
|
const view2 = self.app.project.views.get(self.proportion_view2.?).?;
|
||||||
|
const channel2 = self.app.project.channels.get(view2.reference.channel).?;
|
||||||
|
|
||||||
|
const new_view_id = try self.app.addView(.{ .mirror = channel1.collected_samples_id });
|
||||||
|
const new_view = self.app.project.views.get(new_view_id).?;
|
||||||
|
new_view.transformation.divider = channel2.collected_samples_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn setProtocolErrorMessage(self: *MainScreen, comptime fmt: []const u8, args: anytype) !void {
|
// fn setProtocolErrorMessage(self: *MainScreen, comptime fmt: []const u8, args: anytype) !void {
|
||||||
@ -742,6 +909,9 @@ fn showViewSettings(self: *MainScreen, view_id: Id) !void {
|
|||||||
file.path = path;
|
file.path = path;
|
||||||
self.app.pushCommand(.{ .reload_file = file_id });
|
self.app.pushCommand(.{ .reload_file = file_id });
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
.mirror => |sample_list_id| {
|
||||||
|
_ = sample_list_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user