Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
| Show All 32 Lines | |||||
| #include "spreadsheet_data_source_geometry.hh" | #include "spreadsheet_data_source_geometry.hh" | ||||
| #include "spreadsheet_intern.hh" | #include "spreadsheet_intern.hh" | ||||
| #include "spreadsheet_layout.hh" | #include "spreadsheet_layout.hh" | ||||
| #include "spreadsheet_row_filter.hh" | #include "spreadsheet_row_filter.hh" | ||||
| namespace blender::ed::spreadsheet { | namespace blender::ed::spreadsheet { | ||||
| template<typename OperationFn> | template<typename T, typename OperationFn> | ||||
| static void apply_filter_operation(const ColumnValues &values, | static void apply_filter_operation(const VArray<T> &data, | ||||
| OperationFn check_fn, | OperationFn check_fn, | ||||
| MutableSpan<bool> rows_included) | const IndexMask mask, | ||||
| Vector<int64_t> &new_indices) | |||||
| { | { | ||||
| for (const int i : rows_included.index_range()) { | for (const int64_t i : mask) { | ||||
| if (!rows_included[i]) { | if (check_fn(data[i])) { | ||||
| continue; | new_indices.append(i); | ||||
| } | |||||
| CellValue cell_value; | |||||
| values.get_value(i, cell_value); | |||||
| if (!check_fn(cell_value)) { | |||||
| rows_included[i] = false; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void apply_row_filter(const SpreadsheetLayout &spreadsheet_layout, | static void apply_row_filter(const SpreadsheetRowFilter &row_filter, | ||||
| const SpreadsheetRowFilter &row_filter, | const Map<StringRef, const ColumnValues *> &columns, | ||||
| MutableSpan<bool> rows_included) | const IndexMask prev_mask, | ||||
| Vector<int64_t> &new_indices) | |||||
| { | { | ||||
| for (const ColumnLayout &column : spreadsheet_layout.columns) { | const ColumnValues &column = *columns.lookup(row_filter.column_name); | ||||
| const ColumnValues &values = *column.values; | const fn::GVArray &column_data = column.data(); | ||||
| if (values.name() != row_filter.column_name) { | if (column_data.type().is<float>()) { | ||||
| continue; | const float value = row_filter.value_float; | ||||
| } | |||||
| switch (values.type()) { | |||||
| case SPREADSHEET_VALUE_TYPE_INT32: { | |||||
| const int value = row_filter.value_int; | |||||
| switch (row_filter.operation) { | switch (row_filter.operation) { | ||||
| case SPREADSHEET_ROW_FILTER_EQUAL: { | case SPREADSHEET_ROW_FILTER_EQUAL: { | ||||
| const float threshold = row_filter.threshold; | |||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<float>(), | ||||
| [value](const CellValue &cell_value) -> bool { | [value, threshold](const float cell) { return std::abs(cell - value) < threshold; }, | ||||
JacquesLucke: Here it would be ok to use `[&]` because the lambda is only evaluated in the called function. | |||||
| return *cell_value.value_int == value; | prev_mask, | ||||
| }, | new_indices); | ||||
| rows_included); | |||||
| break; | break; | ||||
| } | } | ||||
| case SPREADSHEET_ROW_FILTER_GREATER: { | case SPREADSHEET_ROW_FILTER_GREATER: { | ||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<float>(), | ||||
| [value](const CellValue &cell_value) -> bool { | [value](const float cell) { return cell > value; }, | ||||
| return *cell_value.value_int > value; | prev_mask, | ||||
| }, | new_indices); | ||||
| rows_included); | |||||
| break; | break; | ||||
| } | } | ||||
| case SPREADSHEET_ROW_FILTER_LESS: { | case SPREADSHEET_ROW_FILTER_LESS: { | ||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<float>(), | ||||
| [value](const CellValue &cell_value) -> bool { | [value](const float cell) { return cell < value; }, | ||||
| return *cell_value.value_int < value; | prev_mask, | ||||
| }, | new_indices); | ||||
| rows_included); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| } | } | ||||
| case SPREADSHEET_VALUE_TYPE_FLOAT: { | else if (column_data.type().is<int>()) { | ||||
| const float value = row_filter.value_float; | const int value = row_filter.value_int; | ||||
| switch (row_filter.operation) { | switch (row_filter.operation) { | ||||
| case SPREADSHEET_ROW_FILTER_EQUAL: { | case SPREADSHEET_ROW_FILTER_EQUAL: { | ||||
| const float threshold = row_filter.threshold; | |||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<int>(), | ||||
| [value, threshold](const CellValue &cell_value) -> bool { | [value](const int cell) { return cell == value; }, | ||||
| return std::abs(*cell_value.value_float - value) < threshold; | prev_mask, | ||||
| }, | new_indices); | ||||
| rows_included); | |||||
| break; | break; | ||||
| } | } | ||||
| case SPREADSHEET_ROW_FILTER_GREATER: { | case SPREADSHEET_ROW_FILTER_GREATER: { | ||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<int>(), | ||||
| [value](const CellValue &cell_value) -> bool { | [value](const int cell) { return cell > value; }, | ||||
| return *cell_value.value_float > value; | prev_mask, | ||||
| }, | new_indices); | ||||
| rows_included); | |||||
| break; | break; | ||||
| } | } | ||||
| case SPREADSHEET_ROW_FILTER_LESS: { | case SPREADSHEET_ROW_FILTER_LESS: { | ||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<int>(), | ||||
| [value](const CellValue &cell_value) -> bool { | [value](const int cell) { return cell < value; }, | ||||
| return *cell_value.value_float < value; | prev_mask, | ||||
| }, | new_indices); | ||||
| rows_included); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| } | } | ||||
| case SPREADSHEET_VALUE_TYPE_FLOAT2: { | else if (column_data.type().is<float2>()) { | ||||
| const float2 value = row_filter.value_float2; | const float2 value = row_filter.value_float2; | ||||
| switch (row_filter.operation) { | switch (row_filter.operation) { | ||||
| case SPREADSHEET_ROW_FILTER_EQUAL: { | case SPREADSHEET_ROW_FILTER_EQUAL: { | ||||
| const float threshold_squared = row_filter.threshold * row_filter.threshold; | const float threshold_sq = row_filter.threshold; | ||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<float2>(), | ||||
| [value, threshold_squared](const CellValue &cell_value) -> bool { | [value, threshold_sq](const float2 cell) { | ||||
| return float2::distance_squared(*cell_value.value_float2, value) < | return float2::distance_squared(cell, value) > threshold_sq; | ||||
| threshold_squared; | |||||
| }, | }, | ||||
| rows_included); | prev_mask, | ||||
| new_indices); | |||||
| break; | break; | ||||
| } | } | ||||
| case SPREADSHEET_ROW_FILTER_GREATER: { | case SPREADSHEET_ROW_FILTER_GREATER: { | ||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<float2>(), | ||||
| [value](const CellValue &cell_value) -> bool { | [value](const float2 cell) { return cell.x > value.x && cell.y > value.y; }, | ||||
| return cell_value.value_float2->x > value.x && | prev_mask, | ||||
| cell_value.value_float2->y > value.y; | new_indices); | ||||
| }, | |||||
| rows_included); | |||||
| break; | break; | ||||
| } | } | ||||
| case SPREADSHEET_ROW_FILTER_LESS: { | case SPREADSHEET_ROW_FILTER_LESS: { | ||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<float2>(), | ||||
| [value](const CellValue &cell_value) -> bool { | [value](const float2 cell) { return cell.x < value.x && cell.y < value.y; }, | ||||
| return cell_value.value_float2->x < value.x && | prev_mask, | ||||
| cell_value.value_float2->y < value.y; | new_indices); | ||||
| }, | |||||
| rows_included); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| } | } | ||||
| case SPREADSHEET_VALUE_TYPE_FLOAT3: { | else if (column_data.type().is<float3>()) { | ||||
| const float3 value = row_filter.value_float3; | const float3 value = row_filter.value_float3; | ||||
| switch (row_filter.operation) { | switch (row_filter.operation) { | ||||
| case SPREADSHEET_ROW_FILTER_EQUAL: { | case SPREADSHEET_ROW_FILTER_EQUAL: { | ||||
| const float threshold_squared = row_filter.threshold * row_filter.threshold; | const float threshold_sq = row_filter.threshold; | ||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<float3>(), | ||||
| [value, threshold_squared](const CellValue &cell_value) -> bool { | [value, threshold_sq](const float3 cell) { | ||||
| return float3::distance_squared(*cell_value.value_float3, value) < | return float3::distance_squared(cell, value) > threshold_sq; | ||||
| threshold_squared; | |||||
| }, | }, | ||||
| rows_included); | prev_mask, | ||||
| new_indices); | |||||
| break; | break; | ||||
| } | } | ||||
| case SPREADSHEET_ROW_FILTER_GREATER: { | case SPREADSHEET_ROW_FILTER_GREATER: { | ||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<float3>(), | ||||
| [value](const CellValue &cell_value) -> bool { | [value](const float3 cell) { | ||||
| return cell_value.value_float3->x > value.x && | return cell.x > value.x && cell.y > value.y && cell.z > value.z; | ||||
| cell_value.value_float3->y > value.y && | |||||
| cell_value.value_float3->z > value.z; | |||||
| }, | }, | ||||
| rows_included); | prev_mask, | ||||
| new_indices); | |||||
| break; | break; | ||||
| } | } | ||||
| case SPREADSHEET_ROW_FILTER_LESS: { | case SPREADSHEET_ROW_FILTER_LESS: { | ||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<float3>(), | ||||
| [value](const CellValue &cell_value) -> bool { | [value](const float3 cell) { | ||||
| return cell_value.value_float3->x < value.x && | return cell.x < value.x && cell.y < value.y && cell.z < value.z; | ||||
| cell_value.value_float3->y < value.y && | |||||
| cell_value.value_float3->z < value.z; | |||||
| }, | }, | ||||
| rows_included); | prev_mask, | ||||
| new_indices); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| } | } | ||||
| case SPREADSHEET_VALUE_TYPE_COLOR: { | else if (column_data.type().is<ColorGeometry4f>()) { | ||||
| const ColorGeometry4f value = row_filter.value_color; | const ColorGeometry4f value = row_filter.value_color; | ||||
| switch (row_filter.operation) { | switch (row_filter.operation) { | ||||
| case SPREADSHEET_ROW_FILTER_EQUAL: { | case SPREADSHEET_ROW_FILTER_EQUAL: { | ||||
| const float threshold_squared = row_filter.threshold * row_filter.threshold; | const float threshold_sq = row_filter.threshold; | ||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<ColorGeometry4f>(), | ||||
| [value, threshold_squared](const CellValue &cell_value) -> bool { | [value, threshold_sq](const ColorGeometry4f cell) { | ||||
| return len_squared_v4v4(value, *cell_value.value_color) < threshold_squared; | return len_squared_v4v4(cell, value) > threshold_sq; | ||||
| }, | }, | ||||
| rows_included); | prev_mask, | ||||
| new_indices); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| } | |||||
| case SPREADSHEET_VALUE_TYPE_BOOL: { | |||||
| const bool value = (row_filter.flag & SPREADSHEET_ROW_FILTER_BOOL_VALUE) != 0; | |||||
| apply_filter_operation( | |||||
| values, | |||||
| [value](const CellValue &cell_value) -> bool { | |||||
| return *cell_value.value_bool == value; | |||||
| }, | |||||
| rows_included); | |||||
| break; | |||||
| } | } | ||||
| case SPREADSHEET_VALUE_TYPE_INSTANCES: { | else if (column_data.type().is<InstanceReference>()) { | ||||
| const StringRef value = row_filter.value_string; | const StringRef value = row_filter.value_string; | ||||
| switch (row_filter.operation) { | |||||
| case SPREADSHEET_ROW_FILTER_EQUAL: { | |||||
| apply_filter_operation( | apply_filter_operation( | ||||
| values, | column_data.typed<InstanceReference>(), | ||||
| [value](const CellValue &cell_value) -> bool { | [value](const InstanceReference cell) { | ||||
| const ID *id = nullptr; | switch (cell.type()) { | ||||
| if (cell_value.value_object) { | case InstanceReference::Type::Object: { | ||||
| id = &cell_value.value_object->object->id; | return value == (reinterpret_cast<ID &>(cell.object()).name + 2); | ||||
JacquesLuckeUnsubmitted Done Inline ActionsCould just use .id instead of reinterpret_cast. JacquesLucke: Could just use `.id` instead of `reinterpret_cast`. | |||||
HooglyBooglyAuthorUnsubmitted Done Inline ActionsUnless there's a reason not to, I think I prefer doing it this way though, since I can avoid including DNA_object_types.h and DNA_collection_types.h HooglyBoogly: Unless there's a reason not to, I think I prefer doing it this way though, since I can avoid… | |||||
JacquesLuckeUnsubmitted Done Inline ActionsI don't see much value in not including these headers here tbh. JacquesLucke: I don't see much value in not including these headers here tbh. | |||||
HooglyBooglyAuthorUnsubmitted Done Inline ActionsOkay, this is using .id now HooglyBoogly: Okay, this is using `.id` now | |||||
| } | } | ||||
| else if (cell_value.value_collection) { | case InstanceReference::Type::Collection: { | ||||
| id = &cell_value.value_collection->collection->id; | return value == (reinterpret_cast<ID &>(cell.object()).name + 2); | ||||
| } | } | ||||
| if (id == nullptr) { | case InstanceReference::Type::GeometrySet: { | ||||
| return false; | return false; | ||||
| } | } | ||||
| case InstanceReference::Type::None: { | |||||
| return value == id->name + 2; | return false; | ||||
| }, | |||||
| rows_included); | |||||
| break; | |||||
| } | } | ||||
| default: | |||||
| break; | |||||
| } | } | ||||
| BLI_assert_unreachable(); | |||||
| /* Only one column should have this name. */ | return false; | ||||
| }, | |||||
| prev_mask, | |||||
| new_indices); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| static void index_vector_from_bools(Span<bool> selection, Vector<int64_t> &indices) | |||||
| { | |||||
| for (const int i : selection.index_range()) { | |||||
| if (selection[i]) { | |||||
| indices.append(i); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| static bool use_row_filters(const SpaceSpreadsheet &sspreadsheet) | static bool use_row_filters(const SpaceSpreadsheet &sspreadsheet) | ||||
| { | { | ||||
| if (!(sspreadsheet.filter_flag & SPREADSHEET_FILTER_ENABLE)) { | if (!(sspreadsheet.filter_flag & SPREADSHEET_FILTER_ENABLE)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| Show All 10 Lines | if (!(sspreadsheet.filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (!data_source.has_selection_filter()) { | if (!data_source.has_selection_filter()) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| Span<int64_t> spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet, | IndexMask spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet, | ||||
| const SpreadsheetLayout &spreadsheet_layout, | const SpreadsheetLayout &spreadsheet_layout, | ||||
| const DataSource &data_source, | const DataSource &data_source, | ||||
| ResourceScope &scope) | ResourceScope &scope) | ||||
| { | { | ||||
| const int tot_rows = data_source.tot_rows(); | const int tot_rows = data_source.tot_rows(); | ||||
| const bool use_selection = use_selection_filter(sspreadsheet, data_source); | const bool use_selection = use_selection_filter(sspreadsheet, data_source); | ||||
| const bool use_filters = use_row_filters(sspreadsheet); | const bool use_filters = use_row_filters(sspreadsheet); | ||||
| /* Avoid allocating an array if no row filtering is necessary. */ | /* Avoid allocating an array if no row filtering is necessary. */ | ||||
| if (!(use_filters || use_selection)) { | if (!(use_filters || use_selection)) { | ||||
| return IndexRange(tot_rows).as_span(); | return IndexMask(tot_rows); | ||||
| } | } | ||||
| Array<bool> rows_included(tot_rows, true); | IndexMask mask(tot_rows); | ||||
| Vector<int64_t> mask_indices; | |||||
| mask_indices.reserve(tot_rows); | |||||
| if (use_selection) { | |||||
| const GeometryDataSource *geometry_data_source = dynamic_cast<const GeometryDataSource *>( | |||||
| &data_source); | |||||
| mask = geometry_data_source->apply_selection_filter(mask_indices); | |||||
| } | |||||
| if (use_filters) { | if (use_filters) { | ||||
| Map<StringRef, const ColumnValues *> columns; | |||||
| for (const ColumnLayout &column : spreadsheet_layout.columns) { | |||||
| columns.add(column.values->name(), column.values); | |||||
| } | |||||
| LISTBASE_FOREACH (const SpreadsheetRowFilter *, row_filter, &sspreadsheet.row_filters) { | LISTBASE_FOREACH (const SpreadsheetRowFilter *, row_filter, &sspreadsheet.row_filters) { | ||||
| if (row_filter->flag & SPREADSHEET_ROW_FILTER_ENABLED) { | if (row_filter->flag & SPREADSHEET_ROW_FILTER_ENABLED) { | ||||
| apply_row_filter(spreadsheet_layout, *row_filter, rows_included); | if (!columns.contains(row_filter->column_name)) { | ||||
| continue; | |||||
| } | } | ||||
| Vector<int64_t> new_indices; | |||||
| new_indices.reserve(mask_indices.size()); | |||||
| apply_row_filter(*row_filter, columns, mask, new_indices); | |||||
| std::swap(new_indices, mask_indices); | |||||
| mask = IndexMask(mask_indices); | |||||
| } | } | ||||
| } | } | ||||
| if (use_selection) { | |||||
| const GeometryDataSource *geometry_data_source = dynamic_cast<const GeometryDataSource *>( | |||||
| &data_source); | |||||
| geometry_data_source->apply_selection_filter(rows_included); | |||||
| } | } | ||||
| Vector<int64_t> &indices = scope.construct<Vector<int64_t>>(); | if (mask_indices.is_empty()) { | ||||
| index_vector_from_bools(rows_included, indices); | BLI_assert(mask.is_empty() || mask.is_range()); | ||||
| return mask; | |||||
| } | |||||
| return indices; | return IndexMask(scope.add_value(std::move(mask_indices))); | ||||
| } | } | ||||
| SpreadsheetRowFilter *spreadsheet_row_filter_new() | SpreadsheetRowFilter *spreadsheet_row_filter_new() | ||||
| { | { | ||||
| SpreadsheetRowFilter *row_filter = (SpreadsheetRowFilter *)MEM_callocN( | SpreadsheetRowFilter *row_filter = (SpreadsheetRowFilter *)MEM_callocN( | ||||
| sizeof(SpreadsheetRowFilter), __func__); | sizeof(SpreadsheetRowFilter), __func__); | ||||
| row_filter->flag = (SPREADSHEET_ROW_FILTER_UI_EXPAND | SPREADSHEET_ROW_FILTER_ENABLED); | row_filter->flag = (SPREADSHEET_ROW_FILTER_UI_EXPAND | SPREADSHEET_ROW_FILTER_ENABLED); | ||||
| row_filter->operation = SPREADSHEET_ROW_FILTER_LESS; | row_filter->operation = SPREADSHEET_ROW_FILTER_LESS; | ||||
| Show All 24 Lines | |||||
Here it would be ok to use [&] because the lambda is only evaluated in the called function.