Refactor: Simplify spreadsheet handling of cell values
Previously we used a `CellValue` class to hold the data for a cell, and called a function to fill it whenever necessary. This is an unnecessary complication when we have virtual generic arrays and most data is already easily accessible that way anyway. This patch removes `CellValue` and uses `fn::GVArray` to provide access to data instead. In the future, if rows have different types within a single column, we can use a `GVArray` of `blender::Any` to interface with the drawing. Along with that, the use of virtual arrays made it easy to do a few other cleanups: - Use selection domain interpolations from rB5841f8656d95 for the mesh selection filter. - Change the row filter to only calculate for necessary indices. Differential Revision: https://developer.blender.org/D13478
This commit is contained in:
@@ -31,6 +31,8 @@
|
||||
|
||||
#include "attribute_access_intern.hh"
|
||||
|
||||
#include "FN_cpp_type_make.hh"
|
||||
|
||||
using blender::float4x4;
|
||||
using blender::Map;
|
||||
using blender::MutableSpan;
|
||||
@@ -39,6 +41,8 @@ using blender::Span;
|
||||
using blender::VectorSet;
|
||||
using blender::fn::GSpan;
|
||||
|
||||
MAKE_CPP_TYPE(InstanceReference, InstanceReference, CPPTypeFlags::None)
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Geometry Component Implementation
|
||||
* \{ */
|
||||
|
||||
@@ -49,7 +49,6 @@ set(SRC
|
||||
spreadsheet_row_filter_ui.cc
|
||||
|
||||
spreadsheet_cache.hh
|
||||
spreadsheet_cell_value.hh
|
||||
spreadsheet_column.hh
|
||||
spreadsheet_column_values.hh
|
||||
spreadsheet_context.hh
|
||||
|
||||
@@ -323,6 +323,8 @@ static float get_default_column_width(const ColumnValues &values)
|
||||
return 8.0f;
|
||||
case SPREADSHEET_VALUE_TYPE_STRING:
|
||||
return 5.0f;
|
||||
case SPREADSHEET_VALUE_TYPE_UNKNOWN:
|
||||
return 2.0f;
|
||||
}
|
||||
return float_width;
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_float2.hh"
|
||||
#include "BLI_float3.hh"
|
||||
|
||||
struct Collection;
|
||||
struct Object;
|
||||
|
||||
namespace blender::ed::spreadsheet {
|
||||
|
||||
struct ObjectCellValue {
|
||||
const Object *object;
|
||||
};
|
||||
|
||||
struct CollectionCellValue {
|
||||
const Collection *collection;
|
||||
};
|
||||
|
||||
struct GeometrySetCellValue {
|
||||
const GeometrySet *geometry_set;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a type that can hold the value of a cell in a spreadsheet. This type allows us to
|
||||
* decouple the drawing of individual cells from the code that generates the data to be displayed.
|
||||
*/
|
||||
class CellValue {
|
||||
public:
|
||||
/* The implementation just uses a bunch of `std::option` for now. Unfortunately, we cannot use
|
||||
* `std::variant` yet, due to missing compiler support. This type can really be optimized more,
|
||||
* but it does not really matter too much currently. */
|
||||
|
||||
std::optional<int> value_int;
|
||||
std::optional<float> value_float;
|
||||
std::optional<bool> value_bool;
|
||||
std::optional<float2> value_float2;
|
||||
std::optional<float3> value_float3;
|
||||
std::optional<ColorGeometry4f> value_color;
|
||||
std::optional<ObjectCellValue> value_object;
|
||||
std::optional<CollectionCellValue> value_collection;
|
||||
std::optional<GeometrySetCellValue> value_geometry_set;
|
||||
std::optional<std::string> value_string;
|
||||
};
|
||||
|
||||
} // namespace blender::ed::spreadsheet
|
||||
@@ -18,14 +18,52 @@
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_float2.hh"
|
||||
#include "BLI_float3.hh"
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "BKE_geometry_set.hh"
|
||||
|
||||
#include "FN_cpp_type.hh"
|
||||
|
||||
#include "spreadsheet_column.hh"
|
||||
#include "spreadsheet_column_values.hh"
|
||||
|
||||
namespace blender::ed::spreadsheet {
|
||||
|
||||
eSpreadsheetColumnValueType cpp_type_to_column_type(const fn::CPPType &type)
|
||||
{
|
||||
if (type.is<bool>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_BOOL;
|
||||
}
|
||||
if (type.is<int>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_INT32;
|
||||
}
|
||||
if (type.is<float>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_FLOAT;
|
||||
}
|
||||
if (type.is<float2>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_FLOAT2;
|
||||
}
|
||||
if (type.is<float3>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_FLOAT3;
|
||||
}
|
||||
if (type.is<ColorGeometry4f>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_COLOR;
|
||||
}
|
||||
if (type.is<std::string>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_STRING;
|
||||
}
|
||||
if (type.is<InstanceReference>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_INSTANCES;
|
||||
}
|
||||
|
||||
return SPREADSHEET_VALUE_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
SpreadsheetColumnID *spreadsheet_column_id_new()
|
||||
{
|
||||
SpreadsheetColumnID *column_id = (SpreadsheetColumnID *)MEM_callocN(sizeof(SpreadsheetColumnID),
|
||||
|
||||
@@ -20,33 +20,36 @@
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "spreadsheet_cell_value.hh"
|
||||
#include "FN_generic_virtual_array.hh"
|
||||
|
||||
namespace blender::ed::spreadsheet {
|
||||
|
||||
struct CellDrawParams;
|
||||
|
||||
eSpreadsheetColumnValueType cpp_type_to_column_type(const fn::CPPType &type);
|
||||
|
||||
/**
|
||||
* This represents a column in a spreadsheet. It has a name and provides a value for all the cells
|
||||
* in the column.
|
||||
*/
|
||||
class ColumnValues {
|
||||
class ColumnValues final {
|
||||
protected:
|
||||
eSpreadsheetColumnValueType type_;
|
||||
std::string name_;
|
||||
int size_;
|
||||
|
||||
fn::GVArray data_;
|
||||
|
||||
public:
|
||||
ColumnValues(const eSpreadsheetColumnValueType type, std::string name, const int size)
|
||||
: type_(type), name_(std::move(name)), size_(size)
|
||||
ColumnValues(std::string name, fn::GVArray data) : name_(std::move(name)), data_(std::move(data))
|
||||
{
|
||||
/* The array should not be empty. */
|
||||
BLI_assert(data_);
|
||||
}
|
||||
|
||||
virtual ~ColumnValues() = default;
|
||||
|
||||
virtual void get_value(int index, CellValue &r_cell_value) const = 0;
|
||||
|
||||
eSpreadsheetColumnValueType type() const
|
||||
{
|
||||
return type_;
|
||||
return cpp_type_to_column_type(data_.type());
|
||||
}
|
||||
|
||||
StringRefNull name() const
|
||||
@@ -56,45 +59,16 @@ class ColumnValues {
|
||||
|
||||
int size() const
|
||||
{
|
||||
return size_;
|
||||
return data_.size();
|
||||
}
|
||||
|
||||
const fn::GVArray &data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/* The default width of newly created columns, in UI units. */
|
||||
float default_width = 0.0f;
|
||||
};
|
||||
|
||||
/* Utility class for the function below. */
|
||||
template<typename GetValueF> class LambdaColumnValues : public ColumnValues {
|
||||
private:
|
||||
GetValueF get_value_;
|
||||
|
||||
public:
|
||||
LambdaColumnValues(const eSpreadsheetColumnValueType type,
|
||||
std::string name,
|
||||
int size,
|
||||
GetValueF get_value)
|
||||
: ColumnValues(type, std::move(name), size), get_value_(std::move(get_value))
|
||||
{
|
||||
}
|
||||
|
||||
void get_value(int index, CellValue &r_cell_value) const final
|
||||
{
|
||||
get_value_(index, r_cell_value);
|
||||
}
|
||||
};
|
||||
|
||||
/* Utility function that simplifies creating a spreadsheet column from a lambda function. */
|
||||
template<typename GetValueF>
|
||||
std::unique_ptr<ColumnValues> column_values_from_function(const eSpreadsheetColumnValueType type,
|
||||
std::string name,
|
||||
const int size,
|
||||
GetValueF get_value,
|
||||
const float default_width = 0.0f)
|
||||
{
|
||||
std::unique_ptr<ColumnValues> column_values = std::make_unique<LambdaColumnValues<GetValueF>>(
|
||||
type, std::move(name), size, std::move(get_value));
|
||||
column_values->default_width = default_width;
|
||||
return column_values;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::spreadsheet
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_editmesh.h"
|
||||
#include "BKE_lib_id.h"
|
||||
@@ -51,30 +53,6 @@ using blender::fn::GField;
|
||||
|
||||
namespace blender::ed::spreadsheet {
|
||||
|
||||
static std::optional<eSpreadsheetColumnValueType> cpp_type_to_column_value_type(
|
||||
const fn::CPPType &type)
|
||||
{
|
||||
if (type.is<bool>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_BOOL;
|
||||
}
|
||||
if (type.is<int>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_INT32;
|
||||
}
|
||||
if (type.is<float>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_FLOAT;
|
||||
}
|
||||
if (type.is<float2>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_FLOAT2;
|
||||
}
|
||||
if (type.is<float3>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_FLOAT3;
|
||||
}
|
||||
if (type.is<ColorGeometry4f>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_COLOR;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ExtraColumns::foreach_default_column_ids(
|
||||
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
|
||||
{
|
||||
@@ -92,39 +70,7 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values(
|
||||
if (values == nullptr) {
|
||||
return {};
|
||||
}
|
||||
eSpreadsheetColumnValueType column_type = *cpp_type_to_column_value_type(values->type());
|
||||
return column_values_from_function(column_type,
|
||||
column_id.name,
|
||||
values->size(),
|
||||
[column_type, values](int index, CellValue &r_cell_value) {
|
||||
const void *value = (*values)[index];
|
||||
switch (column_type) {
|
||||
case SPREADSHEET_VALUE_TYPE_BOOL:
|
||||
r_cell_value.value_bool = *(const bool *)value;
|
||||
break;
|
||||
case SPREADSHEET_VALUE_TYPE_INT32:
|
||||
r_cell_value.value_int = *(const int *)value;
|
||||
break;
|
||||
case SPREADSHEET_VALUE_TYPE_FLOAT:
|
||||
r_cell_value.value_float = *(const float *)value;
|
||||
break;
|
||||
case SPREADSHEET_VALUE_TYPE_FLOAT2:
|
||||
r_cell_value.value_float2 = *(const float2 *)value;
|
||||
break;
|
||||
case SPREADSHEET_VALUE_TYPE_FLOAT3:
|
||||
r_cell_value.value_float3 = *(const float3 *)value;
|
||||
break;
|
||||
case SPREADSHEET_VALUE_TYPE_COLOR:
|
||||
r_cell_value.value_color = *(
|
||||
const ColorGeometry4f *)value;
|
||||
break;
|
||||
case SPREADSHEET_VALUE_TYPE_STRING:
|
||||
r_cell_value.value_string = *(const std::string *)value;
|
||||
break;
|
||||
case SPREADSHEET_VALUE_TYPE_INSTANCES:
|
||||
break;
|
||||
}
|
||||
});
|
||||
return std::make_unique<ColumnValues>(column_id.name, fn::GVArray::ForSpan(*values));
|
||||
}
|
||||
|
||||
void GeometryDataSource::foreach_default_column_ids(
|
||||
@@ -179,52 +125,25 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
||||
if (STREQ(column_id.name, "Name")) {
|
||||
Span<int> reference_handles = instances.instance_reference_handles();
|
||||
Span<InstanceReference> references = instances.references();
|
||||
std::unique_ptr<ColumnValues> values = column_values_from_function(
|
||||
SPREADSHEET_VALUE_TYPE_INSTANCES,
|
||||
"Name",
|
||||
domain_size,
|
||||
[reference_handles, references](int index, CellValue &r_cell_value) {
|
||||
const InstanceReference &reference = references[reference_handles[index]];
|
||||
switch (reference.type()) {
|
||||
case InstanceReference::Type::Object: {
|
||||
Object &object = reference.object();
|
||||
r_cell_value.value_object = ObjectCellValue{&object};
|
||||
break;
|
||||
}
|
||||
case InstanceReference::Type::Collection: {
|
||||
Collection &collection = reference.collection();
|
||||
r_cell_value.value_collection = CollectionCellValue{&collection};
|
||||
break;
|
||||
}
|
||||
case InstanceReference::Type::GeometrySet: {
|
||||
const GeometrySet &geometry_set = reference.geometry_set();
|
||||
r_cell_value.value_geometry_set = GeometrySetCellValue{&geometry_set};
|
||||
break;
|
||||
}
|
||||
case InstanceReference::Type::None: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return values;
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name,
|
||||
VArray<InstanceReference>::ForFunc(domain_size,
|
||||
[reference_handles, references](int64_t index) {
|
||||
return references[reference_handles[index]];
|
||||
}));
|
||||
}
|
||||
Span<float4x4> transforms = instances.instance_transforms();
|
||||
if (STREQ(column_id.name, "Rotation")) {
|
||||
return column_values_from_function(
|
||||
SPREADSHEET_VALUE_TYPE_FLOAT3,
|
||||
column_id.name,
|
||||
domain_size,
|
||||
[transforms](int index, CellValue &r_cell_value) {
|
||||
r_cell_value.value_float3 = transforms[index].to_euler();
|
||||
});
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) {
|
||||
return transforms[index].to_euler();
|
||||
}));
|
||||
}
|
||||
if (STREQ(column_id.name, "Scale")) {
|
||||
return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3,
|
||||
column_id.name,
|
||||
domain_size,
|
||||
[transforms](int index, CellValue &r_cell_value) {
|
||||
r_cell_value.value_float3 = transforms[index].scale();
|
||||
});
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) {
|
||||
return transforms[index].scale();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,71 +156,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
||||
return {};
|
||||
}
|
||||
|
||||
const CustomDataType type = bke::cpp_type_to_custom_data_type(varray.type());
|
||||
switch (type) {
|
||||
case CD_PROP_FLOAT:
|
||||
return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT,
|
||||
column_id.name,
|
||||
domain_size,
|
||||
[varray](int index, CellValue &r_cell_value) {
|
||||
float value;
|
||||
varray.get(index, &value);
|
||||
r_cell_value.value_float = value;
|
||||
});
|
||||
case CD_PROP_INT32:
|
||||
return column_values_from_function(
|
||||
SPREADSHEET_VALUE_TYPE_INT32,
|
||||
column_id.name,
|
||||
domain_size,
|
||||
[varray](int index, CellValue &r_cell_value) {
|
||||
int value;
|
||||
varray.get(index, &value);
|
||||
r_cell_value.value_int = value;
|
||||
},
|
||||
STREQ(column_id.name, "id") ? 5.5f : 0.0f);
|
||||
case CD_PROP_BOOL:
|
||||
return column_values_from_function(SPREADSHEET_VALUE_TYPE_BOOL,
|
||||
column_id.name,
|
||||
domain_size,
|
||||
[varray](int index, CellValue &r_cell_value) {
|
||||
bool value;
|
||||
varray.get(index, &value);
|
||||
r_cell_value.value_bool = value;
|
||||
});
|
||||
case CD_PROP_FLOAT2: {
|
||||
return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT2,
|
||||
column_id.name,
|
||||
domain_size,
|
||||
[varray](int index, CellValue &r_cell_value) {
|
||||
float2 value;
|
||||
varray.get(index, &value);
|
||||
r_cell_value.value_float2 = value;
|
||||
});
|
||||
}
|
||||
case CD_PROP_FLOAT3: {
|
||||
return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3,
|
||||
column_id.name,
|
||||
domain_size,
|
||||
[varray](int index, CellValue &r_cell_value) {
|
||||
float3 value;
|
||||
varray.get(index, &value);
|
||||
r_cell_value.value_float3 = value;
|
||||
});
|
||||
}
|
||||
case CD_PROP_COLOR: {
|
||||
return column_values_from_function(SPREADSHEET_VALUE_TYPE_COLOR,
|
||||
column_id.name,
|
||||
domain_size,
|
||||
[varray](int index, CellValue &r_cell_value) {
|
||||
ColorGeometry4f value;
|
||||
varray.get(index, &value);
|
||||
r_cell_value.value_color = value;
|
||||
});
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
return std::make_unique<ColumnValues>(column_id.name, std::move(varray));
|
||||
}
|
||||
|
||||
int GeometryDataSource::tot_rows() const
|
||||
@@ -309,90 +164,9 @@ int GeometryDataSource::tot_rows() const
|
||||
return component_->attribute_domain_size(domain_);
|
||||
}
|
||||
|
||||
using IsVertexSelectedFn = FunctionRef<bool(int vertex_index)>;
|
||||
|
||||
static void get_selected_vertex_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
MutableSpan<bool> selection)
|
||||
{
|
||||
for (const int i : IndexRange(mesh.totvert)) {
|
||||
if (!selection[i]) {
|
||||
continue;
|
||||
}
|
||||
if (!is_vertex_selected_fn(i)) {
|
||||
selection[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_corner_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
MutableSpan<bool> selection)
|
||||
{
|
||||
for (const int i : IndexRange(mesh.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[i];
|
||||
if (!selection[i]) {
|
||||
continue;
|
||||
}
|
||||
if (!is_vertex_selected_fn(loop.v)) {
|
||||
selection[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_face_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
MutableSpan<bool> selection)
|
||||
{
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
if (!selection[poly_index]) {
|
||||
continue;
|
||||
}
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
if (!is_vertex_selected_fn(loop.v)) {
|
||||
selection[poly_index] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_edge_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
MutableSpan<bool> selection)
|
||||
{
|
||||
for (const int i : IndexRange(mesh.totedge)) {
|
||||
if (!selection[i]) {
|
||||
continue;
|
||||
}
|
||||
const MEdge &edge = mesh.medge[i];
|
||||
if (!is_vertex_selected_fn(edge.v1) || !is_vertex_selected_fn(edge.v2)) {
|
||||
selection[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_indices_on_domain(const Mesh &mesh,
|
||||
const AttributeDomain domain,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
MutableSpan<bool> selection)
|
||||
{
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return get_selected_vertex_indices(mesh, is_vertex_selected_fn, selection);
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return get_selected_face_indices(mesh, is_vertex_selected_fn, selection);
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return get_selected_corner_indices(mesh, is_vertex_selected_fn, selection);
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return get_selected_edge_indices(mesh, is_vertex_selected_fn, selection);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only data sets corresponding to mesh objects in edit mode currently support selection filtering.
|
||||
*/
|
||||
bool GeometryDataSource::has_selection_filter() const
|
||||
{
|
||||
Object *object_orig = DEG_get_original_object(object_eval_);
|
||||
@@ -409,7 +183,18 @@ bool GeometryDataSource::has_selection_filter() const
|
||||
return true;
|
||||
}
|
||||
|
||||
void GeometryDataSource::apply_selection_filter(MutableSpan<bool> rows_included) const
|
||||
static IndexMask index_mask_from_bool_array(const VArray<bool> &selection,
|
||||
Vector<int64_t> &indices)
|
||||
{
|
||||
for (const int i : selection.index_range()) {
|
||||
if (selection[i]) {
|
||||
indices.append(i);
|
||||
}
|
||||
}
|
||||
return IndexMask(indices);
|
||||
}
|
||||
|
||||
IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) const
|
||||
{
|
||||
std::lock_guard lock{mutex_};
|
||||
|
||||
@@ -425,27 +210,38 @@ void GeometryDataSource::apply_selection_filter(MutableSpan<bool> rows_included)
|
||||
int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
|
||||
if (orig_indices != nullptr) {
|
||||
/* Use CD_ORIGINDEX layer if it exists. */
|
||||
auto is_vertex_selected = [&](int vertex_index) -> bool {
|
||||
const int i_orig = orig_indices[vertex_index];
|
||||
if (i_orig < 0) {
|
||||
return false;
|
||||
}
|
||||
if (i_orig >= bm->totvert) {
|
||||
return false;
|
||||
}
|
||||
BMVert *vert = bm->vtable[i_orig];
|
||||
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
|
||||
};
|
||||
get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, rows_included);
|
||||
VArray<bool> selection = mesh_component->attribute_try_adapt_domain<bool>(
|
||||
VArray<bool>::ForFunc(mesh_eval->totvert,
|
||||
[bm, orig_indices](int vertex_index) -> bool {
|
||||
const int i_orig = orig_indices[vertex_index];
|
||||
if (i_orig < 0) {
|
||||
return false;
|
||||
}
|
||||
if (i_orig >= bm->totvert) {
|
||||
return false;
|
||||
}
|
||||
BMVert *vert = bm->vtable[i_orig];
|
||||
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
|
||||
}),
|
||||
ATTR_DOMAIN_POINT,
|
||||
domain_);
|
||||
return index_mask_from_bool_array(selection, indices);
|
||||
}
|
||||
else if (mesh_eval->totvert == bm->totvert) {
|
||||
|
||||
if (mesh_eval->totvert == bm->totvert) {
|
||||
/* Use a simple heuristic to match original vertices to evaluated ones. */
|
||||
auto is_vertex_selected = [&](int vertex_index) -> bool {
|
||||
BMVert *vert = bm->vtable[vertex_index];
|
||||
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
|
||||
};
|
||||
get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, rows_included);
|
||||
VArray<bool> selection = mesh_component->attribute_try_adapt_domain<bool>(
|
||||
VArray<bool>::ForFunc(mesh_eval->totvert,
|
||||
[bm](int vertex_index) -> bool {
|
||||
BMVert *vert = bm->vtable[vertex_index];
|
||||
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
|
||||
}),
|
||||
ATTR_DOMAIN_POINT,
|
||||
domain_);
|
||||
return index_mask_from_bool_array(selection, indices);
|
||||
}
|
||||
|
||||
return IndexMask(mesh_eval->totvert);
|
||||
}
|
||||
|
||||
void VolumeDataSource::foreach_default_column_ids(
|
||||
@@ -472,50 +268,36 @@ std::unique_ptr<ColumnValues> VolumeDataSource::get_column_values(
|
||||
#ifdef WITH_OPENVDB
|
||||
const int size = this->tot_rows();
|
||||
if (STREQ(column_id.name, "Grid Name")) {
|
||||
return column_values_from_function(
|
||||
SPREADSHEET_VALUE_TYPE_STRING,
|
||||
IFACE_("Grid Name"),
|
||||
size,
|
||||
[volume](int index, CellValue &r_cell_value) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
IFACE_("Grid Name"), VArray<std::string>::ForFunc(size, [volume](int64_t index) {
|
||||
const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index);
|
||||
r_cell_value.value_string = BKE_volume_grid_name(volume_grid);
|
||||
},
|
||||
6.0f);
|
||||
return BKE_volume_grid_name(volume_grid);
|
||||
}));
|
||||
}
|
||||
if (STREQ(column_id.name, "Data Type")) {
|
||||
return column_values_from_function(
|
||||
SPREADSHEET_VALUE_TYPE_STRING,
|
||||
IFACE_("Type"),
|
||||
size,
|
||||
[volume](int index, CellValue &r_cell_value) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
IFACE_("Data Type"), VArray<std::string>::ForFunc(size, [volume](int64_t index) {
|
||||
const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index);
|
||||
const VolumeGridType type = BKE_volume_grid_type(volume_grid);
|
||||
const char *name = nullptr;
|
||||
RNA_enum_name_from_value(rna_enum_volume_grid_data_type_items, type, &name);
|
||||
r_cell_value.value_string = IFACE_(name);
|
||||
},
|
||||
5.0f);
|
||||
return IFACE_(name);
|
||||
}));
|
||||
}
|
||||
if (STREQ(column_id.name, "Class")) {
|
||||
return column_values_from_function(
|
||||
SPREADSHEET_VALUE_TYPE_STRING,
|
||||
IFACE_("Class"),
|
||||
size,
|
||||
[volume](int index, CellValue &r_cell_value) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
IFACE_("Class"), VArray<std::string>::ForFunc(size, [volume](int64_t index) {
|
||||
const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index);
|
||||
openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
|
||||
openvdb::GridClass grid_class = grid->getGridClass();
|
||||
if (grid_class == openvdb::GridClass::GRID_FOG_VOLUME) {
|
||||
r_cell_value.value_string = IFACE_("Fog Volume");
|
||||
return IFACE_("Fog Volume");
|
||||
}
|
||||
else if (grid_class == openvdb::GridClass::GRID_LEVEL_SET) {
|
||||
r_cell_value.value_string = IFACE_("Level Set");
|
||||
if (grid_class == openvdb::GridClass::GRID_LEVEL_SET) {
|
||||
return IFACE_("Level Set");
|
||||
}
|
||||
else {
|
||||
r_cell_value.value_string = IFACE_("Unknown");
|
||||
}
|
||||
},
|
||||
5.0f);
|
||||
return IFACE_("Unknown");
|
||||
}));
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(column_id);
|
||||
|
||||
@@ -87,7 +87,7 @@ class GeometryDataSource : public DataSource {
|
||||
* filtering.
|
||||
*/
|
||||
bool has_selection_filter() const override;
|
||||
void apply_selection_filter(MutableSpan<bool> rows_included) const;
|
||||
IndexMask apply_selection_filter(Vector<int64_t> &indices) const;
|
||||
|
||||
void foreach_default_column_ids(
|
||||
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override;
|
||||
|
||||
@@ -17,8 +17,16 @@
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "BLI_float2.hh"
|
||||
#include "BLI_float3.hh"
|
||||
|
||||
#include "BKE_geometry_set.hh"
|
||||
|
||||
#include "spreadsheet_column_values.hh"
|
||||
#include "spreadsheet_layout.hh"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
@@ -92,13 +100,14 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
|
||||
{
|
||||
const int real_index = spreadsheet_layout_.row_indices[row_index];
|
||||
const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values;
|
||||
CellValue cell_value;
|
||||
if (real_index < column.size()) {
|
||||
column.get_value(real_index, cell_value);
|
||||
if (real_index > column.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell_value.value_int.has_value()) {
|
||||
const int value = *cell_value.value_int;
|
||||
const fn::GVArray &data = column.data();
|
||||
|
||||
if (data.type().is<int>()) {
|
||||
const int value = data.get<int>(real_index);
|
||||
const std::string value_str = std::to_string(value);
|
||||
uiBut *but = uiDefIconTextBut(params.block,
|
||||
UI_BTYPE_LABEL,
|
||||
@@ -119,8 +128,8 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
|
||||
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
||||
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
||||
}
|
||||
else if (cell_value.value_float.has_value()) {
|
||||
const float value = *cell_value.value_float;
|
||||
else if (data.type().is<float>()) {
|
||||
const float value = data.get<float>(real_index);
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(3) << value;
|
||||
const std::string value_str = ss.str();
|
||||
@@ -143,8 +152,8 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
|
||||
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
||||
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
||||
}
|
||||
else if (cell_value.value_bool.has_value()) {
|
||||
const bool value = *cell_value.value_bool;
|
||||
else if (data.type().is<bool>()) {
|
||||
const bool value = data.get<bool>(real_index);
|
||||
const int icon = value ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
|
||||
uiBut *but = uiDefIconTextBut(params.block,
|
||||
UI_BTYPE_LABEL,
|
||||
@@ -163,87 +172,81 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
|
||||
nullptr);
|
||||
UI_but_drawflag_disable(but, UI_BUT_ICON_LEFT);
|
||||
}
|
||||
else if (cell_value.value_float2.has_value()) {
|
||||
const float2 value = *cell_value.value_float2;
|
||||
else if (data.type().is<float2>()) {
|
||||
const float2 value = data.get<float2>(real_index);
|
||||
this->draw_float_vector(params, Span(&value.x, 2));
|
||||
}
|
||||
else if (cell_value.value_float3.has_value()) {
|
||||
const float3 value = *cell_value.value_float3;
|
||||
else if (data.type().is<float3>()) {
|
||||
const float3 value = data.get<float3>(real_index);
|
||||
this->draw_float_vector(params, Span(&value.x, 3));
|
||||
}
|
||||
else if (cell_value.value_color.has_value()) {
|
||||
const ColorGeometry4f value = *cell_value.value_color;
|
||||
else if (data.type().is<ColorGeometry4f>()) {
|
||||
const ColorGeometry4f value = data.get<ColorGeometry4f>(real_index);
|
||||
this->draw_float_vector(params, Span(&value.r, 4));
|
||||
}
|
||||
else if (cell_value.value_object.has_value()) {
|
||||
const ObjectCellValue value = *cell_value.value_object;
|
||||
uiDefIconTextBut(params.block,
|
||||
UI_BTYPE_LABEL,
|
||||
0,
|
||||
ICON_OBJECT_DATA,
|
||||
reinterpret_cast<const ID *const>(value.object)->name + 2,
|
||||
params.xmin,
|
||||
params.ymin,
|
||||
params.width,
|
||||
params.height,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nullptr);
|
||||
}
|
||||
else if (cell_value.value_collection.has_value()) {
|
||||
const CollectionCellValue value = *cell_value.value_collection;
|
||||
uiDefIconTextBut(params.block,
|
||||
UI_BTYPE_LABEL,
|
||||
0,
|
||||
ICON_OUTLINER_COLLECTION,
|
||||
reinterpret_cast<const ID *const>(value.collection)->name + 2,
|
||||
params.xmin,
|
||||
params.ymin,
|
||||
params.width,
|
||||
params.height,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nullptr);
|
||||
}
|
||||
else if (cell_value.value_geometry_set.has_value()) {
|
||||
uiDefIconTextBut(params.block,
|
||||
UI_BTYPE_LABEL,
|
||||
0,
|
||||
ICON_MESH_DATA,
|
||||
"Geometry",
|
||||
params.xmin,
|
||||
params.ymin,
|
||||
params.width,
|
||||
params.height,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nullptr);
|
||||
}
|
||||
else if (cell_value.value_string.has_value()) {
|
||||
uiDefIconTextBut(params.block,
|
||||
UI_BTYPE_LABEL,
|
||||
0,
|
||||
ICON_NONE,
|
||||
cell_value.value_string->c_str(),
|
||||
params.xmin,
|
||||
params.ymin,
|
||||
params.width,
|
||||
params.height,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nullptr);
|
||||
else if (data.type().is<InstanceReference>()) {
|
||||
const InstanceReference value = data.get<InstanceReference>(real_index);
|
||||
switch (value.type()) {
|
||||
case InstanceReference::Type::Object: {
|
||||
const Object &object = value.object();
|
||||
uiDefIconTextBut(params.block,
|
||||
UI_BTYPE_LABEL,
|
||||
0,
|
||||
ICON_OBJECT_DATA,
|
||||
object.id.name + 2,
|
||||
params.xmin,
|
||||
params.ymin,
|
||||
params.width,
|
||||
params.height,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nullptr);
|
||||
break;
|
||||
}
|
||||
case InstanceReference::Type::Collection: {
|
||||
Collection &collection = value.collection();
|
||||
uiDefIconTextBut(params.block,
|
||||
UI_BTYPE_LABEL,
|
||||
0,
|
||||
ICON_OUTLINER_COLLECTION,
|
||||
collection.id.name + 2,
|
||||
params.xmin,
|
||||
params.ymin,
|
||||
params.width,
|
||||
params.height,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nullptr);
|
||||
break;
|
||||
}
|
||||
case InstanceReference::Type::GeometrySet: {
|
||||
uiDefIconTextBut(params.block,
|
||||
UI_BTYPE_LABEL,
|
||||
0,
|
||||
ICON_MESH_DATA,
|
||||
"Geometry",
|
||||
params.xmin,
|
||||
params.ymin,
|
||||
params.width,
|
||||
params.height,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nullptr);
|
||||
break;
|
||||
}
|
||||
case InstanceReference::Type::None: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ struct ColumnLayout {
|
||||
/* Layout information for the entire spreadsheet. */
|
||||
struct SpreadsheetLayout {
|
||||
Vector<ColumnLayout> columns;
|
||||
Span<int64_t> row_indices;
|
||||
IndexMask row_indices;
|
||||
int index_column_width = 100;
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
@@ -38,238 +37,197 @@
|
||||
|
||||
namespace blender::ed::spreadsheet {
|
||||
|
||||
template<typename OperationFn>
|
||||
static void apply_filter_operation(const ColumnValues &values,
|
||||
template<typename T, typename OperationFn>
|
||||
static void apply_filter_operation(const VArray<T> &data,
|
||||
OperationFn check_fn,
|
||||
MutableSpan<bool> rows_included)
|
||||
const IndexMask mask,
|
||||
Vector<int64_t> &new_indices)
|
||||
{
|
||||
for (const int i : rows_included.index_range()) {
|
||||
if (!rows_included[i]) {
|
||||
continue;
|
||||
}
|
||||
CellValue cell_value;
|
||||
values.get_value(i, cell_value);
|
||||
if (!check_fn(cell_value)) {
|
||||
rows_included[i] = false;
|
||||
for (const int64_t i : mask) {
|
||||
if (check_fn(data[i])) {
|
||||
new_indices.append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_row_filter(const SpreadsheetLayout &spreadsheet_layout,
|
||||
const SpreadsheetRowFilter &row_filter,
|
||||
MutableSpan<bool> rows_included)
|
||||
static void apply_row_filter(const SpreadsheetRowFilter &row_filter,
|
||||
const Map<StringRef, const ColumnValues *> &columns,
|
||||
const IndexMask prev_mask,
|
||||
Vector<int64_t> &new_indices)
|
||||
{
|
||||
for (const ColumnLayout &column : spreadsheet_layout.columns) {
|
||||
const ColumnValues &values = *column.values;
|
||||
if (values.name() != row_filter.column_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (values.type()) {
|
||||
case SPREADSHEET_VALUE_TYPE_INT32: {
|
||||
const int value = row_filter.value_int;
|
||||
switch (row_filter.operation) {
|
||||
case SPREADSHEET_ROW_FILTER_EQUAL: {
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value](const CellValue &cell_value) -> bool {
|
||||
return *cell_value.value_int == value;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_GREATER: {
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value](const CellValue &cell_value) -> bool {
|
||||
return *cell_value.value_int > value;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_LESS: {
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value](const CellValue &cell_value) -> bool {
|
||||
return *cell_value.value_int < value;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_VALUE_TYPE_FLOAT: {
|
||||
const float value = row_filter.value_float;
|
||||
switch (row_filter.operation) {
|
||||
case SPREADSHEET_ROW_FILTER_EQUAL: {
|
||||
const float threshold = row_filter.threshold;
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value, threshold](const CellValue &cell_value) -> bool {
|
||||
return std::abs(*cell_value.value_float - value) < threshold;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_GREATER: {
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value](const CellValue &cell_value) -> bool {
|
||||
return *cell_value.value_float > value;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_LESS: {
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value](const CellValue &cell_value) -> bool {
|
||||
return *cell_value.value_float < value;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_VALUE_TYPE_FLOAT2: {
|
||||
const float2 value = row_filter.value_float2;
|
||||
switch (row_filter.operation) {
|
||||
case SPREADSHEET_ROW_FILTER_EQUAL: {
|
||||
const float threshold_squared = row_filter.threshold * row_filter.threshold;
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value, threshold_squared](const CellValue &cell_value) -> bool {
|
||||
return float2::distance_squared(*cell_value.value_float2, value) <
|
||||
threshold_squared;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_GREATER: {
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value](const CellValue &cell_value) -> bool {
|
||||
return cell_value.value_float2->x > value.x &&
|
||||
cell_value.value_float2->y > value.y;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_LESS: {
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value](const CellValue &cell_value) -> bool {
|
||||
return cell_value.value_float2->x < value.x &&
|
||||
cell_value.value_float2->y < value.y;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_VALUE_TYPE_FLOAT3: {
|
||||
const float3 value = row_filter.value_float3;
|
||||
switch (row_filter.operation) {
|
||||
case SPREADSHEET_ROW_FILTER_EQUAL: {
|
||||
const float threshold_squared = row_filter.threshold * row_filter.threshold;
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value, threshold_squared](const CellValue &cell_value) -> bool {
|
||||
return float3::distance_squared(*cell_value.value_float3, value) <
|
||||
threshold_squared;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_GREATER: {
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value](const CellValue &cell_value) -> bool {
|
||||
return cell_value.value_float3->x > value.x &&
|
||||
cell_value.value_float3->y > value.y &&
|
||||
cell_value.value_float3->z > value.z;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_LESS: {
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value](const CellValue &cell_value) -> bool {
|
||||
return cell_value.value_float3->x < value.x &&
|
||||
cell_value.value_float3->y < value.y &&
|
||||
cell_value.value_float3->z < value.z;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_VALUE_TYPE_COLOR: {
|
||||
const ColorGeometry4f value = row_filter.value_color;
|
||||
switch (row_filter.operation) {
|
||||
case SPREADSHEET_ROW_FILTER_EQUAL: {
|
||||
const float threshold_squared = row_filter.threshold * row_filter.threshold;
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value, threshold_squared](const CellValue &cell_value) -> bool {
|
||||
return len_squared_v4v4(value, *cell_value.value_color) < threshold_squared;
|
||||
},
|
||||
rows_included);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_VALUE_TYPE_BOOL: {
|
||||
const bool value = (row_filter.flag & SPREADSHEET_ROW_FILTER_BOOL_VALUE) != 0;
|
||||
const ColumnValues &column = *columns.lookup(row_filter.column_name);
|
||||
const fn::GVArray &column_data = column.data();
|
||||
if (column_data.type().is<float>()) {
|
||||
const float value = row_filter.value_float;
|
||||
switch (row_filter.operation) {
|
||||
case SPREADSHEET_ROW_FILTER_EQUAL: {
|
||||
const float threshold = row_filter.threshold;
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value](const CellValue &cell_value) -> bool {
|
||||
return *cell_value.value_bool == value;
|
||||
},
|
||||
rows_included);
|
||||
column_data.typed<float>(),
|
||||
[&](const float cell) { return std::abs(cell - value) < threshold; },
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_VALUE_TYPE_INSTANCES: {
|
||||
const StringRef value = row_filter.value_string;
|
||||
case SPREADSHEET_ROW_FILTER_GREATER: {
|
||||
apply_filter_operation(
|
||||
values,
|
||||
[value](const CellValue &cell_value) -> bool {
|
||||
const ID *id = nullptr;
|
||||
if (cell_value.value_object) {
|
||||
id = &cell_value.value_object->object->id;
|
||||
}
|
||||
else if (cell_value.value_collection) {
|
||||
id = &cell_value.value_collection->collection->id;
|
||||
}
|
||||
if (id == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return value == id->name + 2;
|
||||
},
|
||||
rows_included);
|
||||
column_data.typed<float>(),
|
||||
[&](const float cell) { return cell > value; },
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case SPREADSHEET_ROW_FILTER_LESS: {
|
||||
apply_filter_operation(
|
||||
column_data.typed<float>(),
|
||||
[&](const float cell) { return cell < value; },
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only one column should have this name. */
|
||||
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);
|
||||
else if (column_data.type().is<int>()) {
|
||||
const int value = row_filter.value_int;
|
||||
switch (row_filter.operation) {
|
||||
case SPREADSHEET_ROW_FILTER_EQUAL: {
|
||||
apply_filter_operation(
|
||||
column_data.typed<int>(),
|
||||
[&](const int cell) { return cell == value; },
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_GREATER: {
|
||||
apply_filter_operation(
|
||||
column_data.typed<int>(),
|
||||
[value](const int cell) { return cell > value; },
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_LESS: {
|
||||
apply_filter_operation(
|
||||
column_data.typed<int>(),
|
||||
[&](const int cell) { return cell < value; },
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (column_data.type().is<float2>()) {
|
||||
const float2 value = row_filter.value_float2;
|
||||
switch (row_filter.operation) {
|
||||
case SPREADSHEET_ROW_FILTER_EQUAL: {
|
||||
const float threshold_sq = row_filter.threshold;
|
||||
apply_filter_operation(
|
||||
column_data.typed<float2>(),
|
||||
[&](const float2 cell) {
|
||||
return float2::distance_squared(cell, value) > threshold_sq;
|
||||
},
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_GREATER: {
|
||||
apply_filter_operation(
|
||||
column_data.typed<float2>(),
|
||||
[&](const float2 cell) { return cell.x > value.x && cell.y > value.y; },
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_LESS: {
|
||||
apply_filter_operation(
|
||||
column_data.typed<float2>(),
|
||||
[&](const float2 cell) { return cell.x < value.x && cell.y < value.y; },
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (column_data.type().is<float3>()) {
|
||||
const float3 value = row_filter.value_float3;
|
||||
switch (row_filter.operation) {
|
||||
case SPREADSHEET_ROW_FILTER_EQUAL: {
|
||||
const float threshold_sq = row_filter.threshold;
|
||||
apply_filter_operation(
|
||||
column_data.typed<float3>(),
|
||||
[&](const float3 cell) {
|
||||
return float3::distance_squared(cell, value) > threshold_sq;
|
||||
},
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_GREATER: {
|
||||
apply_filter_operation(
|
||||
column_data.typed<float3>(),
|
||||
[&](const float3 cell) {
|
||||
return cell.x > value.x && cell.y > value.y && cell.z > value.z;
|
||||
},
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
case SPREADSHEET_ROW_FILTER_LESS: {
|
||||
apply_filter_operation(
|
||||
column_data.typed<float3>(),
|
||||
[&](const float3 cell) {
|
||||
return cell.x < value.x && cell.y < value.y && cell.z < value.z;
|
||||
},
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (column_data.type().is<ColorGeometry4f>()) {
|
||||
const ColorGeometry4f value = row_filter.value_color;
|
||||
switch (row_filter.operation) {
|
||||
case SPREADSHEET_ROW_FILTER_EQUAL: {
|
||||
const float threshold_sq = row_filter.threshold;
|
||||
apply_filter_operation(
|
||||
column_data.typed<ColorGeometry4f>(),
|
||||
[&](const ColorGeometry4f cell) {
|
||||
return len_squared_v4v4(cell, value) > threshold_sq;
|
||||
},
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (column_data.type().is<InstanceReference>()) {
|
||||
const StringRef value = row_filter.value_string;
|
||||
switch (row_filter.operation) {
|
||||
case SPREADSHEET_ROW_FILTER_EQUAL: {
|
||||
apply_filter_operation(
|
||||
column_data.typed<InstanceReference>(),
|
||||
[&](const InstanceReference cell) {
|
||||
switch (cell.type()) {
|
||||
case InstanceReference::Type::Object: {
|
||||
return value == (reinterpret_cast<ID &>(cell.object()).name + 2);
|
||||
}
|
||||
case InstanceReference::Type::Collection: {
|
||||
return value == (reinterpret_cast<ID &>(cell.collection()).name + 2);
|
||||
}
|
||||
case InstanceReference::Type::GeometrySet: {
|
||||
return false;
|
||||
}
|
||||
case InstanceReference::Type::None: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
},
|
||||
prev_mask,
|
||||
new_indices);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,10 +255,10 @@ static bool use_selection_filter(const SpaceSpreadsheet &sspreadsheet,
|
||||
return true;
|
||||
}
|
||||
|
||||
Span<int64_t> spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet,
|
||||
const SpreadsheetLayout &spreadsheet_layout,
|
||||
const DataSource &data_source,
|
||||
ResourceScope &scope)
|
||||
IndexMask spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet,
|
||||
const SpreadsheetLayout &spreadsheet_layout,
|
||||
const DataSource &data_source,
|
||||
ResourceScope &scope)
|
||||
{
|
||||
const int tot_rows = data_source.tot_rows();
|
||||
|
||||
@@ -309,29 +267,46 @@ Span<int64_t> spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet,
|
||||
|
||||
/* Avoid allocating an array if no row filtering is necessary. */
|
||||
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);
|
||||
|
||||
if (use_filters) {
|
||||
LISTBASE_FOREACH (const SpreadsheetRowFilter *, row_filter, &sspreadsheet.row_filters) {
|
||||
if (row_filter->flag & SPREADSHEET_ROW_FILTER_ENABLED) {
|
||||
apply_row_filter(spreadsheet_layout, *row_filter, rows_included);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vector<int64_t> mask_indices;
|
||||
mask_indices.reserve(tot_rows);
|
||||
|
||||
if (use_selection) {
|
||||
const GeometryDataSource *geometry_data_source = dynamic_cast<const GeometryDataSource *>(
|
||||
&data_source);
|
||||
geometry_data_source->apply_selection_filter(rows_included);
|
||||
mask = geometry_data_source->apply_selection_filter(mask_indices);
|
||||
}
|
||||
|
||||
Vector<int64_t> &indices = scope.construct<Vector<int64_t>>();
|
||||
index_vector_from_bools(rows_included, indices);
|
||||
if (use_filters) {
|
||||
Map<StringRef, const ColumnValues *> columns;
|
||||
for (const ColumnLayout &column : spreadsheet_layout.columns) {
|
||||
columns.add(column.values->name(), column.values);
|
||||
}
|
||||
|
||||
return indices;
|
||||
LISTBASE_FOREACH (const SpreadsheetRowFilter *, row_filter, &sspreadsheet.row_filters) {
|
||||
if (row_filter->flag & SPREADSHEET_ROW_FILTER_ENABLED) {
|
||||
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 (mask_indices.is_empty()) {
|
||||
BLI_assert(mask.is_empty() || mask.is_range());
|
||||
return mask;
|
||||
}
|
||||
|
||||
return IndexMask(scope.add_value(std::move(mask_indices)));
|
||||
}
|
||||
|
||||
SpreadsheetRowFilter *spreadsheet_row_filter_new()
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
|
||||
namespace blender::ed::spreadsheet {
|
||||
|
||||
Span<int64_t> spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet,
|
||||
const SpreadsheetLayout &spreadsheet_layout,
|
||||
const DataSource &data_source,
|
||||
ResourceScope &scope);
|
||||
IndexMask spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet,
|
||||
const SpreadsheetLayout &spreadsheet_layout,
|
||||
const DataSource &data_source,
|
||||
ResourceScope &scope);
|
||||
|
||||
SpreadsheetRowFilter *spreadsheet_row_filter_new();
|
||||
SpreadsheetRowFilter *spreadsheet_row_filter_copy(const SpreadsheetRowFilter *src_row_filter);
|
||||
|
||||
@@ -114,6 +114,8 @@ static std::string value_string(const SpreadsheetRowFilter &row_filter,
|
||||
}
|
||||
case SPREADSHEET_VALUE_TYPE_STRING:
|
||||
return row_filter.value_string;
|
||||
case SPREADSHEET_VALUE_TYPE_UNKNOWN:
|
||||
return "";
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return "";
|
||||
@@ -238,6 +240,10 @@ static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel)
|
||||
uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE);
|
||||
break;
|
||||
case SPREADSHEET_VALUE_TYPE_STRING:
|
||||
uiItemR(layout, filter_ptr, "value_string", 0, IFACE_("Value"), ICON_NONE);
|
||||
break;
|
||||
case SPREADSHEET_VALUE_TYPE_UNKNOWN:
|
||||
uiItemL(layout, IFACE_("Unkown column type"), ICON_ERROR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +174,11 @@ class GVArrayCommon {
|
||||
void get_internal_single_to_uninitialized(void *r_value) const;
|
||||
|
||||
void get(const int64_t index, void *r_value) const;
|
||||
/**
|
||||
* Returns a copy of the value at the given index. Usually a typed virtual array should
|
||||
* be used instead, but sometimes this is simpler when only a few indices are needed.
|
||||
*/
|
||||
template<typename T> T get(const int64_t index) const;
|
||||
void get_to_uninitialized(const int64_t index, void *r_value) const;
|
||||
};
|
||||
|
||||
@@ -691,6 +696,16 @@ inline void GVArrayCommon::get(const int64_t index, void *r_value) const
|
||||
impl_->get(index, r_value);
|
||||
}
|
||||
|
||||
template<typename T> inline T GVArrayCommon::get(const int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < this->size());
|
||||
BLI_assert(this->type().is<T>());
|
||||
T value{};
|
||||
impl_->get(index, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Same as `get`, but `r_value` is expected to point to uninitialized memory. */
|
||||
inline void GVArrayCommon::get_to_uninitialized(const int64_t index, void *r_value) const
|
||||
{
|
||||
|
||||
@@ -2006,6 +2006,7 @@ typedef enum eSpaceSpreadsheet_ContextType {
|
||||
} eSpaceSpreadsheet_ContextType;
|
||||
|
||||
typedef enum eSpreadsheetColumnValueType {
|
||||
SPREADSHEET_VALUE_TYPE_UNKNOWN = -1,
|
||||
SPREADSHEET_VALUE_TYPE_BOOL = 0,
|
||||
SPREADSHEET_VALUE_TYPE_INT32 = 1,
|
||||
SPREADSHEET_VALUE_TYPE_FLOAT = 2,
|
||||
|
||||
Reference in New Issue
Block a user