GPv3: Python API for frame, drawing and drawing attributes #124787

Merged
Falk David merged 22 commits from filedescriptor/blender:gpv3-drawing-python-api into main 2024-07-26 16:30:21 +02:00
32 changed files with 391 additions and 340 deletions
Showing only changes of commit 03ffd1240c - Show all commits

View File

@ -122,7 +122,6 @@ def register():
register_class(cls)
space_filebrowser.register_props()
temp_anim_layers.register_props()
from bpy.props import (
EnumProperty,

View File

@ -518,7 +518,7 @@ class MESH_UL_attributes(UIList):
sub.alignment = 'RIGHT'
sub.active = False
sub.label(
text="{:s} {:s}".format(iface_(domain_name), iface_(data_type.name)),
text="{:s} - {:s}".format(iface_(domain_name), iface_(data_type.name)),
translate=False,
)
@ -640,7 +640,7 @@ class MESH_UL_color_attributes(UIList, ColorAttributesListBase):
sub = split.row()
sub.alignment = 'RIGHT'
sub.active = False
sub.label(text="{:s} {:s}".format(iface_(domain_name), iface_(data_type.name)), translate=False)
sub.label(text="{:s} - {:s}".format(iface_(domain_name), iface_(data_type.name)), translate=False)
active_render = _index == data.color_attributes.render_color_index

View File

@ -245,6 +245,16 @@ class DOPESHEET_HT_editor_buttons:
layout.template_ID(st, "action", new="action.new", unlink="action.unlink")
# context.space_data.action comes from the active object.
adt = context.object and context.object.animation_data
if adt and st.action and st.action.is_action_layered:
layout.template_search(
adt, "action_slot",
adt, "action_slots",
new="",
unlink="anim.slot_unassign_object",
)
# Layer management
if st.mode == 'GPENCIL':
ob = context.active_object

View File

@ -7,8 +7,6 @@
It is not meant for any particular use, just to have *something* in the UI.
"""
import threading
import bpy
from bpy.types import (
Panel,
@ -32,97 +30,36 @@ class VIEW3D_PT_animation_layers(Panel):
layout.use_property_split = True
layout.use_property_decorate = False
# FIXME: this should be done in response to a message-bus callback, notifier, whatnot.
adt = context.object.animation_data
with _wm_selected_action_lock:
selected_action = getattr(adt, "action", None)
# Only set if it has to change, to avoid unnecessary notifies (that cause
# a redraw, that cause this code to be called, etc.)
if context.window_manager.selected_action != selected_action:
context.window_manager.selected_action = selected_action
col = layout.column()
# This has to go via an auxiliary property, as assigning an Animation
# data-block should be possible even when `context.object.animation_data`
# is `None`, and thus its `animation` property does not exist.
col.template_ID(context.window_manager, "selected_action")
col = layout.column(align=False)
adt = context.object.animation_data
anim = adt and adt.action
if anim:
slot_sub = col.column(align=True)
# Slot selector.
row = slot_sub.row(align=True)
row.prop(adt, "action_slot", text="Slot")
row.operator("anim.slot_unassign_object", text="", icon='X')
slot = anim.slots.get(adt.action_slot, None)
if slot:
slot_sub.prop(slot, "name_display", text="Name")
slot_sub.template_search(
adt, "action_slot",
adt, "action_slots",
new="",
unlink="anim.slot_unassign_object",
)
internal_sub = slot_sub.box().column(align=True)
internal_sub.active = False
internal_sub.active = False # Just to dim.
internal_sub.prop(adt, "action_slot_handle", text="handle")
if slot:
internal_sub.prop(slot, "name", text="Internal Name")
if adt.action_slot:
internal_sub.prop(adt.action_slot, "name", text="Internal Name")
if adt:
col.prop(adt, "action_slot_name", text="ADT Slot Name")
else:
col.label(text="ADT Slot Name: -")
layout.separator()
if not anim:
layout.label(text="No layers")
return
for layer_idx, layer in reversed(list(enumerate(anim.layers))):
layerbox = layout.box()
col = layerbox.column(align=True)
col.prop(layer, "name", text="Layer {:d}:".format(layer_idx + 1))
col.prop(layer, "influence")
col.prop(layer, "mix_mode")
classes = (
VIEW3D_PT_animation_layers,
)
_wm_selected_action_lock = threading.Lock()
def _wm_selected_action_update(wm, context):
# Avoid responding to changes written by the panel above.
lock_ok = _wm_selected_action_lock.acquire(blocking=False)
if not lock_ok:
return
try:
if wm.selected_action is None and context.object.animation_data is None:
return
adt = context.object.animation_data_create()
if adt.action == wm.selected_action:
# Avoid writing to the property when the new value hasn't changed.
return
adt.action = wm.selected_action
finally:
_wm_selected_action_lock.release()
def register_props():
# Due to this hackyness, the WindowManager will increase the user count of
# the pointed-to Action.
WindowManager.selected_action = PointerProperty(
type=bpy.types.Action,
name="Action",
description="Action assigned to the active Object",
update=_wm_selected_action_update,
)
if __name__ == "__main__": # only for live edit.
register_, _ = bpy.utils.register_classes_factory(classes)
register_()
register_props()

View File

@ -495,6 +495,9 @@ Layer *Action::get_layer_for_keyframing()
bool Action::assign_id(Slot *slot, ID &animated_id)
{
BLI_assert_msg(!slot || this->slots().as_span().contains(slot),
"Slot should be owned by this Action");
AnimData *adt = BKE_animdata_ensure_id(&animated_id);
if (!adt) {
return false;

View File

@ -115,7 +115,18 @@ static void calculate_point_handles(const HandleType type_left,
}
const float3 dir = next_diff / next_len + prev_diff / prev_len;
/* This magic number is unfortunate, but comes from elsewhere in Blender. */
/* The magic number 2.5614 is derived from approximating a circular arc at the control
* point. Given the constraints
* - P0=(0,1),P1=(c,1),P2=(1,c),P3=(1,0).
* - The first derivative of the curve must agree with the circular arc derivative at the
* endpoints.
* - Minimize the maximum radial drift.
* one can compute c 0.5519150244935105707435627. The distance from P0 to P3 is sqrt(2).
* The magic factor for `len` is (sqrt(2) / 0.5519150244935105707435627) 2.562375546255352.
* In older code of blender a slightly worse approximation of 2.5614 is used. It's kept
* for compatibility.
*
* See https://spencermortensen.com/articles/bezier-circle/. */
const float len = math::length(dir) * 2.5614f;
if (len != 0.0f) {
if (type_left == BEZIER_HANDLE_AUTO) {

View File

@ -2,6 +2,8 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_matrix.hh"
#include "COM_algorithm_realize_on_domain.hh"
#include "COM_context.hh"
#include "COM_domain.hh"
@ -29,11 +31,19 @@ RealizeOnDomainOperation::RealizeOnDomainOperation(Context &context,
void RealizeOnDomainOperation::execute()
{
const Domain &in_domain = get_input().domain();
/* Even and odd-sized domains have different pixel locations, which produces unexpected
* filtering. If one is odd and the other is even (detected by testing the low bit of the xor of
* the sizes), shift the input by 1/2 pixel so the pixels align. */
const float2 translation(((in_domain.size[0] ^ domain_.size[0]) & 1) ? -0.5f : 0.0f,
((in_domain.size[1] ^ domain_.size[1]) & 1) ? -0.5f : 0.0f);
realize_on_domain(context(),
get_input(),
get_result(),
domain_,
get_input().domain().transformation,
math::translate(in_domain.transformation, translation),
get_input().get_realization_options());
}

View File

@ -20,11 +20,10 @@ void main()
/* Since an input image with an identity transformation is supposed to be centered in the domain,
* we subtract the offset between the lower left corners of the input image and the domain, which
* is half the difference between their sizes, because the difference in size is on both sides of
* the centered image. Additionally, we floor the offset to retain the 0.5 offset added above in
* case the difference in sizes was odd. */
* the centered image. */
ivec2 domain_size = imageSize(domain_img);
ivec2 input_size = texture_size(input_tx);
vec2 offset = floor(vec2(domain_size - input_size) / 2.0);
vec2 offset = vec2(domain_size - input_size) / 2.0;
/* Subtract the offset and divide by the input image size to get the relevant coordinates into
* the sampler's expected [0, 1] range. */

View File

@ -5051,6 +5051,8 @@ void ANIM_channel_draw(
draw_sliders = (sipo->flag & SIPO_SLIDERS);
break;
}
default:
BLI_assert_unreachable();
}
}
@ -5869,6 +5871,8 @@ void ANIM_channel_draw_widgets(const bContext *C,
draw_sliders = (sipo->flag & SIPO_SLIDERS);
break;
}
default:
BLI_assert_unreachable();
}
}
@ -5980,11 +5984,13 @@ void ANIM_channel_draw_widgets(const bContext *C,
UI_block_emboss_set(block, UI_EMBOSS_NONE);
}
#ifdef WITH_ANIM_BAKLAVA
/* Slot ID type indicator. */
if (ale->type == ANIMTYPE_ACTION_SLOT) {
offset -= ICON_WIDTH;
UI_icon_draw(offset, ymid, acf_action_slot_idtype_icon(ale));
}
#endif /* WITH_ANIM_BAKLAVA */
}
/* Draw slider:

View File

@ -126,6 +126,7 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
{
/* get dopesheet */
ac->ads = &saction->ads;
ac->dopesheet_mode = eAnimEdit_Context(saction->mode);
/* sync settings with current view status, then return appropriate data */
switch (saction->mode) {
@ -143,7 +144,6 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
ac->datatype = ANIMCONT_ACTION;
ac->data = saction->action;
ac->mode = saction->mode;
return true;
case SACTCONT_SHAPEKEY: /* 'ShapeKey Editor' */
@ -161,8 +161,6 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
saction->action = nullptr;
}
}
ac->mode = saction->mode;
return true;
case SACTCONT_GPENCIL: /* Grease Pencil */ /* XXX review how this mode is handled... */
@ -171,8 +169,6 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
ac->datatype = ANIMCONT_GPENCIL;
ac->data = &saction->ads;
ac->mode = saction->mode;
return true;
case SACTCONT_CACHEFILE: /* Cache File */ /* XXX review how this mode is handled... */
@ -181,8 +177,6 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
ac->datatype = ANIMCONT_CHANNEL;
ac->data = &saction->ads;
ac->mode = saction->mode;
return true;
case SACTCONT_MASK: /* Mask */ /* XXX: review how this mode is handled. */
@ -199,8 +193,6 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
ac->datatype = ANIMCONT_MASK;
ac->data = &saction->ads;
ac->mode = saction->mode;
return true;
}
@ -210,8 +202,6 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
ac->datatype = ANIMCONT_DOPESHEET;
ac->data = &saction->ads;
ac->mode = saction->mode;
return true;
case SACTCONT_TIMELINE: /* Timeline */
@ -235,15 +225,11 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
ac->datatype = ANIMCONT_TIMELINE;
ac->data = &saction->ads;
ac->mode = saction->mode;
return true;
default: /* unhandled yet */
ac->datatype = ANIMCONT_NONE;
ac->data = nullptr;
ac->mode = -1;
return false;
}
}
@ -259,6 +245,7 @@ static bool graphedit_get_context(bAnimContext *ac, SpaceGraph *sipo)
sipo->ads->source = (ID *)ac->scene;
}
ac->ads = sipo->ads;
ac->grapheditor_mode = eGraphEdit_Mode(sipo->mode);
/* set settings for Graph Editor - "Selected = Editable" */
if (U.animation_flag & USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) {
@ -277,8 +264,6 @@ static bool graphedit_get_context(bAnimContext *ac, SpaceGraph *sipo)
ac->datatype = ANIMCONT_FCURVES;
ac->data = sipo->ads;
ac->mode = sipo->mode;
return true;
case SIPO_MODE_DRIVERS: /* Driver F-Curve Editor */
@ -288,15 +273,11 @@ static bool graphedit_get_context(bAnimContext *ac, SpaceGraph *sipo)
ac->datatype = ANIMCONT_DRIVERS;
ac->data = sipo->ads;
ac->mode = sipo->mode;
return true;
default: /* unhandled yet */
ac->datatype = ANIMCONT_NONE;
ac->data = nullptr;
ac->mode = -1;
return false;
}
}
@ -348,6 +329,24 @@ bool ANIM_animdata_context_getdata(bAnimContext *ac)
ok = nlaedit_get_context(ac, snla);
break;
}
case SPACE_EMPTY:
case SPACE_VIEW3D:
case SPACE_OUTLINER:
case SPACE_PROPERTIES:
case SPACE_FILE:
case SPACE_IMAGE:
case SPACE_INFO:
case SPACE_SEQ:
case SPACE_TEXT:
case SPACE_SCRIPT:
case SPACE_NODE:
case SPACE_CONSOLE:
case SPACE_USERPREF:
case SPACE_CLIP:
case SPACE_TOPBAR:
case SPACE_STATUSBAR:
case SPACE_SPREADSHEET:
break;
}
}
@ -382,8 +381,8 @@ bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
ac->area = area;
ac->region = region;
ac->sl = sl;
ac->spacetype = (area) ? area->spacetype : 0;
ac->regiontype = (region) ? region->regiontype : 0;
ac->spacetype = eSpace_Type((area) ? area->spacetype : 0);
ac->regiontype = eRegion_Type((region) ? region->regiontype : 0);
/* get data context info */
/* XXX: if the below fails, try to grab this info from context instead...
@ -1530,7 +1529,8 @@ static size_t animfilter_action_slot(bAnimContext *ac,
/* Add a list element for the Slot itself, but only if in Action mode. The Dopesheet mode
* shouldn't display Slots, as F-Curves are always shown in the context of the animated ID
* anyway. */
const bool is_action_mode = (ac->mode == SACTCONT_ACTION);
const bool is_action_mode = (ac->spacetype == SPACE_ACTION &&
ac->dopesheet_mode == SACTCONT_ACTION);
const bool show_fcurves_only = (filter_mode & ANIMFILTER_FCURVESONLY);
const bool include_summary_channels = (filter_mode & ANIMFILTER_LIST_CHANNELS);
const bool show_slot_channel = (is_action_mode && selection_ok_for_slot && !show_fcurves_only &&
@ -1643,7 +1643,8 @@ static size_t animfilter_action(bAnimContext *ac,
/* Only show all Slots in Action editor mode. Otherwise the F-Curves ought to be displayed
* underneath their animated ID anyway. */
const bool is_action_mode = (ac->mode == SACTCONT_ACTION);
const bool is_action_mode = (ac->spacetype == SPACE_ACTION &&
ac->dopesheet_mode == SACTCONT_ACTION);
const bool show_all_slots = (ac->ads->filterflag & ADS_FILTER_ALL_SLOTS);
if (is_action_mode && show_all_slots) {
return animfilter_action_slots(ac, anim_data, action, filter_mode, owner_id);

View File

@ -923,7 +923,8 @@ bool ED_geometry_attribute_convert(Mesh *mesh,
const GVArray varray = *attributes.lookup_or_default(name_copy, dst_domain, dst_type);
const CPPType &cpp_type = varray.type();
void *new_data = MEM_malloc_arrayN(varray.size(), cpp_type.size(), __func__);
void *new_data = MEM_mallocN_aligned(
varray.size() * cpp_type.size(), cpp_type.alignment(), __func__);
varray.materialize_to_uninitialized(new_data);
attributes.remove(name_copy);
if (!attributes.add(name_copy, dst_domain, dst_type, bke::AttributeInitMoveArray(new_data))) {

View File

@ -2462,6 +2462,9 @@ IndexRange clipboard_paste_strokes(Main &bmain,
const bool paste_back)
{
const bke::CurvesGeometry &clipboard_curves = ed::greasepencil::clipboard_curves();
if (clipboard_curves.curves_num() <= 0) {
return {};
}
/* Get a list of all materials in the scene. */
const Array<int> clipboard_material_remap = ed::greasepencil::clipboard_materials_remap(bmain,

View File

@ -11,6 +11,9 @@
#include "BLI_sys_types.h"
#include "BLI_utildefines.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
struct AnimData;
struct Depsgraph;
struct ID;
@ -54,6 +57,32 @@ struct PropertyRNA;
/** \name Context
* \{ */
/** Main Data container types. */
enum eAnimCont_Types {
/** Invalid or no data. */
ANIMCONT_NONE = 0,
/** Action (#bAction). */
ANIMCONT_ACTION = 1,
/** Shape-key (#Key). */
ANIMCONT_SHAPEKEY = 2,
/** Grease pencil (screen). */
ANIMCONT_GPENCIL = 3,
/** Dope-sheet (#bDopesheet). */
ANIMCONT_DOPESHEET = 4,
/** Animation F-Curves (#bDopesheet). */
ANIMCONT_FCURVES = 5,
/** Drivers (#bDopesheet). */
ANIMCONT_DRIVERS = 6,
/** NLA (#bDopesheet). */
ANIMCONT_NLA = 7,
/** Animation channel (#bAnimListElem). */
ANIMCONT_CHANNEL = 8,
/** Mask dope-sheet. */
ANIMCONT_MASK = 9,
/** "timeline" editor (#bDopeSheet). */
ANIMCONT_TIMELINE = 10,
};
/**
* This struct defines a structure used for animation-specific
* 'context' information.
@ -61,15 +90,17 @@ struct PropertyRNA;
struct bAnimContext {
/** data to be filtered for use in animation editor */
void *data;
/** type of data eAnimCont_Types */
short datatype;
/** Type of `data`. */
eAnimCont_Types datatype;
/** Editor mode, which depends on `spacetype` (below). */
eAnimEdit_Context dopesheet_mode;
eGraphEdit_Mode grapheditor_mode;
/** editor->mode */
short mode;
/** area->spacetype */
short spacetype;
eSpace_Type spacetype;
/** active region -> type (channels or main) */
short regiontype;
eRegion_Type regiontype;
/** editor host */
ScrArea *area;
@ -98,32 +129,6 @@ struct bAnimContext {
ReportList *reports;
};
/** Main Data container types. */
enum eAnimCont_Types {
/** Invalid or no data. */
ANIMCONT_NONE = 0,
/** Action (#bAction). */
ANIMCONT_ACTION = 1,
/** Shape-key (#Key). */
ANIMCONT_SHAPEKEY = 2,
/** Grease pencil (screen). */
ANIMCONT_GPENCIL = 3,
/** Dope-sheet (#bDopesheet). */
ANIMCONT_DOPESHEET = 4,
/** Animation F-Curves (#bDopesheet). */
ANIMCONT_FCURVES = 5,
/** Drivers (#bDopesheet). */
ANIMCONT_DRIVERS = 6,
/** NLA (#bDopesheet). */
ANIMCONT_NLA = 7,
/** Animation channel (#bAnimListElem). */
ANIMCONT_CHANNEL = 8,
/** Mask dope-sheet. */
ANIMCONT_MASK = 9,
/** "timeline" editor (#bDopeSheet). */
ANIMCONT_TIMELINE = 10,
};
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -32,6 +32,7 @@
#include "MEM_guardedalloc.h"
#include "RNA_access.hh"
#include "RNA_prototypes.hh"
#include "UI_interface.hh"
#include "UI_interface_icons.hh"
@ -457,9 +458,17 @@ void ui_rna_collection_search_update_fn(
has_sep_char = ID_IS_LINKED(id);
}
}
else {
name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
#ifdef WITH_ANIM_BAKLAVA
else if (itemptr.type == &RNA_ActionSlot) {
PropertyRNA *prop = RNA_struct_find_property(&itemptr, "name_display");
name = RNA_property_string_get_alloc(&itemptr, prop, name_buf, sizeof(name_buf), nullptr);
}
else {
#endif /* WITH_ANIM_BAKLAVA */
name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
#ifdef WITH_ANIM_BAKLAVA
}
#endif /* WITH_ANIM_BAKLAVA */
if (name) {
auto cis = std::make_unique<CollItemSearch>();

View File

@ -2075,7 +2075,18 @@ static void template_search_add_button_name(uiBlock *block,
return;
}
PropertyRNA *name_prop = RNA_struct_name_property(type);
PropertyRNA *name_prop;
#ifdef WITH_ANIM_BAKLAVA
if (type == &RNA_ActionSlot) {
name_prop = RNA_struct_find_property(active_ptr, "name_display");
}
else {
#endif /* WITH_ANIM_BAKLAVA */
name_prop = RNA_struct_name_property(type);
#ifdef WITH_ANIM_BAKLAVA
}
#endif /* WITH_ANIM_BAKLAVA */
const int width = template_search_textbut_width(active_ptr, name_prop);
const int height = template_search_textbut_height();
uiDefAutoButR(block, active_ptr, name_prop, 0, "", ICON_NONE, 0, 0, width, height);

View File

@ -1095,6 +1095,8 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
filter |= ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS;
check_selected = true;
break;
default:
BLI_assert_unreachable();
}
ANIM_animdata_filter(

View File

@ -21,6 +21,7 @@
#include "BLI_color.hh"
#include "BLI_index_mask.hh"
#include "BLI_kdopbvh.h"
#include "BLI_kdtree.h"
#include "BLI_math_matrix.hh"
#include "BLI_math_vector.hh"
#include "BLI_offset_indices.hh"
@ -527,7 +528,8 @@ struct GreasePencilFillOpData {
brush.gpencil_settings->fill_extend_mode);
const bool show_boundaries = brush.gpencil_settings->flag & GP_BRUSH_FILL_SHOW_HELPLINES;
const bool show_extension = brush.gpencil_settings->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES;
const float extension_length = brush.gpencil_settings->fill_extend_fac;
const float extension_length = brush.gpencil_settings->fill_extend_fac *
bke::greasepencil::LEGACY_RADIUS_CONVERSION_FACTOR;
const bool extension_cut = brush.gpencil_settings->flag & GP_BRUSH_FILL_STROKE_COLLIDE;
return {layer,
@ -703,6 +705,94 @@ static void grease_pencil_fill_extension_cut(const bContext &C,
extension_data.lines.ends = std::move(new_extension_ends);
}
/* Find closest point in each circle and generate extension lines between such pairs. */
static void grease_pencil_fill_extension_lines_from_circles(
const bContext &C,
ed::greasepencil::ExtensionData &extension_data,
Span<int> /*origin_drawings*/,
Span<int> /*origin_points*/)
{
const RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
const Scene &scene = *CTX_data_scene(&C);
const Object &object = *CTX_data_active_object(&C);
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
const float4x4 view_matrix = float4x4(rv3d.viewmat);
const Vector<ed::greasepencil::DrawingInfo> drawings =
ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
const IndexRange circles_range = extension_data.circles.centers.index_range();
/* TODO Include high-curvature feature points. */
const IndexRange feature_points_range = circles_range.after(0);
const IndexRange kd_points_range = IndexRange(circles_range.size() +
feature_points_range.size());
/* Upper bound for segment count. Arrays are sized for easy index mapping, exact count isn't
* necessary. Not all entries are added to the BVH tree. */
const int max_kd_entries = kd_points_range.size();
/* Cached view positions for lines. */
Array<float2> view_centers(max_kd_entries);
Array<float> view_radii(max_kd_entries);
KDTree_2d *kdtree = BLI_kdtree_2d_new(max_kd_entries);
/* Insert points for overlap tests. */
for (const int point_i : circles_range.index_range()) {
const float2 center =
math::transform_point(view_matrix, extension_data.circles.centers[point_i]).xy();
const float radius = math::average(math::to_scale(view_matrix)) *
extension_data.circles.radii[point_i];
const int kd_index = circles_range[point_i];
view_centers[kd_index] = center;
view_radii[kd_index] = radius;
BLI_kdtree_2d_insert(kdtree, kd_index, center);
}
for (const int i_point : feature_points_range.index_range()) {
/* TODO Insert feature points into the KDTree. */
UNUSED_VARS(i_point);
}
BLI_kdtree_2d_balance(kdtree);
struct {
Vector<float3> starts;
Vector<float3> ends;
} connection_lines;
for (const int point_i : circles_range.index_range()) {
const int kd_index = circles_range[point_i];
const float2 center = view_centers[kd_index];
const float radius = view_radii[kd_index];
BLI_kdtree_2d_range_search_cb_cpp(
kdtree,
center,
radius,
[&](const int other_point_i, const float * /*co*/, float /*dist_sq*/) {
if (other_point_i == kd_index) {
return true;
}
connection_lines.starts.append(extension_data.circles.centers[point_i]);
if (circles_range.contains(other_point_i)) {
connection_lines.ends.append(extension_data.circles.centers[other_point_i]);
}
else if (feature_points_range.contains(other_point_i)) {
/* TODO copy feature point to connection_lines (beware of start index!). */
connection_lines.ends.append(float3(0));
}
return true;
});
}
BLI_kdtree_2d_free(kdtree);
/* Add new extension lines. */
extension_data.lines.starts.extend(connection_lines.starts);
extension_data.lines.ends.extend(connection_lines.ends);
}
static ed::greasepencil::ExtensionData grease_pencil_fill_get_extension_data(
const bContext &C, const GreasePencilFillOpData &op_data)
{
@ -774,9 +864,17 @@ static ed::greasepencil::ExtensionData grease_pencil_fill_get_extension_data(
}
}
/* Intersection test against strokes and other extension lines. */
if (op_data.extension_cut) {
grease_pencil_fill_extension_cut(C, extension_data, origin_drawings, origin_points);
switch (op_data.extension_mode) {
case GP_FILL_EMODE_EXTEND:
/* Intersection test against strokes and other extension lines. */
if (op_data.extension_cut) {
grease_pencil_fill_extension_cut(C, extension_data, origin_drawings, origin_points);
}
break;
case GP_FILL_EMODE_RADIUS:
grease_pencil_fill_extension_lines_from_circles(
C, extension_data, origin_drawings, origin_points);
break;
}
return extension_data;
@ -1075,7 +1173,7 @@ static bool grease_pencil_apply_fill(bContext &C, wmOperator &op, const wmEvent
constexpr const ed::greasepencil::FillToolFitMethod fit_method =
ed::greasepencil::FillToolFitMethod::FitToView;
/* Debug setting: keep image data blocks for inspection. */
constexpr const bool keep_images = false;
constexpr const bool keep_images = true;
ARegion &region = *CTX_wm_region(&C);
/* Perform bounds check. */

View File

@ -1109,20 +1109,6 @@ bke::CurvesGeometry fill_strokes(const ViewContext &view_context,
line_colors,
line_width);
}
const IndexRange circles_range = extensions.circles.centers.index_range();
if (!circles_range.is_empty()) {
const VArray<ColorGeometry4f> circle_colors = VArray<ColorGeometry4f>::ForSingle(
draw_boundary_color, circles_range.size());
image_render::draw_circles(world_to_view,
circles_range,
extensions.circles.centers,
VArray<float>::ForSpan(extensions.circles.radii),
circle_colors,
float2(image_size),
1.0f,
true);
}
}
ed::greasepencil::image_render::clear_projection_matrix();

View File

@ -712,11 +712,12 @@ static void invert_visibility_mesh(Object &object, const Span<bke::pbvh::Node *>
bke::SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_span<bool>(
".hide_poly", bke::AttrDomain::Face);
undo::push_nodes(object, nodes, undo::Type::HideFace);
threading::EnumerableThreadSpecific<Vector<int>> all_index_data;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
Vector<int> &faces = all_index_data.local();
for (bke::pbvh::Node *node : nodes.slice(range)) {
undo::push_node(object, node, undo::Type::HideFace);
bke::pbvh::node_face_indices_calc_mesh(tri_faces, *node, faces);
for (const int face : faces) {
hide_poly.span[face] = !hide_poly.span[face];
@ -737,11 +738,12 @@ static void invert_visibility_grids(Depsgraph &depsgraph,
Mesh &mesh = *static_cast<Mesh *>(object.data);
bke::pbvh::Tree &pbvh = *object.sculpt->pbvh;
SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg;
BitGroupVector<> &grid_hidden = BKE_subdiv_ccg_grid_hidden_ensure(subdiv_ccg);
undo::push_nodes(object, nodes, undo::Type::HideVert);
BitGroupVector<> &grid_hidden = BKE_subdiv_ccg_grid_hidden_ensure(subdiv_ccg);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (bke::pbvh::Node *node : nodes.slice(range)) {
undo::push_node(object, node, undo::Type::HideVert);
for (const int i : bke::pbvh::node_grid_indices(*node)) {
bits::invert(grid_hidden[i]);
}
@ -756,9 +758,10 @@ static void invert_visibility_grids(Depsgraph &depsgraph,
static void invert_visibility_bmesh(Object &object, const Span<bke::pbvh::Node *> nodes)
{
undo::push_nodes(object, nodes, undo::Type::HideVert);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (bke::pbvh::Node *node : nodes.slice(range)) {
undo::push_node(object, node, undo::Type::HideVert);
bool fully_hidden = true;
for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(node)) {
BM_elem_flag_toggle(vert, BM_ELEM_HIDDEN);

View File

@ -555,13 +555,14 @@ static void invert_mask_grids(Main &bmain,
MultiresModifierData &mmd = *BKE_sculpt_multires_active(&scene, &object);
BKE_sculpt_mask_layers_ensure(&depsgraph, &bmain, &object, &mmd);
undo::push_nodes(object, nodes, undo::Type::Mask);
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
const Span<CCGElem *> grids = subdiv_ccg.grids;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (bke::pbvh::Node *node : nodes.slice(range)) {
undo::push_node(object, node, undo::Type::Mask);
const Span<int> grid_indices = bke::pbvh::node_grid_indices(*node);
if (grid_hidden.is_empty()) {
@ -597,7 +598,7 @@ static void invert_mask_bmesh(Object &object, const Span<bke::pbvh::Node *> node
return;
}
undo::push_node(object, nodes.first(), undo::Type::Mask);
undo::push_nodes(object, nodes, undo::Type::Mask);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (bke::pbvh::Node *node : nodes.slice(range)) {
for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(node)) {

View File

@ -1776,9 +1776,7 @@ static void vpaint_paint_leaves(bContext *C,
GMutableSpan attribute,
const Span<bke::pbvh::Node *> nodes)
{
for (bke::pbvh::Node *node : nodes) {
undo::push_node(ob, node, undo::Type::Color);
}
undo::push_nodes(ob, nodes, undo::Type::Color);
const Brush &brush = *ob.sculpt->cache->brush;
@ -2223,9 +2221,7 @@ static int vertex_color_set_exec(bContext *C, wmOperator *op)
* color attributes. */
BKE_pbvh_ensure_node_loops(*obact.sculpt->pbvh, mesh.corner_tris());
for (bke::pbvh::Node *node : nodes) {
undo::push_node(obact, node, undo::Type::Color);
}
undo::push_nodes(obact, nodes, undo::Type::Color);
fill_active_color(obact, paintcol, true, affect_alpha);

View File

@ -321,9 +321,7 @@ static void transform_active_color(bContext *C,
BKE_pbvh_ensure_node_loops(pbvh, mesh.corner_tris());
Vector<bke::pbvh::Node *> nodes = bke::pbvh::search_gather(pbvh, {});
for (bke::pbvh::Node *node : nodes) {
undo::push_node(obact, node, undo::Type::Color);
}
undo::push_nodes(obact, nodes, undo::Type::Color);
transform_active_color_data(*BKE_mesh_from_object(&obact), transform_fn);

View File

@ -2205,23 +2205,16 @@ static void undo_push(Object &ob, Cache *expand_cache)
switch (expand_cache->target) {
case SCULPT_EXPAND_TARGET_MASK:
for (bke::pbvh::Node *node : nodes) {
undo::push_node(ob, node, undo::Type::Mask);
}
undo::push_nodes(ob, nodes, undo::Type::Mask);
break;
case SCULPT_EXPAND_TARGET_FACE_SETS:
for (bke::pbvh::Node *node : nodes) {
undo::push_node(ob, node, undo::Type::FaceSet);
}
undo::push_nodes(ob, nodes, undo::Type::FaceSet);
break;
case SCULPT_EXPAND_TARGET_COLORS: {
const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
/* The sculpt undo system needs corner indices for corner domain color attributes. */
BKE_pbvh_ensure_node_loops(*ss.pbvh, mesh.corner_tris());
for (bke::pbvh::Node *node : nodes) {
undo::push_node(ob, node, undo::Type::Color);
}
undo::push_nodes(ob, nodes, undo::Type::Color);
break;
}
}

View File

@ -1262,9 +1262,9 @@ static void edit_modify_coordinates(
const float strength = RNA_float_get(op->ptr, "strength");
undo::push_begin(ob, op);
undo::push_nodes(ob, nodes, undo::Type::Position);
for (bke::pbvh::Node *node : nodes) {
BKE_pbvh_node_mark_positions_update(node);
undo::push_node(ob, node, undo::Type::Position);
}
switch (mode) {
case EditMode::FairPositions:

View File

@ -169,10 +169,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
Vector<bke::pbvh::Node *> nodes = bke::pbvh::search_gather(pbvh, {});
undo::push_begin(ob, op);
for (bke::pbvh::Node *node : nodes) {
undo::push_node(ob, node, undo::Type::Mask);
}
undo::push_nodes(ob, nodes, undo::Type::Mask);
Array<float> prev_mask;
int iterations = RNA_int_get(op->ptr, "iterations");

View File

@ -53,11 +53,9 @@ void write_mask_mesh(Object &object,
Mesh &mesh = *static_cast<Mesh *>(object.data);
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
undo::push_node(object, nodes[i], undo::Type::Mask);
}
});
undo::push_nodes(object, nodes, undo::Type::Mask);
bke::SpanAttributeWriter mask = attributes.lookup_or_add_for_write_span<float>(
".sculpt_mask", bke::AttrDomain::Point);
if (!mask) {
@ -90,9 +88,10 @@ static void init_mask_grids(Main &bmain,
const Span<CCGElem *> grids = subdiv_ccg.grids;
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
undo::push_nodes(object, nodes, undo::Type::Mask);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
undo::push_node(object, nodes[i], undo::Type::Mask);
for (const int grid : bke::pbvh::node_grid_indices(*nodes[i])) {
write_fn(grid_hidden, grid, grids[grid]);
}

View File

@ -55,8 +55,6 @@ static void apply_projection_mesh(const Sculpt &sd,
{
Mesh &mesh = *static_cast<Mesh *>(object.data);
undo::push_node(object, &node, undo::Type::Position);
const Span<int> verts = bke::pbvh::node_unique_verts(node);
const MutableSpan positions = gather_data_mesh(positions_eval, verts, tls.positions);
const MutableSpan normals = gather_data_mesh(vert_normals, verts, tls.normals);
@ -82,7 +80,6 @@ static void apply_projection_grids(const Sculpt &sd,
LocalData &tls)
{
SculptSession &ss = *object.sculpt;
undo::push_node(object, &node, undo::Type::Position);
SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
@ -116,8 +113,6 @@ static void apply_projection_bmesh(const Sculpt &sd,
{
const SculptSession &ss = *object.sculpt;
undo::push_node(object, &node, undo::Type::Position);
const Set<BMVert *, 0> &verts = BKE_pbvh_bmesh_node_unique_verts(&node);
const MutableSpan positions = gather_bmesh_positions(verts, tls.positions);
@ -157,6 +152,7 @@ static void gesture_apply_for_symmetry_pass(bContext &C, gesture::GestureData &g
const Span<float3> positions_eval = BKE_pbvh_get_vert_positions(pbvh);
const Span<float3> vert_normals = BKE_pbvh_get_vert_normals(pbvh);
MutableSpan<float3> positions_orig = mesh.vert_positions_for_write();
undo::push_nodes(object, nodes, undo::Type::Position);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
LocalData &tls = all_tls.local();
for (const int i : range) {
@ -174,6 +170,7 @@ static void gesture_apply_for_symmetry_pass(bContext &C, gesture::GestureData &g
break;
}
case bke::pbvh::Type::BMesh: {
undo::push_nodes(object, nodes, undo::Type::Position);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
LocalData &tls = all_tls.local();
for (const int i : range) {
@ -184,6 +181,7 @@ static void gesture_apply_for_symmetry_pass(bContext &C, gesture::GestureData &g
break;
}
case bke::pbvh::Type::Grids: {
undo::push_nodes(object, nodes, undo::Type::Position);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
LocalData &tls = all_tls.local();
for (const int i : range) {

View File

@ -983,8 +983,8 @@ static void recalcData_actedit(TransInfo *t)
ac.area = t->area;
ac.region = t->region;
ac.sl = static_cast<SpaceLink *>((t->area) ? t->area->spacedata.first : nullptr);
ac.spacetype = (t->area) ? t->area->spacetype : 0;
ac.regiontype = (t->region) ? t->region->regiontype : 0;
ac.spacetype = eSpace_Type((t->area) ? t->area->spacetype : 0);
ac.regiontype = eRegion_Type((t->region) ? t->region->regiontype : 0);
ANIM_animdata_context_getdata(&ac);