Nodes: Allow adding multiple search items per node type in add menu #104794

Merged
Hans Goudey merged 3 commits from HooglyBoogly/blender:node-add-search-items into main 2023-02-20 19:17:14 +01:00
145 changed files with 3123 additions and 1155 deletions
Showing only changes of commit e261c01922 - Show all commits

View File

@ -10,7 +10,7 @@ ExternalProject_Add(external_epoxy
URL_HASH ${EPOXY_HASH_TYPE}=${EPOXY_HASH}
PREFIX ${BUILD_DIR}/epoxy
PATCH_COMMAND ${PATCH_CMD} -p 1 -N -d ${BUILD_DIR}/epoxy/src/external_epoxy/ < ${PATCH_DIR}/epoxy.diff
CONFIGURE_COMMAND ${CONFIGURE_ENV} && ${MESON} setup --prefix ${LIBDIR}/epoxy --default-library ${EPOXY_LIB_TYPE} --libdir lib ${BUILD_DIR}/epoxy/src/external_epoxy-build ${BUILD_DIR}/epoxy/src/external_epoxy -Dtests=false
CONFIGURE_COMMAND ${CONFIGURE_ENV} && ${MESON} setup --prefix ${LIBDIR}/epoxy --default-library ${EPOXY_LIB_TYPE} --libdir lib ${BUILD_DIR}/epoxy/src/external_epoxy-build ${BUILD_DIR}/epoxy/src/external_epoxy -Dtests=false ${MESON_BUILD_TYPE}
BUILD_COMMAND ninja
INSTALL_COMMAND ninja install
)

View File

@ -9,7 +9,7 @@ ExternalProject_Add(external_fribidi
URL_HASH ${FRIBIDI_HASH_TYPE}=${FRIBIDI_HASH}
DOWNLOAD_DIR ${DOWNLOAD_DIR}
PREFIX ${BUILD_DIR}/fribidi
CONFIGURE_COMMAND ${MESON} setup --prefix ${LIBDIR}/fribidi -Ddocs=false --default-library static --libdir lib ${BUILD_DIR}/fribidi/src/external_fribidi-build ${BUILD_DIR}/fribidi/src/external_fribidi
CONFIGURE_COMMAND ${MESON} setup --prefix ${LIBDIR}/fribidi ${MESON_BUILD_TYPE} -Ddocs=false --default-library static --libdir lib ${BUILD_DIR}/fribidi/src/external_fribidi-build ${BUILD_DIR}/fribidi/src/external_fribidi
BUILD_COMMAND ninja
INSTALL_COMMAND ninja install
INSTALL_DIR ${LIBDIR}/fribidi

View File

@ -21,6 +21,7 @@ set(HARFBUZZ_EXTRA_OPTIONS
# Only used for command line utilities,
# disable as this would add an addition & unnecessary build-dependency.
-Dcairo=disabled
${MESON_BUILD_TYPE}
)
ExternalProject_Add(external_harfbuzz
@ -59,3 +60,10 @@ if(BUILD_MODE STREQUAL Release AND WIN32)
DEPENDEES install
)
endif()
if(BUILD_MODE STREQUAL Debug AND WIN32)
ExternalProject_Add_Step(external_harfbuzz after_install
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/harfbuzz/lib/libharfbuzz.a ${HARVEST_TARGET}/harfbuzz/lib/libharfbuzz_d.lib
DEPENDEES install
)
endif()

View File

@ -15,7 +15,7 @@ llvm-config = '${LIBDIR}/llvm/bin/llvm-config'"
)
set(MESA_EXTRA_FLAGS
-Dbuildtype=release
${MESON_BUILD_TYPE}
-Dc_args=${MESA_CFLAGS}
-Dcpp_args=${MESA_CXXFLAGS}
-Dc_link_args=${MESA_LDFLAGS}

View File

@ -16,8 +16,10 @@ message("BuildMode = ${BUILD_MODE}")
if(BUILD_MODE STREQUAL "Debug")
set(LIBDIR ${CMAKE_CURRENT_BINARY_DIR}/Debug)
set(MESON_BUILD_TYPE -Dbuildtype=debug)
else()
set(LIBDIR ${CMAKE_CURRENT_BINARY_DIR}/Release)
set(MESON_BUILD_TYPE -Dbuildtype=release)
endif()
set(DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/downloads" CACHE STRING "Path for downloaded files")

View File

@ -13,7 +13,7 @@ ExternalProject_Add(external_wayland
# NOTE: `-lm` is needed for `libxml2` which is a static library that uses `libm.so`,
# without this, math symbols such as `floor` aren't found.
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env PKG_CONFIG_PATH=${LIBDIR}/expat/lib/pkgconfig:${LIBDIR}/xml2/lib/pkgconfig:${LIBDIR}/ffi/lib/pkgconfig:$PKG_CONFIG_PATH
${MESON} --prefix ${LIBDIR}/wayland -Ddocumentation=false -Dtests=false -D "c_link_args=-L${LIBDIR}/ffi/lib -lm" . ../external_wayland
${MESON} --prefix ${LIBDIR}/wayland ${MESON_BUILD_TYPE} -Ddocumentation=false -Dtests=false -D "c_link_args=-L${LIBDIR}/ffi/lib -lm" . ../external_wayland
BUILD_COMMAND ninja
INSTALL_COMMAND ninja install
)

View File

@ -7,7 +7,7 @@ ExternalProject_Add(external_wayland_protocols
PREFIX ${BUILD_DIR}/wayland-protocols
# Use `-E` so the `PKG_CONFIG_PATH` can be defined to link against our own WAYLAND.
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env PKG_CONFIG_PATH=${LIBDIR}/wayland/lib64/pkgconfig:$PKG_CONFIG_PATH
${MESON} --prefix ${LIBDIR}/wayland-protocols . ../external_wayland_protocols -Dtests=false
${MESON} --prefix ${LIBDIR}/wayland-protocols ${MESON_BUILD_TYPE} . ../external_wayland_protocols -Dtests=false
BUILD_COMMAND ninja
INSTALL_COMMAND ninja install
)

View File

@ -17,11 +17,13 @@ ExternalProject_Add(external_xvidcore
INSTALL_DIR ${LIBDIR}/xvidcore
)
ExternalProject_Add_Step(external_xvidcore after_install
COMMAND ${CMAKE_COMMAND} -E rename ${LIBDIR}/xvidcore/lib/xvidcore.a ${LIBDIR}/xvidcore/lib/libxvidcore.a || true
COMMAND ${CMAKE_COMMAND} -E remove ${LIBDIR}/xvidcore/lib/xvidcore.dll.a
DEPENDEES install
)
if(WIN32)
ExternalProject_Add_Step(external_xvidcore after_install
COMMAND ${CMAKE_COMMAND} -E rename ${LIBDIR}/xvidcore/lib/xvidcore.a ${LIBDIR}/xvidcore/lib/libxvidcore.a || true
COMMAND ${CMAKE_COMMAND} -E remove ${LIBDIR}/xvidcore/lib/xvidcore.dll.a
DEPENDEES install
)
endif()
if(MSVC)
set_target_properties(external_xvidcore PROPERTIES FOLDER Mingw)

View File

@ -142,7 +142,7 @@ def cmake_advanced_info() -> Union[Tuple[List[str], List[Tuple[str, str]]], Tupl
make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM")
if make_exe is None:
print("Make command not found in: %r not found" % project_path)
print("Make command not found: CMAKE_MAKE_PROGRAM")
return None, None
make_exe_basename = os.path.basename(make_exe)

View File

@ -42,6 +42,7 @@ def parse_arguments() -> argparse.Namespace:
parser.add_argument("--svn-branch", default=None)
parser.add_argument("--git-command", default="git")
parser.add_argument("--use-linux-libraries", action="store_true")
parser.add_argument("--architecture", type=str, choices=("x86_64", "amd64", "arm64",))
return parser.parse_args()
@ -51,6 +52,19 @@ def get_blender_git_root() -> str:
# Setup for precompiled libraries and tests from svn.
def get_effective_architecture(args: argparse.Namespace) -> str:
architecture = args.architecture
if architecture:
assert isinstance(architecture, str)
return architecture
# Check platform.version to detect arm64 with x86_64 python binary.
if "ARM64" in platform.version():
return "arm64"
return platform.machine().lower()
def svn_update(args: argparse.Namespace, release_version: Optional[str]) -> None:
svn_non_interactive = [args.svn_command, '--non-interactive']
@ -58,11 +72,11 @@ def svn_update(args: argparse.Namespace, release_version: Optional[str]) -> None
svn_url = make_utils.svn_libraries_base_url(release_version, args.svn_branch)
# Checkout precompiled libraries
architecture = get_effective_architecture(args)
if sys.platform == 'darwin':
# Check platform.version to detect arm64 with x86_64 python binary.
if platform.machine() == 'arm64' or ('ARM64' in platform.version()):
if architecture == 'arm64':
lib_platform = "darwin_arm64"
elif platform.machine() == 'x86_64':
elif architecture == 'x86_64':
lib_platform = "darwin"
else:
lib_platform = None
@ -256,14 +270,15 @@ if __name__ == "__main__":
blender_skip_msg = ""
submodules_skip_msg = ""
# Test if we are building a specific release version.
branch = make_utils.git_branch(args.git_command)
if branch == 'HEAD':
sys.stderr.write('Blender git repository is in detached HEAD state, must be in a branch\n')
sys.exit(1)
tag = make_utils.git_tag(args.git_command)
release_version = make_utils.git_branch_release_version(branch, tag)
blender_version = make_utils. parse_blender_version()
if blender_version.cycle != 'alpha':
major = blender_version.version // 100
minor = blender_version.version % 100
branch = f"blender-v{major}.{minor}-release"
release_version: Optional[str] = f"{major}.{minor}"
else:
branch = 'main'
release_version = None
if not args.no_libraries:
svn_update(args, release_version)

View File

@ -122,7 +122,7 @@ KERNEL_STRUCT_MEMBER(guiding, bool, use_surface_guiding, KERNEL_FEATURE_PATH_GUI
KERNEL_STRUCT_MEMBER(guiding, float, sample_surface_guiding_rand, KERNEL_FEATURE_PATH_GUIDING)
/* The probability to use surface guiding (i.e., diffuse sampling prob * guiding prob)*/
KERNEL_STRUCT_MEMBER(guiding, float, surface_guiding_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
/* Probability of sampling a BSSRDF closure instead of a BSDF closure*/
/* Probability of sampling a BSSRDF closure instead of a BSDF closure. */
KERNEL_STRUCT_MEMBER(guiding, float, bssrdf_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
/* If volume guiding is enabled */
KERNEL_STRUCT_MEMBER(guiding, bool, use_volume_guiding, KERNEL_FEATURE_PATH_GUIDING)

View File

@ -1177,7 +1177,7 @@ void LightManager::device_update(Device *device,
void LightManager::device_free(Device *, DeviceScene *dscene, const bool free_background)
{
/* to-do: check if the light tree member variables need to be wrapped in a conditional too*/
/* TODO: check if the light tree member variables need to be wrapped in a conditional too. */
dscene->light_tree_nodes.free();
dscene->light_tree_emitters.free();
dscene->light_to_tree.free();

View File

@ -306,14 +306,23 @@ static void gwl_window_frame_update_from_pending(GWL_Window *win);
#ifdef USE_EVENT_BACKGROUND_THREAD
enum eGWL_PendingWindowActions {
PENDING_FRAME_CONFIGURE = 0,
PENDING_EGL_RESIZE,
/**
* The state of the window frame has changed, apply the state from #GWL_Window::frame_pending.
*/
PENDING_WINDOW_FRAME_CONFIGURE = 0,
/** The EGL buffer must be resized to match #GWL_WindowFrame::size. */
PENDING_EGL_WINDOW_RESIZE,
# ifdef GHOST_OPENGL_ALPHA
/** Draw an opaque region behind the window. */
PENDING_OPAQUE_SET,
# endif
PENDING_SCALE_UPDATE,
/**
* The DPI for a monitor has changed or the monitors (outputs)
* this window is visible on may have changed. Recalculate the windows scale.
*/
PENDING_OUTPUT_SCALE_UPDATE,
};
# define PENDING_NUM (PENDING_SCALE_UPDATE + 1)
# define PENDING_NUM (PENDING_OUTPUT_SCALE_UPDATE + 1)
static void gwl_window_pending_actions_tag(GWL_Window *win, enum eGWL_PendingWindowActions type)
{
@ -323,10 +332,10 @@ static void gwl_window_pending_actions_tag(GWL_Window *win, enum eGWL_PendingWin
static void gwl_window_pending_actions_handle(GWL_Window *win)
{
if (win->pending_actions[PENDING_FRAME_CONFIGURE].exchange(false)) {
if (win->pending_actions[PENDING_WINDOW_FRAME_CONFIGURE].exchange(false)) {
gwl_window_frame_update_from_pending(win);
}
if (win->pending_actions[PENDING_EGL_RESIZE].exchange(false)) {
if (win->pending_actions[PENDING_EGL_WINDOW_RESIZE].exchange(false)) {
wl_egl_window_resize(win->egl_window, UNPACK2(win->frame.size), 0, 0);
}
# ifdef GHOST_OPENGL_ALPHA
@ -334,7 +343,7 @@ static void gwl_window_pending_actions_handle(GWL_Window *win)
win->ghost_window->setOpaque();
}
# endif
if (win->pending_actions[PENDING_SCALE_UPDATE].exchange(false)) {
if (win->pending_actions[PENDING_OUTPUT_SCALE_UPDATE].exchange(false)) {
win->ghost_window->outputs_changed_update_scale();
}
}
@ -342,9 +351,10 @@ static void gwl_window_pending_actions_handle(GWL_Window *win)
#endif /* USE_EVENT_BACKGROUND_THREAD */
/**
* Update the window's #GWL_WindowFrame
* Update the window's #GWL_WindowFrame.
* The caller must handle locking & run from the main thread.
*/
static void gwl_window_frame_update_from_pending_lockfree(GWL_Window *win)
static void gwl_window_frame_update_from_pending_no_lock(GWL_Window *win)
{
#ifdef USE_EVENT_BACKGROUND_THREAD
GHOST_ASSERT(win->ghost_system->main_thread_id == std::this_thread::get_id(),
@ -381,7 +391,7 @@ static void gwl_window_frame_update_from_pending(GWL_Window *win)
#ifdef USE_EVENT_BACKGROUND_THREAD
std::lock_guard lock_frame_guard{win->frame_pending_mutex};
#endif
gwl_window_frame_update_from_pending_lockfree(win);
gwl_window_frame_update_from_pending_no_lock(win);
}
/** \} */
@ -576,12 +586,12 @@ static void frame_handle_configure(struct libdecor_frame *frame,
GHOST_SystemWayland *system = win->ghost_system;
const bool is_main_thread = system->main_thread_id == std::this_thread::get_id();
if (!is_main_thread) {
gwl_window_pending_actions_tag(win, PENDING_FRAME_CONFIGURE);
gwl_window_pending_actions_tag(win, PENDING_WINDOW_FRAME_CONFIGURE);
}
else
# endif
{
gwl_window_frame_update_from_pending_lockfree(win);
gwl_window_frame_update_from_pending_no_lock(win);
}
}
}
@ -671,7 +681,7 @@ static void xdg_surface_handle_configure(void *data,
if (!is_main_thread) {
/* NOTE(@ideasman42): this only gets one redraw,
* I could not find a case where this causes problems. */
gwl_window_pending_actions_tag(win, PENDING_FRAME_CONFIGURE);
gwl_window_pending_actions_tag(win, PENDING_WINDOW_FRAME_CONFIGURE);
}
else
#endif
@ -1373,7 +1383,7 @@ bool GHOST_WindowWayland::outputs_changed_update_scale()
{
#ifdef USE_EVENT_BACKGROUND_THREAD
if (system_->main_thread_id != std::this_thread::get_id()) {
gwl_window_pending_actions_tag(window_, PENDING_SCALE_UPDATE);
gwl_window_pending_actions_tag(window_, PENDING_OUTPUT_SCALE_UPDATE);
return false;
}
#endif

View File

@ -93,7 +93,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
}
RECT win_rect = {left, top, long(left + width), long(top + height)};
adjustWindowRectForClosestMonitor(&win_rect, style, extended_style);
adjustWindowRectForDesktop(&win_rect, style, extended_style);
wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0);
m_hWnd = ::CreateWindowExW(extended_style, /* window extended style */
@ -298,24 +298,55 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
m_directManipulationHelper = NULL;
}
void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect,
DWORD dwStyle,
DWORD dwExStyle)
void GHOST_WindowWin32::adjustWindowRectForDesktop(LPRECT win_rect, DWORD dwStyle, DWORD dwExStyle)
{
/* Get Details of the closest monitor. */
HMONITOR hmonitor = MonitorFromRect(win_rect, MONITOR_DEFAULTTONEAREST);
/* Windows can span multiple monitors, but must be usable. The desktop can have a larger
* surface than all monitors combined, for example when two monitors are aligned diagonally.
* Therefore we ensure that all the window's corners are within some monitor's Work area. */
POINT pt;
HMONITOR hmonitor;
MONITORINFOEX monitor;
monitor.cbSize = sizeof(MONITORINFOEX);
monitor.dwFlags = 0;
GetMonitorInfo(hmonitor, &monitor);
/* Constrain requested size and position to fit within this monitor. */
LONG width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect->right - win_rect->left);
LONG height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect->bottom - win_rect->top);
win_rect->left = min(max(monitor.rcWork.left, win_rect->left), monitor.rcWork.right - width);
win_rect->right = win_rect->left + width;
win_rect->top = min(max(monitor.rcWork.top, win_rect->top), monitor.rcWork.bottom - height);
win_rect->bottom = win_rect->top + height;
/* We'll need this value before it is altered for checking later. */
LONG requested_top = win_rect->top;
/* Note that with MonitorFromPoint using MONITOR_DEFAULTTONEAREST, it will return
* the exact monitor if there is one at the location or the nearest monitor if not. */
/* Top-left. */
pt.x = win_rect->left;
pt.y = win_rect->top;
hmonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
win_rect->top = max(win_rect->top, monitor.rcWork.top);
win_rect->left = max(win_rect->left, monitor.rcWork.left);
/* Top-right. */
pt.x = win_rect->right;
pt.y = win_rect->top;
hmonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
win_rect->top = max(win_rect->top, monitor.rcWork.top);
win_rect->right = min(win_rect->right, monitor.rcWork.right);
/* Bottom-left. */
pt.x = win_rect->left;
pt.y = win_rect->bottom;
hmonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
win_rect->bottom = min(win_rect->bottom, monitor.rcWork.bottom);
win_rect->left = max(win_rect->left, monitor.rcWork.left);
/* Bottom-right. */
pt.x = win_rect->right;
pt.y = win_rect->bottom;
hmonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
win_rect->bottom = min(win_rect->bottom, monitor.rcWork.bottom);
win_rect->right = min(win_rect->right, monitor.rcWork.right);
/* With Windows 10 and newer we can adjust for chrome that differs with DPI and scale. */
GHOST_WIN32_AdjustWindowRectExForDpi fpAdjustWindowRectExForDpi = nullptr;
@ -327,6 +358,9 @@ void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect,
/* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be
* correctly outside of monitor bounds. NOTE: You cannot specify #WS_OVERLAPPED when calling. */
if (fpAdjustWindowRectExForDpi) {
/* Use the DPI of the monitor that is at the middle of the rect. */
hmonitor = MonitorFromRect(win_rect, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
UINT dpiX, dpiY;
GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
fpAdjustWindowRectExForDpi(win_rect, dwStyle & ~WS_OVERLAPPED, FALSE, dwExStyle, dpiX);
@ -335,7 +369,12 @@ void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect,
AdjustWindowRectEx(win_rect, dwStyle & ~WS_OVERLAPPED, FALSE, dwExStyle);
}
/* But never allow a top position that can hide part of the title bar. */
/* Don't hide the title bar. Check the working area of the monitor at the top-left corner, using
* the original top since the justWindowRects might have altered it to different monitor. */
pt.x = win_rect->left;
pt.y = requested_top;
hmonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
GetMonitorInfo(hmonitor, &monitor);
win_rect->top = max(monitor.rcWork.top, win_rect->top);
}

View File

@ -87,12 +87,12 @@ class GHOST_WindowWin32 : public GHOST_Window {
~GHOST_WindowWin32();
/**
* Adjusts a requested window rect to fit and position correctly in monitor.
* Adjusts a requested window rect to fit and position within the desktop.
* \param win_rect: pointer to rectangle that will be modified.
* \param dwStyle: The Window Style of the window whose required size is to be calculated.
* \param dwExStyle: The Extended Window Style of the window.
*/
void adjustWindowRectForClosestMonitor(LPRECT win_rect, DWORD dwStyle, DWORD dwExStyle);
void adjustWindowRectForDesktop(LPRECT win_rect, DWORD dwStyle, DWORD dwExStyle);
/**
* Returns indication as to whether the window is valid.

View File

@ -7726,22 +7726,6 @@
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccc" />
</g>
<g
id="g3791"
inkscape:label="CA-24">
<path
sodipodi:nodetypes="csssscccccc"
inkscape:connector-curvature="0"
id="path28911-6"
d="m 489,604 v 8 c 0,0.54532 0.45468,1 1,1 h 12 c 0.54532,0 1,-0.45468 1,-1 v -8 h -1 v 8 h -12 v -8 z"
style="opacity:0.6;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke" />
<path
sodipodi:nodetypes="ssccccsssccccc"
inkscape:connector-curvature="0"
style="vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke"
d="m 490,599 c -0.54532,0 -1,0.45468 -1,1 v 3 h 1 12 1 v -3 c 0,-0.54532 -0.45468,-1 -1,-1 z m 0,1 h 2 v 2 h -2 z"
id="path28913-4" />
</g>
<g
style="display:inline;enable-background:new"
id="g28909-7"
@ -7762,6 +7746,22 @@
transform="matrix(1,0,0,-1,520,1334)"
id="path28907-4" />
</g>
<g
id="g3791"
inkscape:label="CA-24">
<path
sodipodi:nodetypes="csssscccccc"
inkscape:connector-curvature="0"
id="path28911-6"
d="m 489,604 v 8 c 0,0.54532 0.45468,1 1,1 h 12 c 0.54532,0 1,-0.45468 1,-1 v -8 h -1 v 8 h -12 v -8 z"
style="opacity:0.6;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke" />
<path
sodipodi:nodetypes="ssccccsssccccc"
inkscape:connector-curvature="0"
style="vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke"
d="m 490,599 c -0.54532,0 -1,0.45468 -1,1 v 3 h 1 12 1 v -3 c 0,-0.54532 -0.45468,-1 -1,-1 z m 0,1 h 2 v 2 h -2 z"
id="path28913-4" />
</g>
<g
id="g9239"
inkscape:label="CA-23"
@ -13113,6 +13113,40 @@
d="m 221.03217,109.33958 c 0.67621,0.01 0.67621,-1.00961 0,-1 h -2.79493 c 1.0479,-1.11729 1.7641,-1.66802 2.82812,-2.73204 0.62065,-0.56444 -0.28321,-1.46832 -0.84765,-0.84766 -1.06063,1.10128 -1.59202,1.7772 -2.68554,2.87072 v -2.79102 c 0.01,-0.67616 -1.00956,-0.67616 -1,0 v 4 c 3e-5,0.27613 0.22387,0.49998 0.5,0.5 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
id="g24638"
inkscape:label="O-18"
style="display:inline;enable-background:new">
<path
style="color:#000000;opacity:1;fill:none;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;-inkscape-stroke:none"
d="m 374.12695,305.31445 c -1.10721,0 -1.93616,0.24199 -2.97851,0.63086 -1.04236,0.38888 -2.18224,0.95849 -3.30078,1.77735 -2.2371,1.63771 -4.46485,4.54533 -4.46485,8.40625 a 3.0581999,3.0581999 0 0 0 3.0586,3.05859 3.0581999,3.0581999 0 0 0 3.05859,-3.05859 c 0,-1.63187 0.77567,-2.603 1.96094,-3.47071 0.59263,-0.43385 1.26841,-0.77311 1.82422,-0.98047 0.5558,-0.20735 1.10962,-0.24804 0.84179,-0.24804 a 3.0581999,3.0581999 0 0 0 3.05664,-3.0586 3.0581999,3.0581999 0 0 0 -3.05664,-3.05664 z"
id="path6454-6" />
<path
style="display:inline;opacity:0.5;fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
d="m 366.4419,316.12916 c 0,-5.49278 6.00534,-7.75723 7.68412,-7.75723"
id="path15424"
sodipodi:nodetypes="cc" />
</g>
<g
id="g24643"
inkscape:label="O-17"
style="display:inline;enable-background:new">
<path
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 343.45275,317.52104 4.51631,-3.99333"
id="path4694"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 342.44508,318.83525 c 14.125,0.14581 -0.42233,-12.9179 13.5429,-12.85486"
id="path6454"
sodipodi:nodetypes="cc" />
<path
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 350.84964,311.18794 4.21454,-3.72502"
id="path19640"
sodipodi:nodetypes="cc" />
</g>
<g
style="display:inline;enable-background:new"
id="g28812"
@ -18997,36 +19031,6 @@
id="path4817-4"
inkscape:connector-curvature="0" />
</g>
<g
id="g24638">
<path
style="color:#000000;fill:none;stroke-linecap:round;-inkscape-stroke:none;opacity:1;stroke:#ffffff;stroke-opacity:1;stroke-linejoin:round"
d="m 374.12695,305.31445 c -1.10721,0 -1.93616,0.24199 -2.97851,0.63086 -1.04236,0.38888 -2.18224,0.95849 -3.30078,1.77735 -2.2371,1.63771 -4.46485,4.54533 -4.46485,8.40625 a 3.0581999,3.0581999 0 0 0 3.0586,3.05859 3.0581999,3.0581999 0 0 0 3.05859,-3.05859 c 0,-1.63187 0.77567,-2.603 1.96094,-3.47071 0.59263,-0.43385 1.26841,-0.77311 1.82422,-0.98047 0.5558,-0.20735 1.10962,-0.24804 0.84179,-0.24804 a 3.0581999,3.0581999 0 0 0 3.05664,-3.0586 3.0581999,3.0581999 0 0 0 -3.05664,-3.05664 z"
id="path6454-6" />
<path
style="display:inline;opacity:0.5;fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
d="m 366.4419,316.12916 c 0,-5.49278 6.00534,-7.75723 7.68412,-7.75723"
id="path15424"
sodipodi:nodetypes="cc" />
</g>
<g
id="g24643">
<path
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 343.45275,317.52104 4.51631,-3.99333"
id="path4694"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 342.44508,318.83525 c 14.125,0.14581 -0.42233,-12.9179 13.5429,-12.85486"
id="path6454"
sodipodi:nodetypes="cc" />
<path
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 350.84964,311.18794 4.21454,-3.72502"
id="path19640"
sodipodi:nodetypes="cc" />
</g>
</g>
<g
inkscape:groupmode="layer"

Before

Width:  |  Height:  |  Size: 2.5 MiB

After

Width:  |  Height:  |  Size: 2.5 MiB

View File

@ -101,7 +101,11 @@ const UserDef U_default = {
.gp_euclideandist = 2,
.gp_eraser = 25,
.gp_settings = 0,
#ifdef __APPLE__
.gpu_backend = GPU_BACKEND_METAL,
#else
.gpu_backend = GPU_BACKEND_OPENGL,
#endif
/** Initialized by: #BKE_studiolight_default. */
.light_param = {{0}},

View File

@ -29,11 +29,11 @@
icon_modifier="#84b8ffff"
icon_shading="#ea7581ff"
icon_folder="#e3c16eff"
icon_border_intensity="0.85"
icon_border_intensity="1"
>
<wcol_regular>
<ThemeWidgetColors
outline="#b8b8b8"
outline="#4d4d4d"
inner="#dbdbdbff"
inner_sel="#668cccff"
item="#191919ff"
@ -48,7 +48,7 @@
</wcol_regular>
<wcol_tool>
<ThemeWidgetColors
outline="#b8b8b8"
outline="#4d4d4d"
inner="#dbdbdbff"
inner_sel="#5680c2ff"
item="#191919ff"
@ -63,7 +63,7 @@
</wcol_tool>
<wcol_toolbar_item>
<ThemeWidgetColors
outline="#363636"
outline="#4d4d4d"
inner="#434343ff"
inner_sel="#5680c2ff"
item="#e6e6e6cc"
@ -78,7 +78,7 @@
</wcol_toolbar_item>
<wcol_radio>
<ThemeWidgetColors
outline="#434343"
outline="#4d4d4d"
inner="#3b3b3bff"
inner_sel="#5680c2e6"
item="#ffffffff"
@ -93,7 +93,7 @@
</wcol_radio>
<wcol_text>
<ThemeWidgetColors
outline="#666666"
outline="#4d4d4d"
inner="#282828ff"
inner_sel="#333333ff"
item="#5680c2ff"
@ -108,7 +108,7 @@
</wcol_text>
<wcol_option>
<ThemeWidgetColors
outline="#373737"
outline="#4d4d4d"
inner="#3c3c3cff"
inner_sel="#5680c2ff"
item="#ffffffff"
@ -123,8 +123,8 @@
</wcol_option>
<wcol_toggle>
<ThemeWidgetColors
outline="#999999"
inner="#c0c0c0ff"
outline="#4d4d4d"
inner="#dbdbdbff"
inner_sel="#5680c2ff"
item="#191919ff"
text="#000000"
@ -138,7 +138,7 @@
</wcol_toggle>
<wcol_num>
<ThemeWidgetColors
outline="#b8b8b8"
outline="#4d4d4d"
inner="#d3d3d3ff"
inner_sel="#5680c2ff"
item="#80b1ffff"
@ -153,7 +153,7 @@
</wcol_num>
<wcol_numslider>
<ThemeWidgetColors
outline="#b8b8b8"
outline="#4d4d4d"
inner="#999999ff"
inner_sel="#999999ff"
item="#e6e6e6ff"
@ -168,8 +168,8 @@
</wcol_numslider>
<wcol_box>
<ThemeWidgetColors
outline="#959595"
inner="#cccccc80"
outline="#4d4d4d"
inner="#80808080"
inner_sel="#5680c2ff"
item="#191919ff"
text="#333333"
@ -183,7 +183,7 @@
</wcol_box>
<wcol_menu>
<ThemeWidgetColors
outline="#3b3b3b"
outline="#3d3d3d"
inner="#3b3b3bff"
inner_sel="#767676ff"
item="#808080ff"
@ -217,7 +217,7 @@
inner="#c0c0c0ff"
inner_sel="#cdcdcdff"
item="#727272ff"
text="#1a1a1a"
text="#4d4d4d"
text_sel="#1a1a1a"
show_shaded="FALSE"
shadetop="25"
@ -304,11 +304,11 @@
<wcol_list_item>
<ThemeWidgetColors
outline="#e6e6e6"
inner="#00000000"
inner_sel="#808080ff"
inner="#1a1a1a00"
inner_sel="#c0c0c0ff"
item="#1a1a1aff"
text="#1a1a1a"
text_sel="#ffffff"
text_sel="#000000"
show_shaded="FALSE"
shadetop="0"
shadedown="0"
@ -316,6 +316,21 @@
>
</ThemeWidgetColors>
</wcol_list_item>
<wcol_view_item>
<ThemeWidgetColors
outline="#e6e6e6"
inner="#c0c0c044"
inner_sel="#c0c0c0ff"
item="#1a1a1aff"
text="#1a1a1a"
text_sel="#000000"
show_shaded="FALSE"
shadetop="0"
shadedown="0"
roundness="0.4"
>
</ThemeWidgetColors>
</wcol_view_item>
<wcol_state>
<ThemeWidgetStateColors
inner_anim="#73be4c"
@ -334,8 +349,8 @@
</wcol_state>
<wcol_tab>
<ThemeWidgetColors
outline="#656565"
inner="#818181ff"
outline="#333333"
inner="#808080cc"
inner_sel="#b3b3b3ff"
item="#28292dff"
text="#1a1a1a"
@ -414,6 +429,7 @@
bone_locked_weight="#ff000080"
bundle_solid="#c8c8c8"
camera_path="#000000"
camera_passepartout="#000000"
skin_root="#b44d4d"
view_overlay="#000000"
transform="#ffffff"
@ -452,9 +468,9 @@
</gradients>
<panelcolors>
<ThemePanelColors
header="#b3b3b3ff"
back="#b3b3b3cc"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -519,9 +535,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -552,7 +568,7 @@
header="#adadadff"
header_text="#000000"
header_text_hi="#ffffff"
button="#999999e6"
button="#b3b3b3ff"
button_title="#1a1a1a"
button_text="#000000"
button_text_hi="#000000"
@ -565,9 +581,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -623,9 +639,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -698,9 +714,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -783,9 +799,9 @@
>
<panelcolors>
<ThemePanelColors
header="#b3b3b3ff"
back="#b3b3b3cc"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -843,14 +859,23 @@
>
<panelcolors>
<ThemePanelColors
header="#b3b3b3ff"
back="#b3b3b3cc"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
</ThemeSpaceGeneric>
</space>
<space_list>
<ThemeSpaceListGeneric
list="#181818"
list_title="#ffffff"
list_text="#ffffff"
list_text_hi="#ffffff"
>
</ThemeSpaceListGeneric>
</space_list>
</ThemeSequenceEditor>
</sequence_editor>
<properties>
@ -871,7 +896,7 @@
button_title="#000000"
button_text="#000000"
button_text_hi="#000000"
navigation_bar="#656565ff"
navigation_bar="#1d1d1dff"
execution_buts="#00000000"
tab_active="#6697e6"
tab_inactive="#535353"
@ -880,9 +905,9 @@
>
<panelcolors>
<ThemePanelColors
header="#b3b3b300"
back="#a3a3a3cc"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -927,9 +952,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -939,44 +964,44 @@
</text_editor>
<node_editor>
<ThemeNodeEditor
grid="#1B1B1B"
node_selected="#f15800"
node_active="#f15800"
wire="#191919"
wire_inner="#999999"
wire_select="#ffa733"
selected_text="#7f7070"
node_backdrop="#e6e6e6ff"
converter_node="#66c4ff"
color_node="#ffcb4d"
group_node="#59b36ab9"
group_socket_node="#dfc300"
frame_node="#9b9b9b60"
matte_node="#977474"
distor_node="#749797"
grid="#282828"
node_selected="#ed5700"
node_active="#ffffff"
wire="#1a1a1aff"
wire_inner="#8d8d8d"
wire_select="#ffffffb3"
selected_text="#7f7f7f"
node_backdrop="#666666ff"
converter_node="#12adff"
color_node="#cccc00"
group_node="#3b660a"
group_socket_node="#000000"
frame_node="#0f0f0fcc"
matte_node="#973c3c"
distor_node="#4c9797"
noodle_curving="4"
grid_levels="3"
dash_alpha="0.5"
input_node="#cb3d4a"
output_node="#cb3d4a"
filter_node="#6c696f"
vector_node="#9999ff"
texture_node="#ffc399"
shader_node="#ea7581"
script_node="#6c696f"
input_node="#ff3371"
output_node="#4d0017"
filter_node="#551a80"
vector_node="#4d4dff"
texture_node="#e66800"
shader_node="#24b524"
script_node="#084d4d"
pattern_node="#6c696f"
layout_node="#6c696f"
geometry_node="#00d7a4"
attribute_node="#3f5980"
geometry_node="#00d6a3"
attribute_node="#001566"
>
<space>
<ThemeSpaceGeneric
back="#353535"
title="#000000"
text="#000000"
back="#1d1d1d"
title="#eeeeee"
text="#e6e6e6"
text_hi="#ffffff"
header="#b3b3b3ff"
header_text="#000000"
header_text="#eeeeee"
header_text_hi="#ffffff"
button="#99999900"
button_title="#1a1a1a"
@ -991,9 +1016,9 @@
>
<panelcolors>
<ThemePanelColors
header="#b3b3b3ff"
back="#b3b3b3cc"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -1013,8 +1038,8 @@
<outliner>
<ThemeOutliner
match="#337f33"
selected_highlight="#7a8e99"
active="#92aab7"
selected_highlight="#7a8499"
active="#929eb7"
selected_object="#ffddb3"
active_object="#ffffff"
edited_object="#0080624d"
@ -1042,9 +1067,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -1091,9 +1116,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -1125,9 +1150,9 @@
>
<panelcolors>
<ThemePanelColors
header="#b3b3b300"
back="#a3a3a3cc"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -1137,26 +1162,26 @@
</preferences>
<console>
<ThemeConsole
line_output="#6080ff"
line_input="#ffffff"
line_info="#00aa00"
line_error="#dc6060"
cursor="#dc6060"
line_output="#71a8ff"
line_input="#f2f2f2"
line_info="#95d600"
line_error="#ff4d84"
cursor="#ff0000"
select="#ffffff30"
>
<space>
<ThemeSpaceGeneric
back="#000000"
title="#000000"
text="#000000"
back="#1d1d1d"
title="#eeeeee"
text="#e6e6e6"
text_hi="#ffffff"
header="#b3b3b3ff"
header_text="#000000"
header_text_hi="#ffffff"
button="#7272727f"
button_title="#000000"
button_text="#000000"
button_text_hi="#000000"
button="#30303000"
button_title="#ffffff"
button_text="#cccccc"
button_text_hi="#ffffff"
navigation_bar="#00000000"
execution_buts="#00000000"
tab_active="#6697e6"
@ -1166,9 +1191,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -1231,9 +1256,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -1274,9 +1299,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -1292,7 +1317,7 @@
title="#ffffff"
text="#ffffff"
text_hi="#ffffff"
header="#adadadff"
header="#999999ff"
header_text="#1a1a1a"
header_text_hi="#ffffff"
button="#2f303500"
@ -1308,9 +1333,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -1331,7 +1356,7 @@
header="#adadadff"
header_text="#000000"
header_text_hi="#ffffff"
button="#999999e6"
button="#b3b3b3ff"
button_title="#1a1a1a"
button_text="#000000"
button_text_hi="#000000"
@ -1344,9 +1369,9 @@
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
header="#ccccccff"
back="#ccccccff"
sub_back="#0000000f"
>
</ThemePanelColors>
</panelcolors>
@ -1585,8 +1610,8 @@
shadow="3"
shadow_offset_x="0"
shadow_offset_y="-1"
shadow_alpha="0.3"
shadow_value="0.7"
shadow_alpha="1"
shadow_value="0.8"
>
</ThemeFontStyle>
</panel_title>
@ -1596,8 +1621,8 @@
shadow="3"
shadow_offset_x="0"
shadow_offset_y="-1"
shadow_alpha="0.3"
shadow_value="0.7"
shadow_alpha="0"
shadow_value="0.8"
>
</ThemeFontStyle>
</widget_label>
@ -1607,8 +1632,8 @@
shadow="1"
shadow_offset_x="0"
shadow_offset_y="-1"
shadow_alpha="0.3"
shadow_value="0.7"
shadow_alpha="0"
shadow_value="0.8"
>
</ThemeFontStyle>
</widget>

View File

@ -3513,6 +3513,9 @@ def km_animation_channels(params):
("anim.channels_ungroup", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None),
# Menus.
*_template_items_context_menu("DOPESHEET_MT_channel_context_menu", params.context_menu_event),
# View
("anim.channel_view_pick", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "alt": True}, None),
("anim.channels_view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
])
return keymap
@ -5631,6 +5634,8 @@ def km_curves(params):
("curves.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
("curves.delete", {"type": 'X', "value": 'PRESS'}, None),
("curves.delete", {"type": 'DEL', "value": 'PRESS'}, None),
("curves.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
("curves.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
])
return keymap

View File

@ -25,7 +25,7 @@ def build_default_empty_geometry_node_group(name):
def geometry_node_group_empty_new():
group = build_default_empty_geometry_node_group(data_("Geometry Nodes"))
group.links.new(group.nodes["Group Input"].outputs[0], group.nodes["Group Output"].inputs[0])
group.links.new(group.nodes[data_("Group Input")].outputs[0], group.nodes[data_("Group Output")].inputs[0])
return group
@ -121,8 +121,8 @@ class MoveModifierToNodes(Operator):
group_node.node_tree = old_group
group_node.update()
group_input_node = group.nodes["Group Input"]
group_output_node = group.nodes["Group Output"]
group_input_node = group.nodes[data_("Group Input")]
group_output_node = group.nodes[data_("Group Output")]
# Copy default values for inputs and create named attribute input nodes.
input_nodes = []
@ -173,6 +173,7 @@ class MoveModifierToNodes(Operator):
first_geometry_output = group_node_output
# Adjust locations of store named attribute nodes and move group output.
# Note that the node group has its sockets names translated, while the built-in nodes don't.
if store_nodes:
for i, node in enumerate(store_nodes):
node.location.x = (i + 1) * 175
@ -182,9 +183,10 @@ class MoveModifierToNodes(Operator):
group.links.new(first_geometry_output, store_nodes[0].inputs["Geometry"])
for i in range(len(store_nodes) - 1):
group.links.new(store_nodes[i].outputs["Geometry"], store_nodes[i + 1].inputs["Geometry"])
group.links.new(store_nodes[-1].outputs["Geometry"], group_output_node.inputs["Geometry"])
group.links.new(store_nodes[-1].outputs["Geometry"], group_output_node.inputs[data_("Geometry")])
else:
group.links.new(first_geometry_output, group_output_node.inputs["Geometry"])
group.links.new(first_geometry_output, group_output_node.inputs[data_("Geometry")])
modifier.node_group = group

View File

@ -111,6 +111,7 @@ class QuickFur(ObjectModeOperator, Operator):
bpy.ops.wm.append(directory=assets_directory,
filename=name,
use_recursive=True,
clear_asset_data=True,
do_reuse_local_id=True)
generate_group = bpy.data.node_groups["Generate Hair Curves"]
interpolate_group = bpy.data.node_groups["Interpolate Hair Curves"]

View File

@ -556,7 +556,7 @@ class WM_OT_context_toggle_enum(Operator):
class WM_OT_context_cycle_int(Operator):
"""Set a context value (useful for cycling active material, """ \
"""vertex keys, groups, etc.)"""
"""shape keys, groups, etc.)"""
bl_idname = "wm.context_cycle_int"
bl_label = "Context Int Cycle"
bl_options = {'UNDO', 'INTERNAL'}

View File

@ -397,7 +397,7 @@ class ConstraintButtonsPanel:
sub.prop(con, "invert_z", text="Z", toggle=True)
row.label(icon='BLANK1')
layout.prop(con, "mix_mode", text="Mix")
layout.prop(con, "mix_mode", text="Mix", text_ctxt=i18n_contexts.constraint)
self.space_template(layout, con)
@ -488,7 +488,7 @@ class ConstraintButtonsPanel:
self.target_template(layout, con)
layout.prop(con, "remove_target_shear")
layout.prop(con, "mix_mode", text="Mix")
layout.prop(con, "mix_mode", text="Mix", text_ctxt=i18n_contexts.constraint)
self.space_template(layout, con)
@ -513,7 +513,7 @@ class ConstraintButtonsPanel:
subsub.prop(con, "eval_time", text="")
row.prop_decorator(con, "eval_time")
layout.prop(con, "mix_mode", text="Mix")
layout.prop(con, "mix_mode", text="Mix", text_ctxt=i18n_contexts.constraint)
self.draw_influence(layout, con)
@ -1024,7 +1024,7 @@ class ConstraintButtonsSubPanel:
col.prop(con, "to_min_z" + ext, text="Min")
col.prop(con, "to_max_z" + ext, text="Max")
layout.prop(con, "mix_mode" + ext, text="Mix")
layout.prop(con, "mix_mode" + ext, text="Mix", text_ctxt=i18n_contexts.constraint)
def draw_armature_bones(self, context):
layout = self.layout

View File

@ -604,7 +604,7 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
colliding_names = []
for collection in (
# Built-in names.
{"shade_smooth": None, "normal": None, "crease": None},
{"shade_smooth": None, "crease": None},
mesh.attributes,
None if ob is None else ob.vertex_groups,
):

View File

@ -78,7 +78,7 @@ class PARTICLE_MT_context_menu(Menu):
props.use_active = False
props.remove_target_particles = True
if psys.settings.type == 'HAIR':
if psys is not None and psys.settings.type == 'HAIR':
layout.operator(
"curves.convert_from_particle_system",
text="Convert to Curves")

View File

@ -474,6 +474,9 @@ class DOPESHEET_MT_channel(Menu):
layout.separator()
layout.operator("anim.channels_fcurves_enable")
layout.separator()
layout.operator("anim.channels_view_selected")
class DOPESHEET_MT_key(Menu):
bl_label = "Key"
@ -601,6 +604,9 @@ class DOPESHEET_MT_gpencil_channel(Menu):
layout.separator()
layout.operator_menu_enum("anim.channels_move", "direction", text="Move...")
layout.separator()
layout.operator("anim.channels_view_selected")
class DOPESHEET_MT_gpencil_key(Menu):
bl_label = "Key"
@ -689,6 +695,9 @@ class DOPESHEET_MT_channel_context_menu(Menu):
# This menu is used from the graph editor too.
is_graph_editor = context.area.type == 'GRAPH_EDITOR'
layout.separator()
layout.operator("anim.channels_view_selected")
layout.operator("anim.channels_setting_enable", text="Mute Channels").type = 'MUTE'
layout.operator("anim.channels_setting_disable", text="Unmute Channels").type = 'MUTE'
layout.separator()

View File

@ -239,6 +239,9 @@ class GRAPH_MT_channel(Menu):
layout.separator()
layout.operator("anim.channels_fcurves_enable")
layout.separator()
layout.operator("anim.channels_view_selected")
class GRAPH_MT_key(Menu):
bl_label = "Key"

View File

@ -574,6 +574,11 @@ class NODE_MT_context_menu(Menu):
layout.menu("NODE_MT_context_menu_select_menu")
layout.menu("NODE_MT_context_menu_show_hide_menu")
if active_node:
layout.separator()
props = layout.operator("wm.doc_view_manual", text="Online Manual", icon='URL')
props.doc_id = active_node.bl_idname
class NODE_PT_active_node_generic(Panel):
bl_space_type = 'NODE_EDITOR'

View File

@ -2044,6 +2044,16 @@ class VIEW3D_MT_select_paint_mask_vertex(Menu):
layout.operator("paint.vert_select_linked", text="Select Linked")
class VIEW3D_MT_edit_curves_select_more_less(Menu):
bl_label = "Select More/Less"
def draw(self, _context):
layout = self.layout
layout.operator("curves.select_more", text="More")
layout.operator("curves.select_less", text="Less")
class VIEW3D_MT_select_edit_curves(Menu):
bl_label = "Select"
@ -2053,10 +2063,17 @@ class VIEW3D_MT_select_edit_curves(Menu):
layout.operator("curves.select_all", text="All").action = 'SELECT'
layout.operator("curves.select_all", text="None").action = 'DESELECT'
layout.operator("curves.select_all", text="Invert").action = 'INVERT'
layout.separator()
layout.operator("curves.select_random", text="Random")
layout.operator("curves.select_end", text="Endpoints")
layout.operator("curves.select_linked", text="Linked")
layout.separator()
layout.menu("VIEW3D_MT_edit_curves_select_more_less")
class VIEW3D_MT_select_sculpt_curves(Menu):
bl_label = "Select"
@ -8046,6 +8063,7 @@ classes = (
VIEW3D_MT_select_gpencil,
VIEW3D_MT_select_paint_mask,
VIEW3D_MT_select_paint_mask_vertex,
VIEW3D_MT_edit_curves_select_more_less,
VIEW3D_MT_select_edit_curves,
VIEW3D_MT_select_sculpt_curves,
VIEW3D_MT_mesh_add,

View File

@ -23,8 +23,6 @@ extern "C" {
/** Blender release cycle stage: alpha/beta/rc/release. */
#define BLENDER_VERSION_CYCLE alpha
/* TODO proper version bump. */
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 0

View File

@ -108,7 +108,7 @@ BVHTree *bvhtree_from_editmesh_verts(
*/
BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data,
struct BMEditMesh *em,
const blender::BitVector<> &mask,
blender::BitSpan mask,
int verts_num_active,
float epsilon,
int tree_type,
@ -124,7 +124,7 @@ BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data,
BVHTree *bvhtree_from_mesh_verts_ex(struct BVHTreeFromMesh *data,
const float (*vert_positions)[3],
int verts_num,
const blender::BitVector<> &verts_mask,
blender::BitSpan verts_mask,
int verts_num_active,
float epsilon,
int tree_type,
@ -138,7 +138,7 @@ BVHTree *bvhtree_from_editmesh_edges(
*/
BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data,
struct BMEditMesh *em,
const blender::BitVector<> &edges_mask,
blender::BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
@ -156,7 +156,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(struct BVHTreeFromMesh *data,
const float (*vert_positions)[3],
const struct MEdge *edge,
int edges_num,
const blender::BitVector<> &edges_mask,
blender::BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
@ -170,7 +170,7 @@ BVHTree *bvhtree_from_editmesh_looptri(
*/
BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data,
struct BMEditMesh *em,
const blender::BitVector<> &mask,
blender::BitSpan mask,
int looptri_num_active,
float epsilon,
int tree_type,
@ -184,7 +184,7 @@ BVHTree *bvhtree_from_mesh_looptri_ex(struct BVHTreeFromMesh *data,
const struct MLoop *mloop,
const struct MLoopTri *looptri,
int looptri_num,
const blender::BitVector<> &mask,
blender::BitSpan mask,
int looptri_num_active,
float epsilon,
int tree_type,

View File

@ -282,6 +282,7 @@ bool CustomData_has_layer(const struct CustomData *data, int type);
* Returns the number of layers with this type.
*/
int CustomData_number_of_layers(const struct CustomData *data, int type);
int CustomData_number_of_anonymous_layers(const struct CustomData *data, int type);
int CustomData_number_of_layers_typemask(const struct CustomData *data, eCustomDataMask mask);
/**

View File

@ -373,6 +373,8 @@ bool BKE_fcurve_calc_range(
/**
* Calculate the extents of F-Curve's data.
* \param range Only calculate the bounds of the FCurve in the given range.
* Does the full range if NULL.
*/
bool BKE_fcurve_calc_bounds(const struct FCurve *fcu,
float *xmin,
@ -380,7 +382,8 @@ bool BKE_fcurve_calc_bounds(const struct FCurve *fcu,
float *ymin,
float *ymax,
bool do_sel_only,
bool include_handles);
bool include_handles,
const float range[2]);
/**
* Return an array of keyed frames, rounded to `interval`.

View File

@ -41,10 +41,10 @@ struct PropertyRNA;
*/
void BKE_nlastrip_free(struct NlaStrip *strip, bool do_id_user);
/**
* Remove the given NLA track from the set of NLA tracks, free the track's data,
* and the track itself.
* Remove & Frees all NLA strips from the given NLA track,
* then frees (doesn't remove) the track itself.
*/
void BKE_nlatrack_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user);
void BKE_nlatrack_free(struct NlaTrack *nlt, bool do_id_user);
/**
* Free the elements of type NLA Tracks provided in the given list, but do not free
* the list itself since that is not free-standing
@ -95,6 +95,17 @@ struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt,
struct NlaTrack *prev,
bool is_liboverride);
/**
* Removes the given NLA track from the list of tracks provided.
*/
void BKE_nlatrack_remove(ListBase *tracks, struct NlaTrack *nlt);
/**
* Remove the given NLA track from the list of NLA tracks, free the track's data,
* and the track itself.
*/
void BKE_nlatrack_remove_and_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user);
/**
* Create a NLA Strip referencing the given Action.
*/

View File

@ -316,9 +316,6 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) cons
GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner) const
{
if (writable_ != Writable) {
return {};
}
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
return {};
@ -905,10 +902,8 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer(
BLI_assert(src);
bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
id, meta_data.domain, meta_data.data_type);
if (dst) {
/* Writing the the legacy "normal" attribute will fail. */
attributes.append({std::move(src), meta_data, std::move(dst)});
}
BLI_assert(dst);
attributes.append({std::move(src), meta_data, std::move(dst)});
return true;
});

View File

@ -37,10 +37,6 @@ class BuiltinAttributeProvider {
Creatable,
NonCreatable,
};
enum WritableEnum {
Writable,
Readonly,
};
enum DeletableEnum {
Deletable,
NonDeletable,
@ -51,7 +47,6 @@ class BuiltinAttributeProvider {
const eAttrDomain domain_;
const eCustomDataType data_type_;
const CreatableEnum createable_;
const WritableEnum writable_;
const DeletableEnum deletable_;
const AttributeValidator validator_;
@ -60,14 +55,12 @@ class BuiltinAttributeProvider {
const eAttrDomain domain,
const eCustomDataType data_type,
const CreatableEnum createable,
const WritableEnum writable,
const DeletableEnum deletable,
AttributeValidator validator = {})
: name_(std::move(name)),
domain_(domain),
data_type_(data_type),
createable_(createable),
writable_(writable),
deletable_(deletable),
validator_(validator)
{
@ -205,20 +198,14 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
const eCustomDataType attribute_type,
const eCustomDataType stored_type,
const CreatableEnum creatable,
const WritableEnum writable,
const DeletableEnum deletable,
const CustomDataAccessInfo custom_data_access,
const AsReadAttribute as_read_attribute,
const AsWriteAttribute as_write_attribute,
const UpdateOnChange update_on_write,
const AttributeValidator validator = {})
: BuiltinAttributeProvider(std::move(attribute_name),
domain,
attribute_type,
creatable,
writable,
deletable,
validator),
: BuiltinAttributeProvider(
std::move(attribute_name), domain, attribute_type, creatable, deletable, validator),
stored_type_(stored_type),
custom_data_access_(custom_data_access),
as_read_attribute_(as_read_attribute),

View File

@ -25,6 +25,8 @@
#include "BKE_particle.h"
#include "BLI_kdopbvh.h"
#include "BLT_translation.h"
#include "BKE_modifier.h"
#include "RNA_enum_types.h"
@ -1607,7 +1609,7 @@ BoidRule *boid_new_rule(int type)
rule->type = type;
rule->flag |= BOIDRULE_IN_AIR | BOIDRULE_ON_LAND;
BLI_strncpy(rule->name, rna_enum_boidrule_type_items[type - 1].name, sizeof(rule->name));
BLI_strncpy(rule->name, DATA_(rna_enum_boidrule_type_items[type - 1].name), sizeof(rule->name));
return rule;
}

View File

@ -29,6 +29,7 @@
#include "MEM_guardedalloc.h"
using blender::BitSpan;
using blender::BitVector;
using blender::float3;
using blender::IndexRange;
@ -672,7 +673,7 @@ static BVHTree *bvhtree_from_editmesh_verts_create_tree(float epsilon,
int tree_type,
int axis,
BMEditMesh *em,
const BitVector<> &verts_mask,
const BitSpan verts_mask,
int verts_num_active)
{
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
@ -706,7 +707,7 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon,
int axis,
const float (*positions)[3],
const int verts_num,
const BitVector<> &verts_mask,
const BitSpan verts_mask,
int verts_num_active)
{
if (!verts_mask.is_empty()) {
@ -737,7 +738,7 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon,
BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
const BitVector<> &verts_mask,
const BitSpan verts_mask,
int verts_num_active,
float epsilon,
int tree_type,
@ -764,7 +765,7 @@ BVHTree *bvhtree_from_editmesh_verts(
BVHTree *bvhtree_from_mesh_verts_ex(BVHTreeFromMesh *data,
const float (*vert_positions)[3],
const int verts_num,
const BitVector<> &verts_mask,
const BitSpan verts_mask,
int verts_num_active,
float epsilon,
int tree_type,
@ -794,7 +795,7 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon,
int tree_type,
int axis,
BMEditMesh *em,
const BitVector<> &edges_mask,
const BitSpan edges_mask,
int edges_num_active)
{
BM_mesh_elem_table_ensure(em->bm, BM_EDGE);
@ -833,7 +834,7 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon,
static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
const MEdge *edge,
const int edge_num,
const BitVector<> &edges_mask,
const BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
@ -871,7 +872,7 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
const BitVector<> &edges_mask,
const BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
@ -899,7 +900,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data,
const float (*vert_positions)[3],
const MEdge *edge,
const int edges_num,
const BitVector<> &edges_mask,
const BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
@ -931,7 +932,7 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree(float epsilon,
const float (*positions)[3],
const MFace *face,
const int faces_num,
const BitVector<> &faces_mask,
const BitSpan faces_mask,
int faces_num_active)
{
if (faces_num == 0) {
@ -984,7 +985,7 @@ static BVHTree *bvhtree_from_editmesh_looptri_create_tree(float epsilon,
int tree_type,
int axis,
BMEditMesh *em,
const BitVector<> &looptri_mask,
const BitSpan looptri_mask,
int looptri_num_active)
{
const int looptri_num = em->tottri;
@ -1038,7 +1039,7 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree(float epsilon,
const MLoop *mloop,
const MLoopTri *looptri,
const int looptri_num,
const BitVector<> &looptri_mask,
const BitSpan looptri_mask,
int looptri_num_active)
{
if (!looptri_mask.is_empty()) {
@ -1079,7 +1080,7 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree(float epsilon,
BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
const BitVector<> &looptri_mask,
const BitSpan looptri_mask,
int looptri_num_active,
float epsilon,
int tree_type,
@ -1109,7 +1110,7 @@ BVHTree *bvhtree_from_mesh_looptri_ex(BVHTreeFromMesh *data,
const struct MLoop *mloop,
const struct MLoopTri *looptri,
const int looptri_num,
const BitVector<> &looptri_mask,
const BitSpan looptri_mask,
int looptri_num_active,
float epsilon,
int tree_type,

View File

@ -159,12 +159,8 @@ static void camera_blend_read_expand(BlendExpander *expander, ID *id)
BLO_expand(expander, ca->ipo); // XXX deprecated - old animation system
LISTBASE_FOREACH (CameraBGImage *, bgpic, &ca->bg_images) {
if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) {
BLO_expand(expander, bgpic->ima);
}
else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) {
BLO_expand(expander, bgpic->ima);
}
BLO_expand(expander, bgpic->ima);
BLO_expand(expander, bgpic->clip);
}
}

View File

@ -2991,6 +2991,19 @@ int CustomData_number_of_layers(const CustomData *data, const int type)
return number;
}
int CustomData_number_of_anonymous_layers(const CustomData *data, const int type)
{
int number = 0;
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type && data->layers[i].anonymous_id != nullptr) {
number++;
}
}
return number;
}
int CustomData_number_of_layers_typemask(const CustomData *data, const eCustomDataMask mask)
{
int number = 0;

View File

@ -557,10 +557,11 @@ int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[],
/* ...................................... */
/* Helper for calc_fcurve_* functions -> find first and last BezTriple to be used. */
static short get_fcurve_end_keyframes(const FCurve *fcu,
BezTriple **first,
BezTriple **last,
const bool do_sel_only)
static bool get_fcurve_end_keyframes(const FCurve *fcu,
BezTriple **first,
BezTriple **last,
const bool do_sel_only,
const float range[2])
{
bool found = false;
@ -573,11 +574,31 @@ static short get_fcurve_end_keyframes(const FCurve *fcu,
return found;
}
int first_index = 0;
int last_index = fcu->totvert - 1;
if (range != NULL) {
/* If a range is passed in find the first and last keyframe within that range. */
bool replace = false;
first_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[0], fcu->totvert, &replace);
last_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[1], fcu->totvert, &replace);
/* If first and last index are the same, no keyframes were found in the range. */
if (first_index == last_index) {
return found;
}
/* The binary search returns an index where a keyframe would be inserted,
so it needs to be clamped to ensure it is in range of the array. */
first_index = clamp_i(first_index, 0, fcu->totvert - 1);
last_index = clamp_i(last_index - 1, 0, fcu->totvert - 1);
}
/* Only include selected items? */
if (do_sel_only) {
/* Find first selected. */
BezTriple *bezt = fcu->bezt;
for (int i = 0; i < fcu->totvert; bezt++, i++) {
for (int i = first_index; i <= last_index; i++) {
BezTriple *bezt = &fcu->bezt[i];
if (BEZT_ISSEL_ANY(bezt)) {
*first = bezt;
found = true;
@ -586,8 +607,8 @@ static short get_fcurve_end_keyframes(const FCurve *fcu,
}
/* Find last selected. */
bezt = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert);
for (int i = 0; i < fcu->totvert; bezt--, i++) {
for (int i = last_index; i >= first_index; i--) {
BezTriple *bezt = &fcu->bezt[i];
if (BEZT_ISSEL_ANY(bezt)) {
*last = bezt;
found = true;
@ -596,9 +617,8 @@ static short get_fcurve_end_keyframes(const FCurve *fcu,
}
}
else {
/* Use the whole array. */
*first = fcu->bezt;
*last = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert);
*first = &fcu->bezt[first_index];
*last = &fcu->bezt[last_index];
found = true;
}
@ -611,23 +631,25 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu,
float *ymin,
float *ymax,
const bool do_sel_only,
const bool include_handles)
const bool include_handles,
const float range[2])
{
float xminv = 999999999.0f, xmaxv = -999999999.0f;
float yminv = 999999999.0f, ymaxv = -999999999.0f;
bool foundvert = false;
const bool use_range = range != NULL;
if (fcu->totvert) {
if (fcu->bezt) {
BezTriple *bezt_first = NULL, *bezt_last = NULL;
if (xmin || xmax) {
/* Get endpoint keyframes. */
foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only);
BezTriple *bezt_first = NULL, *bezt_last = NULL;
foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, range);
if (bezt_first) {
BLI_assert(bezt_last != NULL);
foundvert = true;
if (include_handles) {
xminv = min_fff(xminv, bezt_first->vec[0][0], bezt_first->vec[1][0]);
xmaxv = max_fff(xmaxv, bezt_last->vec[1][0], bezt_last->vec[2][0]);
@ -645,6 +667,9 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu,
int i;
for (bezt = fcu->bezt, i = 0; i < fcu->totvert; prevbezt = bezt, bezt++, i++) {
if (use_range && (bezt->vec[1][0] < range[0] || bezt->vec[1][0] > range[1])) {
continue;
}
if ((do_sel_only == false) || BEZT_ISSEL_ANY(bezt)) {
/* Keyframe itself. */
yminv = min_ff(yminv, bezt->vec[1][1]);
@ -717,10 +742,10 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu,
}
if (xmin) {
*xmin = 0.0f;
*xmin = use_range ? range[0] : 0.0f;
}
if (xmax) {
*xmax = 1.0f;
*xmax = use_range ? range[1] : 1.0f;
}
if (ymin) {
@ -745,7 +770,7 @@ bool BKE_fcurve_calc_range(
BezTriple *bezt_first = NULL, *bezt_last = NULL;
/* Get endpoint keyframes. */
get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only);
get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, NULL);
if (bezt_first) {
BLI_assert(bezt_last != NULL);

View File

@ -375,7 +375,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
make_array_read_attribute<float3>,
@ -387,7 +386,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
@ -399,7 +397,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int>,
@ -411,7 +408,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
@ -423,7 +419,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float3>,
@ -435,7 +430,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float3>,
@ -453,7 +447,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int8_t>,
@ -466,7 +459,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int8_t>,
@ -479,7 +471,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
@ -495,7 +486,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int8_t>,
@ -514,7 +504,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int8_t>,
@ -533,7 +522,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int8_t>,
@ -552,7 +540,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int8_t>,
@ -569,7 +556,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int>,
@ -582,7 +568,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
CD_PROP_BOOL,
CD_PROP_BOOL,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<bool>,

View File

@ -129,7 +129,7 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider
public:
InstancePositionAttributeProvider()
: BuiltinAttributeProvider(
"position", ATTR_DOMAIN_INSTANCE, CD_PROP_FLOAT3, NonCreatable, Writable, NonDeletable)
"position", ATTR_DOMAIN_INSTANCE, CD_PROP_FLOAT3, NonCreatable, NonDeletable)
{
}
@ -200,7 +200,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
instance_custom_data_access,
make_array_read_attribute<int>,

View File

@ -1137,48 +1137,6 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
}
};
/**
* This provider makes face normals available as a read-only float3 attribute.
*/
class NormalAttributeProvider final : public BuiltinAttributeProvider {
public:
NormalAttributeProvider()
: BuiltinAttributeProvider(
"normal", ATTR_DOMAIN_FACE, CD_PROP_FLOAT3, NonCreatable, Readonly, NonDeletable)
{
}
GVArray try_get_for_read(const void *owner) const final
{
const Mesh *mesh = static_cast<const Mesh *>(owner);
if (mesh == nullptr || mesh->totpoly == 0) {
return {};
}
return VArray<float3>::ForSpan({(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly});
}
GAttributeWriter try_get_for_write(void * /*owner*/) const final
{
return {};
}
bool try_delete(void * /*owner*/) const final
{
return false;
}
bool try_create(void * /*owner*/, const AttributeInit & /*initializer*/) const final
{
return false;
}
bool exists(const void *owner) const final
{
const Mesh *mesh = static_cast<const Mesh *>(owner);
return mesh->totpoly != 0;
}
};
/**
* In this function all the attribute providers for a mesh component are created. Most data in this
* function is statically allocated, because it does not change over time.
@ -1222,21 +1180,17 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
make_array_read_attribute<float3>,
make_array_write_attribute<float3>,
tag_component_positions_changed);
static NormalAttributeProvider normal;
static BuiltinCustomDataLayerProvider id("id",
ATTR_DOMAIN_POINT,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int>,
@ -1255,7 +1209,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
face_access,
make_array_read_attribute<int>,
@ -1269,7 +1222,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_BOOL,
CD_MPOLY,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
face_access,
make_derived_read_attribute<MPoly, bool, get_shade_smooth>,
@ -1281,7 +1233,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_BOOL,
CD_PROP_BOOL,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
edge_access,
make_array_read_attribute<bool>,
@ -1294,7 +1245,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_FLOAT,
CD_CREASE,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
edge_access,
make_array_read_attribute<float>,
@ -1308,7 +1258,7 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static CustomDataAttributeProvider face_custom_data(ATTR_DOMAIN_FACE, face_access);
return ComponentAttributeProviders(
{&position, &id, &material_index, &shade_smooth, &sharp_edge, &normal, &crease},
{&position, &id, &material_index, &shade_smooth, &sharp_edge, &crease},
{&corner_custom_data,
&vertex_groups,
&point_custom_data,

View File

@ -142,7 +142,6 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
make_array_read_attribute<float3>,
@ -153,7 +152,6 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
@ -164,7 +162,6 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int>,

View File

@ -190,6 +190,9 @@ static int stroke_march_next_point(const bGPDstroke *gps,
float *pressure,
float *strength,
float *vert_color,
float *uv_fac,
float *uv_fill,
float *uv_rot,
float *ratio_result,
int *index_from,
int *index_to)
@ -271,6 +274,10 @@ static int stroke_march_next_point(const bGPDstroke *gps,
gps->points[next_point_index].pressure, gps->points[*index_from].pressure, vratio);
*strength = interpf(
gps->points[next_point_index].strength, gps->points[*index_from].strength, vratio);
*uv_fac = interpf(gps->points[next_point_index].uv_fac, gps->points[*index_from].uv_fac, vratio);
*uv_rot = interpf(gps->points[next_point_index].uv_rot, gps->points[*index_from].uv_rot, vratio);
interp_v2_v2v2(
uv_fill, gps->points[*index_from].uv_fill, gps->points[next_point_index].uv_fill, vratio);
interp_v4_v4v4(vert_color,
gps->points[*index_from].vert_color,
gps->points[next_point_index].vert_color,
@ -474,6 +481,7 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd,
int next_point_index = 1;
int i = 0;
float pressure, strength, ratio_result;
float uv_fac, uv_rot, uv_fill[2];
float vert_color[4];
int index_from, index_to;
float last_coord[3];
@ -504,6 +512,9 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd,
&pressure,
&strength,
vert_color,
&uv_fac,
uv_fill,
&uv_rot,
&ratio_result,
&index_from,
&index_to)) > -1) {
@ -514,6 +525,10 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd,
copy_v3_v3(&pt2->x, last_coord);
new_pt[i].pressure = pressure;
new_pt[i].strength = strength;
new_pt[i].uv_fac = uv_fac;
new_pt[i].uv_rot = uv_rot;
copy_v2_v2(new_pt[i].uv_fill, uv_fill);
memcpy(new_pt[i].vert_color, vert_color, sizeof(float[4]));
if (select) {
new_pt[i].flag |= GP_SPOINT_SELECT;

View File

@ -17,7 +17,6 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "BLI_bit_vector.hh"
#include "BLI_bounds.hh"
#include "BLI_edgehash.h"
#include "BLI_endian_switch.h"
@ -66,7 +65,6 @@
#include "BLO_read_write.h"
using blender::BitVector;
using blender::float3;
using blender::MutableSpan;
using blender::Span;

View File

@ -316,7 +316,7 @@ void BKE_mesh_foreach_mapped_subdiv_face_center(
BKE_mesh_vertex_normals_ensure(mesh) :
nullptr;
const int *index = static_cast<const int *>(CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX));
const blender::BitVector<> &facedot_tags = mesh->runtime->subsurf_face_dot_tags;
const blender::BitSpan facedot_tags = mesh->runtime->subsurf_face_dot_tags;
if (index) {
for (int i = 0; i < mesh->totpoly; i++, mp++) {

View File

@ -1558,7 +1558,6 @@ void BKE_mesh_legacy_convert_uvs_to_struct(
{
using namespace blender;
using namespace blender::bke;
const AttributeAccessor attributes = mesh->attributes();
Vector<CustomDataLayer, 16> new_layer_to_write;
/* Don't write the boolean UV map sublayers which will be written in the legacy #MLoopUV type. */
@ -1591,24 +1590,30 @@ void BKE_mesh_legacy_convert_uvs_to_struct(
mloopuv_layer.data = mloopuv.data();
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
const VArray<bool> vert_selection = attributes.lookup_or_default<bool>(
BKE_uv_map_vert_select_name_get(layer.name, buffer), ATTR_DOMAIN_CORNER, false);
const VArray<bool> edge_selection = attributes.lookup_or_default<bool>(
BKE_uv_map_edge_select_name_get(layer.name, buffer), ATTR_DOMAIN_CORNER, false);
const VArray<bool> pin = attributes.lookup_or_default<bool>(
BKE_uv_map_pin_name_get(layer.name, buffer), ATTR_DOMAIN_CORNER, false);
const bool *vert_selection = static_cast<const bool *>(CustomData_get_layer_named(
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_vert_select_name_get(layer.name, buffer)));
const bool *edge_selection = static_cast<const bool *>(CustomData_get_layer_named(
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_edge_select_name_get(layer.name, buffer)));
const bool *pin = static_cast<const bool *>(CustomData_get_layer_named(
&mesh->ldata, CD_PROP_BOOL, BKE_uv_map_pin_name_get(layer.name, buffer)));
threading::parallel_for(mloopuv.index_range(), 2048, [&](IndexRange range) {
for (const int i : range) {
copy_v2_v2(mloopuv[i].uv, coords[i]);
SET_FLAG_FROM_TEST(mloopuv[i].flag, vert_selection[i], MLOOPUV_VERTSEL);
SET_FLAG_FROM_TEST(mloopuv[i].flag, edge_selection[i], MLOOPUV_EDGESEL);
SET_FLAG_FROM_TEST(mloopuv[i].flag, pin[i], MLOOPUV_PINNED);
SET_FLAG_FROM_TEST(mloopuv[i].flag, vert_selection && vert_selection[i], MLOOPUV_VERTSEL);
SET_FLAG_FROM_TEST(mloopuv[i].flag, edge_selection && edge_selection[i], MLOOPUV_EDGESEL);
SET_FLAG_FROM_TEST(mloopuv[i].flag, pin && pin[i], MLOOPUV_PINNED);
}
});
new_layer_to_write.append(mloopuv_layer);
}
/* #CustomData expects the layers to be sorted in increasing order based on type. */
std::stable_sort(
new_layer_to_write.begin(),
new_layer_to_write.end(),
[](const CustomDataLayer &a, const CustomDataLayer &b) { return a.type < b.type; });
loop_layers_to_write = new_layer_to_write;
mesh->ldata.totlayer = new_layer_to_write.size();
mesh->ldata.maxlayer = mesh->ldata.totlayer;

View File

@ -42,6 +42,7 @@
using blender::BitVector;
using blender::float3;
using blender::int2;
using blender::MutableBitSpan;
using blender::MutableSpan;
using blender::short2;
using blender::Span;
@ -1238,7 +1239,7 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const Span<MLoop> mloop
const Span<int2> edge_to_loops,
const Span<int> loop_to_poly,
const int *e2l_prev,
BitVector<> &skip_loops,
MutableBitSpan skip_loops,
const int ml_curr_index,
const int ml_prev_index,
const int mp_curr_index)

View File

@ -91,7 +91,7 @@ void BKE_nlastrip_free(NlaStrip *strip, const bool do_id_user)
MEM_freeN(strip);
}
void BKE_nlatrack_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user)
void BKE_nlatrack_free(NlaTrack *nlt, const bool do_id_user)
{
NlaStrip *strip, *stripn;
@ -107,12 +107,7 @@ void BKE_nlatrack_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user)
}
/* free NLA track itself now */
if (tracks) {
BLI_freelinkN(tracks, nlt);
}
else {
MEM_freeN(nlt);
}
MEM_freeN(nlt);
}
void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user)
@ -127,7 +122,7 @@ void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user)
/* free tracks one by one */
for (nlt = tracks->first; nlt; nlt = nltn) {
nltn = nlt->next;
BKE_nlatrack_free(tracks, nlt, do_id_user);
BKE_nlatrack_remove_and_free(tracks, nlt, do_id_user);
}
/* clear the list's pointers to be safe */
@ -514,6 +509,20 @@ void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data)
}
}
/* Removing ------------------------------------------ */
void BKE_nlatrack_remove(ListBase *tracks, struct NlaTrack *nlt)
{
BLI_assert(tracks);
BLI_remlink(tracks, nlt);
}
void BKE_nlatrack_remove_and_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user)
{
BKE_nlatrack_remove(tracks, nlt);
BKE_nlatrack_free(nlt, do_id_user);
}
/* *************************************************** */
/* NLA Evaluation <-> Editing Stuff */

View File

@ -77,18 +77,43 @@ TEST(nla_track, BKE_nlatrack_remove_strip)
strip2.start = 11;
strip2.end = 20;
// Add NLA strips to the NLATrack.
/* Add NLA strips to the NLATrack. */
BKE_nlastrips_add_strip(&strips, &strip1);
BKE_nlastrips_add_strip(&strips, &strip2);
track.strips = strips;
// ensure we have 2 strips in the track.
/* Ensure we have 2 strips in the track. */
EXPECT_EQ(2, BLI_listbase_count(&track.strips));
BKE_nlatrack_remove_strip(&track, &strip2);
EXPECT_EQ(1, BLI_listbase_count(&track.strips));
// ensure the correct strip was removed.
/* Ensure the correct strip was removed. */
EXPECT_EQ(-1, BLI_findindex(&track.strips, &strip2));
}
TEST(nla_track, BKE_nlatrack_remove_and_free)
{
AnimData adt{};
NlaTrack *track1;
NlaTrack *track2;
/* Add NLA tracks to the Animation Data. */
track1 = BKE_nlatrack_add(&adt, NULL, false);
track2 = BKE_nlatrack_add(&adt, track1, false);
/* Ensure we have 2 tracks in the track. */
EXPECT_EQ(2, BLI_listbase_count(&adt.nla_tracks));
BKE_nlatrack_remove_and_free(&adt.nla_tracks, track2, false);
EXPECT_EQ(1, BLI_listbase_count(&adt.nla_tracks));
/* Ensure the correct track was removed. */
EXPECT_EQ(-1, BLI_findindex(&adt.nla_tracks, track2));
/* Free the rest of the tracks, and ensure they are removed. */
BKE_nlatrack_remove_and_free(&adt.nla_tracks, track1, false);
EXPECT_EQ(0, BLI_listbase_count(&adt.nla_tracks));
EXPECT_EQ(-1, BLI_findindex(&adt.nla_tracks, track1));
}
} // namespace blender::bke::tests

View File

@ -2479,6 +2479,8 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
link.tosock->link = &link;
}
Vector<bNodeLink *> duplicate_links_to_remove;
/* redirect downstream links */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
/* do we have internal link? */
@ -2495,7 +2497,7 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
link_to_compare->tosock == link->tosock) {
adjust_multi_input_indices_after_removed_link(
ntree, link_to_compare->tosock, link_to_compare->multi_input_socket_index);
nodeRemLink(ntree, link_to_compare);
duplicate_links_to_remove.append_non_duplicates(link_to_compare);
}
}
}
@ -2533,6 +2535,10 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
}
}
for (bNodeLink *link : duplicate_links_to_remove) {
nodeRemLink(ntree, link);
}
/* remove remaining upstream links */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
if (link->tonode == node) {

View File

@ -508,6 +508,7 @@ static void prepare_inferencing_interfaces(
bool update_field_inferencing(const bNodeTree &tree)
{
BLI_assert(tree.type == NTREE_GEOMETRY);
tree.ensure_topology_cache();
const Span<const bNode *> nodes = tree.all_nodes();

View File

@ -2266,6 +2266,8 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
return pbvh;
}
ob->sculpt->islands_valid = false;
if (ob->sculpt->bm != nullptr) {
/* Sculpting on a BMesh (dynamic-topology) gets a special PBVH. */
pbvh = build_pbvh_for_dynamic_topology(ob);

View File

@ -956,7 +956,9 @@ ScrArea *BKE_screen_area_map_find_area_xy(const ScrAreaMap *areamap,
const int xy[2])
{
LISTBASE_FOREACH (ScrArea *, area, &areamap->areabase) {
if (BLI_rcti_isect_pt_v(&area->totrct, xy)) {
/* Test area's outer screen verts, not inner `area->totrct`. */
if (xy[0] >= area->v1->vec.x && xy[0] <= area->v4->vec.x && xy[1] >= area->v1->vec.y &&
xy[1] <= area->v2->vec.y) {
if (ELEM(spacetype, SPACE_TYPE_ANY, area->spacetype)) {
return area;
}

View File

@ -0,0 +1,234 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bli
*
* This file provides the basis for processing "indexed bits" (i.e. every bit has an index).
* The main purpose of this file is to define how bits are indexed within a memory buffer.
* For example, one has to define whether the first bit is the least or most significant bit and
* how endianness affect the bit order.
*
* The order is defined as follows:
* - Every indexed bit is part of an #BitInt. These ints are ordered by their address as usual.
* - Within each #BitInt, the bits are ordered from least to most significant.
*/
#include "BLI_index_range.hh"
#include "BLI_utildefines.h"
#include <ostream>
namespace blender::bits {
/** Using a large integer type is better because then it's easier to process many bits at once. */
using BitInt = uint64_t;
/** Number of bits that fit into #BitInt. */
static constexpr int64_t BitsPerInt = int64_t(sizeof(BitInt) * 8);
/** Shift amount to get from a bit index to an int index. Equivalent to `log(BitsPerInt, 2)`. */
static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(BitInt) >= 2) + (sizeof(BitInt) >= 4) +
(sizeof(BitInt) >= 8);
/** Bit mask containing a 1 for the last few bits that index a bit inside of an #BitInt. */
static constexpr BitInt BitIndexMask = (BitInt(1) << BitToIntIndexShift) - 1;
inline BitInt mask_first_n_bits(const int64_t n)
{
BLI_assert(n >= 0);
BLI_assert(n <= BitsPerInt);
if (n == BitsPerInt) {
return BitInt(-1);
}
return (BitInt(1) << n) - 1;
}
inline BitInt mask_last_n_bits(const int64_t n)
{
return ~mask_first_n_bits(BitsPerInt - n);
}
inline BitInt mask_range_bits(const int64_t start, const int64_t size)
{
BLI_assert(start >= 0);
BLI_assert(size >= 0);
const int64_t end = start + size;
BLI_assert(end <= BitsPerInt);
if (end == BitsPerInt) {
return mask_last_n_bits(size);
}
return ((BitInt(1) << end) - 1) & ~((BitInt(1) << start) - 1);
}
inline BitInt mask_single_bit(const int64_t bit_index)
{
BLI_assert(bit_index >= 0);
BLI_assert(bit_index < BitsPerInt);
return BitInt(1) << bit_index;
}
inline BitInt *int_containing_bit(BitInt *data, const int64_t bit_index)
{
return data + (bit_index >> BitToIntIndexShift);
}
inline const BitInt *int_containing_bit(const BitInt *data, const int64_t bit_index)
{
return data + (bit_index >> BitToIntIndexShift);
}
/**
* This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but
* not changed.
*/
class BitRef {
private:
/** Points to the exact integer that the bit is in. */
const BitInt *int_;
/** All zeros except for a single one at the bit that is referenced. */
BitInt mask_;
friend class MutableBitRef;
public:
BitRef() = default;
/**
* Reference a specific bit in an array. Note that #data does *not* have to point to the
* exact integer the bit is in.
*/
BitRef(const BitInt *data, const int64_t bit_index)
{
int_ = int_containing_bit(data, bit_index);
mask_ = mask_single_bit(bit_index & BitIndexMask);
}
/**
* Return true when the bit is currently 1 and false otherwise.
*/
bool test() const
{
const BitInt value = *int_;
const BitInt masked_value = value & mask_;
return masked_value != 0;
}
operator bool() const
{
return this->test();
}
};
/**
* Similar to #BitRef, but also allows changing the referenced bit.
*/
class MutableBitRef {
private:
/** Points to the integer that the bit is in. */
BitInt *int_;
/** All zeros except for a single one at the bit that is referenced. */
BitInt mask_;
public:
MutableBitRef() = default;
/**
* Reference a specific bit in an array. Note that #data does *not* have to point to the
* exact int the bit is in.
*/
MutableBitRef(BitInt *data, const int64_t bit_index)
{
int_ = int_containing_bit(data, bit_index);
mask_ = mask_single_bit(bit_index & BitIndexMask);
}
/**
* Support implicitly casting to a read-only #BitRef.
*/
operator BitRef() const
{
BitRef bit_ref;
bit_ref.int_ = int_;
bit_ref.mask_ = mask_;
return bit_ref;
}
/**
* Return true when the bit is currently 1 and false otherwise.
*/
bool test() const
{
const BitInt value = *int_;
const BitInt masked_value = value & mask_;
return masked_value != 0;
}
operator bool() const
{
return this->test();
}
/**
* Change the bit to a 1.
*/
void set()
{
*int_ |= mask_;
}
/**
* Change the bit to a 0.
*/
void reset()
{
*int_ &= ~mask_;
}
/**
* Change the bit to a 1 if #value is true and 0 otherwise. If the value is highly unpredictable
* by the CPU branch predictor, it can be faster to use #set_branchless instead.
*/
void set(const bool value)
{
if (value) {
this->set();
}
else {
this->reset();
}
}
/**
* Does the same as #set, but does not use a branch. This is faster when the input value is
* unpredictable for the CPU branch predictor (best case for this function is a uniform random
* distribution with 50% probability for true and false). If the value is predictable, this is
* likely slower than #set.
*/
void set_branchless(const bool value)
{
const BitInt value_int = BitInt(value);
BLI_assert(ELEM(value_int, 0, 1));
const BitInt old = *int_;
*int_ =
/* Unset bit. */
(~mask_ & old)
/* Optionally set it again. The -1 turns a 1 into `0x00...` and a 0 into `0xff...`. */
| (mask_ & ~(value_int - 1));
}
};
inline std::ostream &operator<<(std::ostream &stream, const BitRef &bit)
{
return stream << (bit ? "1" : "0");
}
inline std::ostream &operator<<(std::ostream &stream, const MutableBitRef &bit)
{
return stream << BitRef(bit);
}
} // namespace blender::bits
namespace blender {
using bits::BitRef;
using bits::MutableBitRef;
} // namespace blender

View File

@ -0,0 +1,290 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_bit_ref.hh"
#include "BLI_index_range.hh"
#include "BLI_memory_utils.hh"
namespace blender::bits {
/** Base class for a const and non-const bit-iterator. */
class BitIteratorBase {
protected:
const BitInt *data_;
int64_t bit_index_;
public:
BitIteratorBase(const BitInt *data, const int64_t bit_index) : data_(data), bit_index_(bit_index)
{
}
BitIteratorBase &operator++()
{
bit_index_++;
return *this;
}
friend bool operator!=(const BitIteratorBase &a, const BitIteratorBase &b)
{
BLI_assert(a.data_ == b.data_);
return a.bit_index_ != b.bit_index_;
}
};
/** Allows iterating over the bits in a memory buffer. */
class BitIterator : public BitIteratorBase {
public:
BitIterator(const BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index)
{
}
BitRef operator*() const
{
return BitRef(data_, bit_index_);
}
};
/** Allows iterating over the bits in a memory buffer. */
class MutableBitIterator : public BitIteratorBase {
public:
MutableBitIterator(BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index)
{
}
MutableBitRef operator*() const
{
return MutableBitRef(const_cast<BitInt *>(data_), bit_index_);
}
};
/**
* Similar to #Span, but references a range of bits instead of normal C++ types (which must be at
* least one byte large). Use #MutableBitSpan if the values are supposed to be modified.
*
* The beginning and end of a #BitSpan does *not* have to be at byte/int boundaries. It can start
* and end at any bit.
*/
class BitSpan {
private:
/** Base pointer to the integers containing the bits. The actual bit span might start at a much
* higher address when `bit_range_.start()` is large. */
const BitInt *data_ = nullptr;
/** The range of referenced bits. */
IndexRange bit_range_ = {0, 0};
public:
/** Construct an empty span. */
BitSpan() = default;
BitSpan(const BitInt *data, const int64_t size_in_bits) : data_(data), bit_range_(size_in_bits)
{
}
BitSpan(const BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range)
{
}
/** Number of bits referenced by the span. */
int64_t size() const
{
return bit_range_.size();
}
bool is_empty() const
{
return bit_range_.is_empty();
}
IndexRange index_range() const
{
return IndexRange(bit_range_.size());
}
BitRef operator[](const int64_t index) const
{
BLI_assert(index >= 0);
BLI_assert(index < bit_range_.size());
return {data_, bit_range_.start() + index};
}
BitSpan slice(const IndexRange range) const
{
return {data_, bit_range_.slice(range)};
}
const BitInt *data() const
{
return data_;
}
const IndexRange &bit_range() const
{
return bit_range_;
}
BitIterator begin() const
{
return {data_, bit_range_.start()};
}
BitIterator end() const
{
return {data_, bit_range_.one_after_last()};
}
};
/** Same as #BitSpan, but also allows modifying the referenced bits. */
class MutableBitSpan {
private:
BitInt *data_ = nullptr;
IndexRange bit_range_ = {0, 0};
public:
MutableBitSpan() = default;
MutableBitSpan(BitInt *data, const int64_t size) : data_(data), bit_range_(size)
{
}
MutableBitSpan(BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range)
{
}
int64_t size() const
{
return bit_range_.size();
}
bool is_empty() const
{
return bit_range_.is_empty();
}
IndexRange index_range() const
{
return IndexRange(bit_range_.size());
}
MutableBitRef operator[](const int64_t index) const
{
BLI_assert(index >= 0);
BLI_assert(index < bit_range_.size());
return {data_, bit_range_.start() + index};
}
MutableBitSpan slice(const IndexRange range) const
{
return {data_, bit_range_.slice(range)};
}
BitInt *data() const
{
return data_;
}
const IndexRange &bit_range() const
{
return bit_range_;
}
MutableBitIterator begin() const
{
return {data_, bit_range_.start()};
}
MutableBitIterator end() const
{
return {data_, bit_range_.one_after_last()};
}
operator BitSpan() const
{
return {data_, bit_range_};
}
/** Sets all referenced bits to 1. */
void set_all()
{
const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt);
{
BitInt &first_int = *int_containing_bit(data_, bit_range_.start());
const BitInt first_int_mask = mask_range_bits(ranges.prefix.start() & BitIndexMask,
ranges.prefix.size());
first_int |= first_int_mask;
}
{
BitInt *start = int_containing_bit(data_, ranges.aligned.start());
const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt;
constexpr BitInt fill_value = BitInt(-1);
initialized_fill_n(start, ints_to_fill, fill_value);
}
{
BitInt &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1);
const BitInt last_int_mask = mask_first_n_bits(ranges.suffix.size());
last_int |= last_int_mask;
}
}
/** Sets all referenced bits to 0. */
void reset_all()
{
const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt);
{
BitInt &first_int = *int_containing_bit(data_, bit_range_.start());
const BitInt first_int_mask = mask_range_bits(ranges.prefix.start() & BitIndexMask,
ranges.prefix.size());
first_int &= ~first_int_mask;
}
{
BitInt *start = int_containing_bit(data_, ranges.aligned.start());
const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt;
constexpr BitInt fill_value = 0;
initialized_fill_n(start, ints_to_fill, fill_value);
}
{
BitInt &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1);
const BitInt last_int_mask = mask_first_n_bits(ranges.suffix.size());
last_int &= ~last_int_mask;
}
}
/** Sets all referenced bits to either 0 or 1. */
void set_all(const bool value)
{
if (value) {
this->set_all();
}
else {
this->reset_all();
}
}
/** Same as #set_all to mirror #MutableSpan. */
void fill(const bool value)
{
this->set_all(value);
}
};
inline std::ostream &operator<<(std::ostream &stream, const BitSpan &span)
{
stream << "(Size: " << span.size() << ", ";
for (const BitRef bit : span) {
stream << bit;
}
stream << ")";
return stream;
}
inline std::ostream &operator<<(std::ostream &stream, const MutableBitSpan &span)
{
return stream << BitSpan(span);
}
} // namespace blender::bits
namespace blender {
using bits::BitSpan;
using bits::MutableBitSpan;
} // namespace blender

View File

@ -38,142 +38,11 @@
#include <cstring>
#include "BLI_allocator.hh"
#include "BLI_index_range.hh"
#include "BLI_memory_utils.hh"
#include "BLI_bit_span.hh"
#include "BLI_span.hh"
namespace blender::bits {
/**
* Using a large integer type is better because then it's easier to process many bits at once.
*/
using IntType = uint64_t;
static constexpr int64_t BitsPerInt = int64_t(sizeof(IntType) * 8);
static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(IntType) >= 2) + (sizeof(IntType) >= 4) +
(sizeof(IntType) >= 8);
static constexpr IntType BitIndexMask = (IntType(1) << BitToIntIndexShift) - 1;
/**
* This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but
* not changed.
*/
class BitRef {
private:
/** Points to the integer that the bit is in. */
const IntType *ptr_;
/** All zeros except for a single one at the bit that is referenced. */
IntType mask_;
friend class MutableBitRef;
public:
BitRef() = default;
/**
* Reference a specific bit in an array. Note that #ptr does *not* have to point to the
* exact integer the bit is in.
*/
BitRef(const IntType *ptr, const int64_t bit_index)
{
ptr_ = ptr + (bit_index >> BitToIntIndexShift);
mask_ = IntType(1) << (bit_index & BitIndexMask);
}
/**
* Return true when the bit is currently 1 and false otherwise.
*/
bool test() const
{
const IntType value = *ptr_;
const IntType masked_value = value & mask_;
return masked_value != 0;
}
operator bool() const
{
return this->test();
}
};
/**
* Similar to #BitRef, but also allows changing the referenced bit.
*/
class MutableBitRef {
private:
/** Points to the integer that the bit is in. */
IntType *ptr_;
/** All zeros except for a single one at the bit that is referenced. */
IntType mask_;
public:
MutableBitRef() = default;
/**
* Reference a specific bit in an array. Note that #ptr does *not* have to point to the
* exact int the bit is in.
*/
MutableBitRef(IntType *ptr, const int64_t bit_index)
{
ptr_ = ptr + (bit_index >> BitToIntIndexShift);
mask_ = IntType(1) << IntType(bit_index & BitIndexMask);
}
/**
* Support implicitly casting to a read-only #BitRef.
*/
operator BitRef() const
{
BitRef bit_ref;
bit_ref.ptr_ = ptr_;
bit_ref.mask_ = mask_;
return bit_ref;
}
/**
* Return true when the bit is currently 1 and false otherwise.
*/
bool test() const
{
const IntType value = *ptr_;
const IntType masked_value = value & mask_;
return masked_value != 0;
}
operator bool() const
{
return this->test();
}
/**
* Change the bit to a 1.
*/
void set()
{
*ptr_ |= mask_;
}
/**
* Change the bit to a 0.
*/
void reset()
{
*ptr_ &= ~mask_;
}
/**
* Change the bit to a 1 if #value is true and 0 otherwise.
*/
void set(const bool value)
{
if (value) {
this->set();
}
else {
this->reset();
}
}
};
template<
/**
* Number of bits that can be stored in the vector without doing an allocation.
@ -193,13 +62,13 @@ class BitVector {
static constexpr int64_t IntsInInlineBuffer = required_ints_for_bits(InlineBufferCapacity);
static constexpr int64_t BitsInInlineBuffer = IntsInInlineBuffer * BitsPerInt;
static constexpr int64_t AllocationAlignment = alignof(IntType);
static constexpr int64_t AllocationAlignment = alignof(BitInt);
/**
* Points to the first integer used by the vector. It might point to the memory in the inline
* buffer.
*/
IntType *data_;
BitInt *data_;
/** Current size of the vector in bits. */
int64_t size_in_bits_;
@ -211,7 +80,7 @@ class BitVector {
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
/** Contains the bits as long as the vector is small enough. */
BLI_NO_UNIQUE_ADDRESS TypedBuffer<IntType, IntsInInlineBuffer> inline_buffer_;
BLI_NO_UNIQUE_ADDRESS TypedBuffer<BitInt, IntsInInlineBuffer> inline_buffer_;
public:
BitVector(Allocator allocator = {}) noexcept : allocator_(allocator)
@ -219,7 +88,7 @@ class BitVector {
data_ = inline_buffer_;
size_in_bits_ = 0;
capacity_in_bits_ = BitsInInlineBuffer;
uninitialized_fill_n(data_, IntsInInlineBuffer, IntType(0));
uninitialized_fill_n(data_, IntsInInlineBuffer, BitInt(0));
}
BitVector(NoExceptConstructor, Allocator allocator = {}) noexcept : BitVector(allocator)
@ -236,8 +105,8 @@ class BitVector {
}
else {
/* Allocate a new array because the inline buffer is too small. */
data_ = static_cast<IntType *>(
allocator_.allocate(ints_to_copy * sizeof(IntType), AllocationAlignment, __func__));
data_ = static_cast<BitInt *>(
allocator_.allocate(ints_to_copy * sizeof(BitInt), AllocationAlignment, __func__));
capacity_in_bits_ = ints_to_copy * BitsPerInt;
}
size_in_bits_ = other.size_in_bits_;
@ -303,6 +172,16 @@ class BitVector {
return move_assign_container(*this, std::move(other));
}
operator BitSpan() const
{
return {data_, IndexRange(size_in_bits_)};
}
operator MutableBitSpan()
{
return {data_, IndexRange(size_in_bits_)};
}
/**
* Number of bits in the bit vector.
*/
@ -352,80 +231,24 @@ class BitVector {
size_in_bits_++;
}
class Iterator {
private:
const BitVector *vector_;
int64_t index_;
public:
Iterator(const BitVector &vector, const int64_t index) : vector_(&vector), index_(index)
{
}
Iterator &operator++()
{
index_++;
return *this;
}
friend bool operator!=(const Iterator &a, const Iterator &b)
{
BLI_assert(a.vector_ == b.vector_);
return a.index_ != b.index_;
}
BitRef operator*() const
{
return (*vector_)[index_];
}
};
class MutableIterator {
private:
BitVector *vector_;
int64_t index_;
public:
MutableIterator(BitVector &vector, const int64_t index) : vector_(&vector), index_(index)
{
}
MutableIterator &operator++()
{
index_++;
return *this;
}
friend bool operator!=(const MutableIterator &a, const MutableIterator &b)
{
BLI_assert(a.vector_ == b.vector_);
return a.index_ != b.index_;
}
MutableBitRef operator*() const
{
return (*vector_)[index_];
}
};
Iterator begin() const
BitIterator begin() const
{
return {*this, 0};
return {data_, 0};
}
Iterator end() const
BitIterator end() const
{
return {*this, size_in_bits_};
return {data_, size_in_bits_};
}
MutableIterator begin()
MutableBitIterator begin()
{
return {*this, 0};
return {data_, 0};
}
MutableIterator end()
MutableBitIterator end()
{
return {*this, size_in_bits_};
return {data_, size_in_bits_};
}
/**
@ -441,31 +264,8 @@ class BitVector {
}
size_in_bits_ = new_size_in_bits;
if (old_size_in_bits < new_size_in_bits) {
this->fill_range(IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits), value);
}
}
/**
* Set #value for every element in #range.
*/
void fill_range(const IndexRange range, const bool value)
{
const AlignedIndexRanges aligned_ranges = split_index_range_by_alignment(range, BitsPerInt);
/* Fill first few bits. */
for (const int64_t i : aligned_ranges.prefix) {
(*this)[i].set(value);
}
/* Fill entire ints at once. */
const int64_t start_fill_int_index = aligned_ranges.aligned.start() / BitsPerInt;
const int64_t ints_to_fill = aligned_ranges.aligned.size() / BitsPerInt;
const IntType fill_value = value ? IntType(-1) : IntType(0);
initialized_fill_n(data_ + start_fill_int_index, ints_to_fill, fill_value);
/* Fill bits in the end that don't cover a full int. */
for (const int64_t i : aligned_ranges.suffix) {
(*this)[i].set(value);
MutableBitSpan(data_, IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits))
.set_all(value);
}
}
@ -474,7 +274,7 @@ class BitVector {
*/
void fill(const bool value)
{
this->fill_range(IndexRange(0, size_in_bits_), value);
MutableBitSpan(data_, size_in_bits_).set_all(value);
}
/**
@ -517,7 +317,7 @@ class BitVector {
}
BLI_NOINLINE void realloc_to_at_least(const int64_t min_capacity_in_bits,
const IntType initial_value_for_new_ints = 0x00)
const BitInt initial_value_for_new_ints = 0)
{
if (capacity_in_bits_ >= min_capacity_in_bits) {
return;
@ -531,8 +331,8 @@ class BitVector {
const int64_t new_capacity_in_ints = std::max(min_capacity_in_ints, min_new_capacity_in_ints);
const int64_t ints_to_copy = this->used_ints_amount();
IntType *new_data = static_cast<IntType *>(allocator_.allocate(
new_capacity_in_ints * sizeof(IntType), AllocationAlignment, __func__));
BitInt *new_data = static_cast<BitInt *>(
allocator_.allocate(new_capacity_in_ints * sizeof(BitInt), AllocationAlignment, __func__));
uninitialized_copy_n(data_, ints_to_copy, new_data);
/* Always initialize new capacity even if it isn't used yet. That's necessary to avoid warnings
* caused by using uninitialized memory. This happens when e.g. setting a clearing a bit in an
@ -562,7 +362,5 @@ class BitVector {
} // namespace blender::bits
namespace blender {
using bits::BitRef;
using bits::BitVector;
using bits::MutableBitRef;
} // namespace blender

View File

@ -142,6 +142,18 @@ double BLI_dir_free_space(const char *dir) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(
*/
char *BLI_current_working_dir(char *dir, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
eFileAttributes BLI_file_attributes(const char *path);
/**
* Changes the current working directory to the provided path.
*
* Usage of this function is strongly discouraged as it is not thread safe. It will likely cause
* issues if there is an operation on another thread that does not expect the current working
* directory to change. This has been added to support USDZ export, which has a problematic
* "feature" described in this issue https://projects.blender.org/blender/blender/issues/99807. It
* will be removed if it is possible to resolve that issue upstream in the USD library.
*
* \return true on success, false otherwise.
*/
bool BLI_change_working_dir(const char *dir);
/** \} */

View File

@ -304,6 +304,13 @@ void rotate_eul(float beul[3], char axis, float angle);
/* Order independent. */
/**
* Manipulate `eul` so it's close to `oldrot` while representing the same rotation
* with the aim of having the minimum difference between all axes.
*
* This is typically done so interpolating the values between two euler rotations
* doesn't add undesired rotation (even rotating multiple times around one axis).
*/
void compatible_eul(float eul[3], const float oldrot[3]);
void add_eul_euleul(float r_eul[3], float a[3], float b[3], short order);

View File

@ -457,6 +457,8 @@ if(WITH_GTESTS)
tests/BLI_array_store_test.cc
tests/BLI_array_test.cc
tests/BLI_array_utils_test.cc
tests/BLI_bit_ref_test.cc
tests/BLI_bit_span_test.cc
tests/BLI_bit_vector_test.cc
tests/BLI_bitmap_test.cc
tests/BLI_bounds_test.cc

View File

@ -1490,15 +1490,18 @@ void rotate_eul(float beul[3], const char axis, const float angle)
void compatible_eul(float eul[3], const float oldrot[3])
{
/* we could use M_PI as pi_thresh: which is correct but 5.1 gives better results.
* Checked with baking actions to fcurves - campbell */
const float pi_thresh = (5.1f);
/* When the rotation exceeds 180 degrees, it can be wrapped by 360 degrees
* to produce a closer match.
* NOTE: Values between `pi` & `2 * pi` work, where `pi` has the lowest number of
* discontinuities and values approaching `2 * pi` center the resulting rotation around zero,
* at the expense of the result being less compatible, see !104856. */
const float pi_thresh = (float)M_PI;
const float pi_x2 = (2.0f * (float)M_PI);
float deul[3];
uint i;
/* correct differences of about 360 degrees first */
/* Correct differences around 360 degrees first. */
for (i = 0; i < 3; i++) {
deul[i] = eul[i] - oldrot[i];
if (deul[i] > pi_thresh) {
@ -1511,29 +1514,17 @@ void compatible_eul(float eul[3], const float oldrot[3])
}
}
/* is 1 of the axis rotations larger than 180 degrees and the other small? NO ELSE IF!! */
if (fabsf(deul[0]) > 3.2f && fabsf(deul[1]) < 1.6f && fabsf(deul[2]) < 1.6f) {
if (deul[0] > 0.0f) {
eul[0] -= pi_x2;
}
else {
eul[0] += pi_x2;
}
}
if (fabsf(deul[1]) > 3.2f && fabsf(deul[2]) < 1.6f && fabsf(deul[0]) < 1.6f) {
if (deul[1] > 0.0f) {
eul[1] -= pi_x2;
}
else {
eul[1] += pi_x2;
}
}
if (fabsf(deul[2]) > 3.2f && fabsf(deul[0]) < 1.6f && fabsf(deul[1]) < 1.6f) {
if (deul[2] > 0.0f) {
eul[2] -= pi_x2;
}
else {
eul[2] += pi_x2;
uint j = 1, k = 2;
for (i = 0; i < 3; j = k, k = i++) {
/* Check if this axis of rotations larger than 180 degrees and
* the others are smaller than 90 degrees. */
if (fabsf(deul[i]) > M_PI && fabsf(deul[j]) < M_PI_2 && fabsf(deul[k]) < M_PI_2) {
if (deul[i] > 0.0f) {
eul[i] -= pi_x2;
}
else {
eul[i] += pi_x2;
}
}
}
}

View File

@ -54,11 +54,36 @@
#include "BLI_linklist.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
#if !defined(__APPLE__)
/* The implementation for Apple lives in storage_apple.mm.*/
bool BLI_change_working_dir(const char *dir)
{
BLI_assert(BLI_thread_is_main());
if (!BLI_is_dir(dir)) {
return false;
}
# if defined(WIN32)
wchar_t wdir[FILE_MAX];
if (conv_utf_8_to_16(dir, wdir, ARRAY_SIZE(wdir)) != 0) {
return false;
}
return _wchdir(wdir) == 0;
# else
int result = chdir(dir);
if (result == 0) {
BLI_setenv("PWD", dir);
}
return result == 0;
# endif
}
char *BLI_current_working_dir(char *dir, const size_t maxncpy)
{
#if defined(WIN32)
# if defined(WIN32)
wchar_t path[MAX_PATH];
if (_wgetcwd(path, MAX_PATH)) {
if (BLI_strncpy_wchar_as_utf8(dir, path, maxncpy) != maxncpy) {
@ -66,7 +91,7 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy)
}
}
return NULL;
#else
# else
const char *pwd = BLI_getenv("PWD");
if (pwd) {
size_t srclen = BLI_strnlen(pwd, maxncpy);
@ -77,8 +102,9 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy)
return NULL;
}
return getcwd(dir, maxncpy);
#endif
# endif
}
#endif /* !defined (__APPLE__) */
double BLI_dir_free_space(const char *dir)
{

View File

@ -13,6 +13,8 @@
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
/* Extended file attribute used by OneDrive to mark placeholder files. */
static const char *ONEDRIVE_RECALLONOPEN_ATTRIBUTE = "com.microsoft.OneDrive.RecallOnOpen";
@ -185,3 +187,27 @@ const char *BLI_expand_tilde(const char *path_with_tilde)
}
return path_expanded;
}
char *BLI_current_working_dir(char *dir, const size_t maxncpy) {
/* Can't just copy to the *dir pointer, as [path getCString gets grumpy.*/
static char path_expanded[PATH_MAX];
@autoreleasepool {
NSString *path = [[NSFileManager defaultManager] currentDirectoryPath];
const size_t length = maxncpy > PATH_MAX ? PATH_MAX : maxncpy;
[path getCString:path_expanded maxLength:length encoding:NSUTF8StringEncoding];
BLI_strncpy(dir, path_expanded, maxncpy);
return path_expanded;
}
}
bool BLI_change_working_dir(const char* dir) {
@autoreleasepool {
NSString* path = [[NSString alloc] initWithUTF8String: dir];
if ([[NSFileManager defaultManager] changeCurrentDirectoryPath: path] == YES) {
return false;
}
else {
return true;
}
}
}

View File

@ -0,0 +1,160 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include <array>
#include "BLI_bit_ref.hh"
#include "testing/testing.h"
namespace blender::bits::tests {
TEST(bit_ref, MaskFirstNBits)
{
EXPECT_EQ(mask_first_n_bits(0), 0);
EXPECT_EQ(mask_first_n_bits(1),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001);
EXPECT_EQ(mask_first_n_bits(5),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111);
EXPECT_EQ(mask_first_n_bits(63),
0b0111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
EXPECT_EQ(mask_first_n_bits(64),
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
}
TEST(bit_ref, MaskLastNBits)
{
EXPECT_EQ(mask_last_n_bits(0),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_last_n_bits(1),
0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_last_n_bits(5),
0b1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_last_n_bits(63),
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1110);
EXPECT_EQ(mask_last_n_bits(64),
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
}
TEST(bit_ref, MaskSingleBit)
{
EXPECT_EQ(mask_single_bit(0), 1);
EXPECT_EQ(mask_single_bit(1),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010);
EXPECT_EQ(mask_single_bit(5),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000);
EXPECT_EQ(mask_single_bit(63),
0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
}
TEST(bit_ref, IntContainingBit)
{
std::array<uint64_t, 5> array;
uint64_t *data = array.data();
EXPECT_EQ(int_containing_bit(data, 0), data);
EXPECT_EQ(int_containing_bit(data, 1), data);
EXPECT_EQ(int_containing_bit(data, 63), data);
EXPECT_EQ(int_containing_bit(data, 64), data + 1);
EXPECT_EQ(int_containing_bit(data, 65), data + 1);
EXPECT_EQ(int_containing_bit(data, 100), data + 1);
EXPECT_EQ(int_containing_bit(data, 127), data + 1);
EXPECT_EQ(int_containing_bit(data, 128), data + 2);
const uint64_t *data_const = data;
EXPECT_EQ(int_containing_bit(data_const, 0), data_const);
EXPECT_EQ(int_containing_bit(data_const, 1), data_const);
EXPECT_EQ(int_containing_bit(data_const, 63), data_const);
EXPECT_EQ(int_containing_bit(data_const, 64), data_const + 1);
EXPECT_EQ(int_containing_bit(data_const, 65), data_const + 1);
EXPECT_EQ(int_containing_bit(data_const, 100), data_const + 1);
EXPECT_EQ(int_containing_bit(data_const, 127), data_const + 1);
EXPECT_EQ(int_containing_bit(data_const, 128), data_const + 2);
}
TEST(bit_ref, Test)
{
uint64_t data = (1 << 3) | (1 << 7);
EXPECT_FALSE(BitRef(&data, 0).test());
EXPECT_FALSE(BitRef(&data, 1).test());
EXPECT_FALSE(BitRef(&data, 2).test());
EXPECT_TRUE(BitRef(&data, 3).test());
EXPECT_FALSE(BitRef(&data, 4));
EXPECT_FALSE(BitRef(&data, 5));
EXPECT_FALSE(BitRef(&data, 6));
EXPECT_TRUE(BitRef(&data, 7));
EXPECT_FALSE(MutableBitRef(&data, 0).test());
EXPECT_FALSE(MutableBitRef(&data, 1).test());
EXPECT_FALSE(MutableBitRef(&data, 2).test());
EXPECT_TRUE(MutableBitRef(&data, 3).test());
EXPECT_FALSE(MutableBitRef(&data, 4));
EXPECT_FALSE(MutableBitRef(&data, 5));
EXPECT_FALSE(MutableBitRef(&data, 6));
EXPECT_TRUE(MutableBitRef(&data, 7));
}
TEST(bit_ref, Set)
{
uint64_t data = 0;
MutableBitRef(&data, 0).set();
MutableBitRef(&data, 1).set();
MutableBitRef(&data, 1).set();
MutableBitRef(&data, 4).set();
EXPECT_EQ(data, (1 << 0) | (1 << 1) | (1 << 4));
MutableBitRef(&data, 5).set(true);
MutableBitRef(&data, 1).set(false);
EXPECT_EQ(data, (1 << 0) | (1 << 4) | (1 << 5));
}
TEST(bit_ref, Reset)
{
uint64_t data = -1;
MutableBitRef(&data, 0).reset();
MutableBitRef(&data, 2).reset();
EXPECT_EQ(data, uint64_t(-1) & ~(1 << 0) & ~(1 << 2));
}
TEST(bit_ref, SetBranchless)
{
uint64_t data = 0;
MutableBitRef(&data, 0).set_branchless(true);
EXPECT_EQ(data, 1);
MutableBitRef(&data, 0).set_branchless(false);
EXPECT_EQ(data, 0);
MutableBitRef(&data, 3).set_branchless(false);
MutableBitRef(&data, 4).set_branchless(true);
EXPECT_EQ(data, 16);
MutableBitRef(&data, 3).set_branchless(true);
MutableBitRef(&data, 4).set_branchless(true);
EXPECT_EQ(data, 24);
}
TEST(bit_ref, Cast)
{
uint64_t data = 0;
MutableBitRef mutable_ref(&data, 3);
BitRef ref = mutable_ref;
EXPECT_FALSE(ref);
mutable_ref.set();
EXPECT_TRUE(ref);
}
TEST(bit_ref, MaskRangeBits)
{
EXPECT_EQ(mask_range_bits(0, 0),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_range_bits(0, 1),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001);
EXPECT_EQ(mask_range_bits(0, 5),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111);
EXPECT_EQ(mask_range_bits(64, 0),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_range_bits(63, 1),
0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_range_bits(59, 5),
0b1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
EXPECT_EQ(mask_range_bits(8, 3),
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0111'0000'0000);
EXPECT_EQ(mask_range_bits(0, 64),
0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
}
} // namespace blender::bits::tests

View File

@ -0,0 +1,139 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include <array>
#include "BLI_bit_span.hh"
#include "testing/testing.h"
namespace blender::bits::tests {
TEST(bit_span, DefaultConstructor)
{
{
char buffer[sizeof(BitSpan)];
memset(buffer, 0xff, sizeof(BitSpan));
BitSpan &span = *new (buffer) BitSpan();
EXPECT_TRUE(span.is_empty());
EXPECT_EQ(span.size(), 0);
}
{
char buffer[sizeof(MutableBitSpan)];
memset(buffer, 0xff, sizeof(MutableBitSpan));
MutableBitSpan &span = *new (buffer) MutableBitSpan();
EXPECT_TRUE(span.is_empty());
EXPECT_EQ(span.size(), 0);
}
}
TEST(bit_span, Iteration)
{
uint64_t data = (1 << 2) | (1 << 3);
const BitSpan span(&data, 30);
EXPECT_EQ(span.size(), 30);
int index = 0;
for (const BitRef bit : span) {
EXPECT_EQ(bit.test(), ELEM(index, 2, 3));
index++;
}
}
TEST(bit_span, MutableIteration)
{
uint64_t data = 0;
MutableBitSpan span(&data, 40);
EXPECT_EQ(span.size(), 40);
int index = 0;
for (MutableBitRef bit : span) {
bit.set(index % 4 == 0);
index++;
}
EXPECT_EQ(data,
0b0000'0000'0000'0000'0000'0000'0001'0001'0001'0001'0001'0001'0001'0001'0001'0001);
}
TEST(bit_span, SubscriptOperator)
{
uint64_t data[2] = {0, 0};
MutableBitSpan mutable_span(data, 128);
BitSpan span = mutable_span;
EXPECT_EQ(mutable_span.data(), data);
EXPECT_EQ(mutable_span.bit_range(), IndexRange(128));
EXPECT_EQ(span.data(), data);
EXPECT_EQ(span.bit_range(), IndexRange(128));
EXPECT_FALSE(mutable_span[5].test());
EXPECT_FALSE(span[5].test());
mutable_span[5].set(5);
EXPECT_TRUE(mutable_span[5].test());
EXPECT_TRUE(span[5].test());
EXPECT_FALSE(mutable_span[120].test());
EXPECT_FALSE(span[120].test());
mutable_span[120].set(120);
EXPECT_TRUE(mutable_span[120].test());
EXPECT_TRUE(span[120].test());
EXPECT_EQ(data[0],
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000);
EXPECT_EQ(data[1],
0b0000'0001'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000);
}
TEST(bit_span, RangeConstructor)
{
uint64_t data = 0;
MutableBitSpan mutable_span(&data, IndexRange(4, 3));
BitSpan span = mutable_span;
EXPECT_FALSE(mutable_span[1].test());
EXPECT_FALSE(span[1].test());
mutable_span[0].set(true);
mutable_span[1].set(true);
mutable_span[2].set(true);
mutable_span[0].set(false);
mutable_span[2].set(false);
EXPECT_TRUE(mutable_span[1].test());
EXPECT_TRUE(span[1].test());
EXPECT_EQ(data,
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000);
}
TEST(bit_span, Set)
{
uint64_t data = 0;
MutableBitSpan(&data, 64).set_all(true);
EXPECT_EQ(data, uint64_t(-1));
MutableBitSpan(&data, 64).set_all(false);
EXPECT_EQ(data, uint64_t(0));
MutableBitSpan(&data, IndexRange(4, 8)).set_all(true);
EXPECT_EQ(data,
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'1111'0000);
MutableBitSpan(&data, IndexRange(8, 30)).set_all(false);
EXPECT_EQ(data,
0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'0000);
}
TEST(bit_span, SetSliced)
{
std::array<uint64_t, 10> data;
memset(data.data(), 0, sizeof(data));
MutableBitSpan span{data.data(), 640};
span.slice(IndexRange(5, 500)).set_all(true);
for (const int64_t i : IndexRange(640)) {
EXPECT_EQ(span[i], i >= 5 && i < 505);
}
span.slice(IndexRange(10, 190)).set_all(false);
for (const int64_t i : IndexRange(640)) {
EXPECT_EQ(span[i], (i >= 5 && i < 10) || (i >= 200 && i < 505));
}
}
} // namespace blender::bits::tests

View File

@ -6,7 +6,7 @@
#include "testing/testing.h"
namespace blender::tests {
namespace blender::bits::tests {
TEST(bit_vector, DefaultConstructor)
{
@ -183,4 +183,4 @@ TEST(bit_vector, AppendMany)
EXPECT_TRUE(vec[5]);
}
} // namespace blender::tests
} // namespace blender::bits::tests

View File

@ -1,11 +1,27 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include "BLI_fileops.hh"
#include "testing/testing.h"
#include "BLI_fileops.hh"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_threads.h"
namespace blender::tests {
class ChangeWorkingDirectoryTest : public testing::Test {
public:
std::string test_temp_dir;
void TearDown() override
{
if (!test_temp_dir.empty()) {
BLI_delete(test_temp_dir.c_str(), true, false);
}
BLI_threadapi_exit();
}
};
TEST(fileops, fstream_open_string_filename)
{
const std::string test_files_dir = blender::tests::flags_test_asset_dir();
@ -37,4 +53,43 @@ TEST(fileops, fstream_open_charptr_filename)
/* Reading the file not tested here. That's deferred to `std::fstream` anyway. */
}
TEST_F(ChangeWorkingDirectoryTest, change_working_directory)
{
/* Must use because BLI_change_working_dir() checks that we are on the main thread. */
BLI_threadapi_init();
char original_wd[FILE_MAX];
if (!BLI_current_working_dir(original_wd, FILE_MAX)) {
FAIL() << "unable to get the current working directory";
}
const std::string temp_file_name(std::tmpnam(nullptr));
test_temp_dir = temp_file_name + "овый";
if (BLI_exists(test_temp_dir.c_str())) {
BLI_delete(test_temp_dir.c_str(), true, false);
}
ASSERT_FALSE(BLI_change_working_dir(test_temp_dir.c_str()))
<< "changing directory to a non-existent directory is expected to fail.";
ASSERT_TRUE(BLI_dir_create_recursive(test_temp_dir.c_str()))
<< "temporary directory should have been created successfully.";
ASSERT_TRUE(BLI_change_working_dir(test_temp_dir.c_str()))
<< "temporary directory should succeed changing directory.";
char cwd[FILE_MAX];
if (!BLI_current_working_dir(cwd, FILE_MAX)) {
FAIL() << "unable to get the current working directory";
}
ASSERT_EQ(BLI_path_cmp_normalized(cwd, test_temp_dir.c_str()), 0)
<< "the path of the current working directory should equal the path of the temporary "
"directory that was created.";
ASSERT_TRUE(BLI_change_working_dir(original_wd))
<< "changing directory back to the original working directory should succeed.";
}
} // namespace blender::tests

View File

@ -290,6 +290,24 @@ TEST(index_range, SplitByAlignment)
EXPECT_EQ(ranges.aligned, IndexRange());
EXPECT_EQ(ranges.suffix, IndexRange());
}
{
AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(64), 64);
EXPECT_EQ(ranges.prefix, IndexRange());
EXPECT_EQ(ranges.aligned, IndexRange(64));
EXPECT_EQ(ranges.suffix, IndexRange());
}
{
AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(64, 64), 64);
EXPECT_EQ(ranges.prefix, IndexRange());
EXPECT_EQ(ranges.aligned, IndexRange(64, 64));
EXPECT_EQ(ranges.suffix, IndexRange());
}
{
AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(4, 8), 64);
EXPECT_EQ(ranges.prefix, IndexRange(4, 8));
EXPECT_EQ(ranges.aligned, IndexRange());
EXPECT_EQ(ranges.suffix, IndexRange());
}
}
} // namespace blender::tests

View File

@ -770,7 +770,11 @@ void blo_do_versions_userdef(UserDef *userdef)
/* Set GPU backend to OpenGL. */
if (!USER_VERSION_ATLEAST(305, 5)) {
#ifdef __APPLE__
userdef->gpu_backend = GPU_BACKEND_METAL;
#else
userdef->gpu_backend = GPU_BACKEND_OPENGL;
#endif
}
if (!USER_VERSION_ATLEAST(305, 10)) {

View File

@ -126,6 +126,10 @@ const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid
#define BLT_I18NCONTEXT_EDITOR_VIEW3D "View3D"
#define BLT_I18NCONTEXT_EDITOR_FILEBROWSER "File browser"
/* Generic contexts. */
#define BLT_I18NCONTEXT_VIRTUAL_REALITY "Virtual reality"
#define BLT_I18NCONTEXT_CONSTRAINT "Constraint"
/* Helper for bpy.app.i18n object... */
typedef struct {
const char *c_id;
@ -190,6 +194,8 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "id_windowmanager"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_VIEW3D, "editor_view3d"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_FILEBROWSER, "editor_filebrowser"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_VIRTUAL_REALITY, "virtual_reality"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_CONSTRAINT, "constraint"), \
{ \
NULL, NULL, NULL \
} \

View File

@ -211,7 +211,7 @@ void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
int BM_iter_mesh_bitmap_from_filter(const char itype,
BMesh *bm,
blender::BitVector<> &bitmap,
blender::MutableBitSpan bitmap,
bool (*test_fn)(BMElem *, void *user_data),
void *user_data)
{
@ -234,7 +234,7 @@ int BM_iter_mesh_bitmap_from_filter(const char itype,
}
int BM_iter_mesh_bitmap_from_filter_tessface(BMesh *bm,
blender::BitVector<> &bitmap,
blender::MutableBitSpan bitmap,
bool (*test_fn)(BMFace *, void *user_data),
void *user_data)
{

View File

@ -21,7 +21,7 @@
#include "BLI_mempool.h"
#ifdef __cplusplus
# include "BLI_bit_vector.hh"
# include "BLI_bit_span.hh"
#endif
#ifdef __cplusplus
@ -228,14 +228,14 @@ void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
int BM_iter_mesh_bitmap_from_filter(char itype,
BMesh *bm,
blender::BitVector<> &bitmap,
blender::MutableBitSpan bitmap,
bool (*test_fn)(BMElem *, void *user_data),
void *user_data);
/**
* Needed when we want to check faces, but return a loop aligned array.
*/
int BM_iter_mesh_bitmap_from_filter_tessface(BMesh *bm,
blender::BitVector<> &bitmap,
blender::MutableBitSpan bitmap,
bool (*test_fn)(BMFace *, void *user_data),
void *user_data);

View File

@ -318,11 +318,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
CustomData_bmesh_merge(&mesh_ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, bm, BM_LOOP);
}
const Vector<MeshToBMeshLayerInfo> vert_info = mesh_to_bm_copy_info_calc(mesh_vdata, bm->vdata);
const Vector<MeshToBMeshLayerInfo> edge_info = mesh_to_bm_copy_info_calc(mesh_edata, bm->edata);
const Vector<MeshToBMeshLayerInfo> poly_info = mesh_to_bm_copy_info_calc(mesh_pdata, bm->pdata);
const Vector<MeshToBMeshLayerInfo> loop_info = mesh_to_bm_copy_info_calc(mesh_ldata, bm->ldata);
/* -------------------------------------------------------------------- */
/* Shape Key */
int tot_shape_keys = 0;
@ -407,6 +402,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
}
}
const Vector<MeshToBMeshLayerInfo> vert_info = mesh_to_bm_copy_info_calc(mesh_vdata, bm->vdata);
const Vector<MeshToBMeshLayerInfo> edge_info = mesh_to_bm_copy_info_calc(mesh_edata, bm->edata);
const Vector<MeshToBMeshLayerInfo> poly_info = mesh_to_bm_copy_info_calc(mesh_pdata, bm->pdata);
const Vector<MeshToBMeshLayerInfo> loop_info = mesh_to_bm_copy_info_calc(mesh_ldata, bm->ldata);
if (is_new) {
CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);

View File

@ -338,16 +338,12 @@ class ShadowPunctual : public NonCopyable, NonMovable {
float size_x_, size_y_;
/** Shape type. */
eLightType light_type_;
/** Random position on the light. In world space. */
float3 random_offset_;
/** Light position. */
float3 position_;
/** Near and far clip distances. */
float far_, near_;
/** Number of tile-maps needed to cover the light angular extents. */
int tilemaps_needed_;
/** Visibility cone angle from the light source. */
int cone_aperture_;
public:
ShadowPunctual(ShadowModule &module) : shadows_(module){};

View File

@ -59,9 +59,9 @@ void main()
* IF PrimType == LineList: base_vertex_id = quad_id*2
* IF PrimType == LineStrip: base_vertex_id = quad_id
*
* Note: This is currently used as LineList.
* NOTE: This is currently used as LineList.
*
* Note: Primitive Restart Will not work with this setup as-is. We should avoid using
* NOTE: Primitive Restart Will not work with this setup as-is. We should avoid using
* input primitive types which use restart indices. */
int base_vertex_id = quad_id * 2;

View File

@ -4,20 +4,15 @@
#include "draw_attributes.hh"
/* Return true if the given DRW_AttributeRequest is already in the requests. */
static bool drw_attributes_has_request(const DRW_Attributes *requests, DRW_AttributeRequest req)
static bool drw_attributes_has_request(const DRW_Attributes *requests,
const DRW_AttributeRequest &req)
{
for (int i = 0; i < requests->num_requests; i++) {
const DRW_AttributeRequest src_req = requests->requests[i];
if (src_req.domain != req.domain) {
continue;
const DRW_AttributeRequest &src_req = requests->requests[i];
if (src_req.domain == req.domain && src_req.layer_index == req.layer_index &&
src_req.cd_type == req.cd_type) {
return true;
}
if (src_req.layer_index != req.layer_index) {
continue;
}
if (src_req.cd_type != req.cd_type) {
continue;
}
return true;
}
return false;
}
@ -61,14 +56,15 @@ bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b)
return true;
}
DRW_AttributeRequest *drw_attributes_add_request(DRW_Attributes *attrs,
const char *name,
const eCustomDataType type,
const int layer_index,
const eAttrDomain domain)
void drw_attributes_add_request(DRW_Attributes *attrs,
const char *name,
const eCustomDataType type,
const int layer_index,
const eAttrDomain domain)
{
if (attrs->num_requests >= GPU_MAX_ATTR) {
return nullptr;
if (attrs->num_requests >= GPU_MAX_ATTR ||
drw_attributes_has_request(attrs, {type, layer_index, domain})) {
return;
}
DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests];
@ -77,7 +73,6 @@ DRW_AttributeRequest *drw_attributes_add_request(DRW_Attributes *attrs,
req->layer_index = layer_index;
req->domain = domain;
attrs->num_requests += 1;
return req;
}
bool drw_custom_data_match_attribute(const CustomData *custom_data,

View File

@ -60,11 +60,11 @@ void drw_attributes_merge(DRW_Attributes *dst,
/* Return true if all requests in b are in a. */
bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b);
DRW_AttributeRequest *drw_attributes_add_request(DRW_Attributes *attrs,
const char *name,
eCustomDataType data_type,
int layer_index,
eAttrDomain domain);
void drw_attributes_add_request(DRW_Attributes *attrs,
const char *name,
eCustomDataType data_type,
int layer_index,
eAttrDomain domain);
bool drw_custom_data_match_attribute(const CustomData *custom_data,
const char *name,

View File

@ -557,7 +557,7 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
const bool mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false;
if (mr->use_subsurf_fdots) {
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;

View File

@ -46,7 +46,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
if (mr->use_subsurf_fdots) {
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;

View File

@ -102,7 +102,7 @@ static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr,
void *_data)
{
MeshExtract_EdgeFac_Data *data = static_cast<MeshExtract_EdgeFac_Data *>(_data);
const BitVector<> &optimal_display_edges = mr->me->runtime->subsurf_optimal_display_edges;
const BitSpan optimal_display_edges = mr->me->runtime->subsurf_optimal_display_edges;
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;

View File

@ -76,7 +76,7 @@ static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr,
zero_v3(co);
const MLoop *mloop = mr->mloop;
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const int ml_index_end = mp->loopstart + mp->totloop;
for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {

View File

@ -74,7 +74,7 @@ static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr,
void *_data)
{
MeshExtract_FdotUV_Data *data = static_cast<MeshExtract_FdotUV_Data *>(_data);
const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags;
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;

View File

@ -47,6 +47,7 @@
#include "ED_anim_api.h"
#include "ED_armature.h"
#include "ED_keyframes_edit.h" /* XXX move the select modes out of there! */
#include "ED_markers.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
@ -3638,6 +3639,283 @@ static void ANIM_OT_channel_select_keys(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name View Channel Operator
* \{ */
static void get_normalized_fcurve_bounds(FCurve *fcu,
bAnimContext *ac,
const bAnimListElem *ale,
const bool include_handles,
const float range[2],
rctf *r_bounds)
{
const bool fcu_selection_only = false;
BKE_fcurve_calc_bounds(fcu,
&r_bounds->xmin,
&r_bounds->xmax,
&r_bounds->ymin,
&r_bounds->ymax,
fcu_selection_only,
include_handles,
range);
const short mapping_flag = ANIM_get_normalization_flags(ac);
const float min_height = 0.01f;
const float height = BLI_rctf_size_y(r_bounds);
if (height < min_height) {
r_bounds->ymin -= (min_height - height) / 2;
r_bounds->ymax += (min_height - height) / 2;
}
float offset;
const float unit_fac = ANIM_unit_mapping_get_factor(
ac->scene, ale->id, fcu, mapping_flag, &offset);
r_bounds->ymin = (r_bounds->ymin + offset) * unit_fac;
r_bounds->ymax = (r_bounds->ymax + offset) * unit_fac;
}
static void get_gpencil_bounds(bGPDlayer *gpl, const float range[2], rctf *r_bounds)
{
bool found_start = false;
int start_frame = 0;
int end_frame = 1;
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
if (gpf->framenum < range[0]) {
continue;
}
if (gpf->framenum > range[1]) {
break;
}
if (!found_start) {
start_frame = gpf->framenum;
found_start = true;
}
end_frame = gpf->framenum;
}
r_bounds->xmin = start_frame;
r_bounds->xmax = end_frame;
r_bounds->ymin = 0;
r_bounds->ymax = 1;
}
static bool get_channel_bounds(bAnimContext *ac,
bAnimListElem *ale,
const float range[2],
const bool include_handles,
rctf *r_bounds)
{
bool found_bounds = false;
switch (ale->datatype) {
case ALE_GPFRAME: {
bGPDlayer *gpl = (bGPDlayer *)ale->data;
get_gpencil_bounds(gpl, range, r_bounds);
found_bounds = true;
break;
}
case ALE_FCURVE: {
FCurve *fcu = (FCurve *)ale->key_data;
get_normalized_fcurve_bounds(fcu, ac, ale, include_handles, range, r_bounds);
found_bounds = true;
break;
}
}
return found_bounds;
}
static void get_view_range(Scene *scene, const bool use_preview_range, float r_range[2])
{
if (use_preview_range && scene->r.flag & SCER_PRV_RANGE) {
r_range[0] = scene->r.psfra;
r_range[1] = scene->r.pefra;
}
else {
r_range[0] = -FLT_MAX;
r_range[1] = FLT_MAX;
}
}
/* Pad the given rctf with regions that could block the view.
* For example Markers and Time Scrubbing. */
static void add_region_padding(bContext *C, bAnimContext *ac, rctf *bounds)
{
BLI_rctf_scale(bounds, 1.1f);
const float pad_top = UI_TIME_SCRUB_MARGIN_Y;
const float pad_bottom = BLI_listbase_is_empty(ED_context_get_markers(C)) ?
V2D_SCROLL_HANDLE_HEIGHT :
UI_MARKER_MARGIN_Y;
BLI_rctf_pad_y(bounds, ac->region->winy, pad_bottom, pad_top);
}
/* Find the window region in the bAnimContext area and move it to bounds. */
static void move_graph_view(bContext *C, bAnimContext *ac, rctf *bounds, const int smooth_viewtx)
{
LISTBASE_FOREACH (ARegion *, region, &ac->area->regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
UI_view2d_smooth_view(C, region, bounds, smooth_viewtx);
}
}
}
static int graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
/* Get editor data. */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
ListBase anim_data = {NULL, NULL};
const int filter = (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS | ANIMFILTER_DATA_VISIBLE |
ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
size_t anim_data_length = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
if (anim_data_length == 0) {
WM_report(RPT_WARNING, "No channels to operate on");
return OPERATOR_CANCELLED;
}
float range[2];
const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range");
get_view_range(ac.scene, use_preview_range, range);
rctf bounds = {.xmin = FLT_MAX, .xmax = -FLT_MAX, .ymin = FLT_MAX, .ymax = -FLT_MAX};
bAnimListElem *ale;
const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
bool valid_bounds = false;
for (ale = anim_data.first; ale; ale = ale->next) {
rctf channel_bounds;
const bool found_bounds = get_channel_bounds(
&ac, ale, range, include_handles, &channel_bounds);
if (found_bounds) {
BLI_rctf_union(&bounds, &channel_bounds);
valid_bounds = true;
}
}
if (!valid_bounds) {
ANIM_animdata_freelist(&anim_data);
return OPERATOR_CANCELLED;
}
add_region_padding(C, &ac, &bounds);
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
move_graph_view(C, &ac, &bounds, smooth_viewtx);
ANIM_animdata_freelist(&anim_data);
return OPERATOR_FINISHED;
}
static bool channel_view_poll(bContext *C)
{
return ED_operator_action_active(C) || ED_operator_graphedit_active(C);
}
static void ANIM_OT_channels_view_selected(wmOperatorType *ot)
{
/* Identifiers */
ot->name = "Frame Selected Channels";
ot->idname = "ANIM_OT_channels_view_selected";
ot->description = "Reset viewable area to show the selected channels";
/* API callbacks */
ot->exec = graphkeys_view_selected_channels_exec;
ot->poll = channel_view_poll;
ot->flag = 0;
ot->prop = RNA_def_boolean(ot->srna,
"include_handles",
true,
"Include Handles",
"Include handles of keyframes when calculating extents");
ot->prop = RNA_def_boolean(ot->srna,
"use_preview_range",
true,
"Use Preview Range",
"Ignore frames outside of the preview range");
}
static int graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
bAnimContext ac;
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
ListBase anim_data = {NULL, NULL};
const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS |
ANIMFILTER_LIST_CHANNELS);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
bAnimListElem *ale;
const int channel_index = animchannels_channel_get(&ac, event->mval);
ale = BLI_findlink(&anim_data, channel_index);
if (ale == NULL) {
ANIM_animdata_freelist(&anim_data);
return OPERATOR_CANCELLED;
}
float range[2];
const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range");
get_view_range(ac.scene, use_preview_range, range);
rctf bounds;
const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
const bool found_bounds = get_channel_bounds(&ac, ale, range, include_handles, &bounds);
if (!found_bounds) {
ANIM_animdata_freelist(&anim_data);
return OPERATOR_CANCELLED;
}
add_region_padding(C, &ac, &bounds);
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
move_graph_view(C, &ac, &bounds, smooth_viewtx);
ANIM_animdata_freelist(&anim_data);
return OPERATOR_FINISHED;
}
static void ANIM_OT_channel_view_pick(wmOperatorType *ot)
{
/* Identifiers */
ot->name = "Frame Channel Under Cursor";
ot->idname = "ANIM_OT_channel_view_pick";
ot->description = "Reset viewable area to show the channel under the cursor";
/* API callbacks */
ot->invoke = graphkeys_channel_view_pick_invoke;
ot->poll = channel_view_poll;
ot->flag = 0;
ot->prop = RNA_def_boolean(ot->srna,
"include_handles",
true,
"Include Handles",
"Include handles of keyframes when calculating extents");
ot->prop = RNA_def_boolean(ot->srna,
"use_preview_range",
true,
"Use Preview Range",
"Ignore frames outside of the preview range");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Operator Registration
* \{ */
@ -3657,6 +3935,9 @@ void ED_operatortypes_animchannels(void)
WM_operatortype_append(ANIM_OT_channels_setting_disable);
WM_operatortype_append(ANIM_OT_channels_setting_toggle);
WM_operatortype_append(ANIM_OT_channel_view_pick);
WM_operatortype_append(ANIM_OT_channels_view_selected);
WM_operatortype_append(ANIM_OT_channels_delete);
/* XXX does this need to be a separate operator? */

View File

@ -1429,7 +1429,7 @@ static int separate_exec(bContext *C, wmOperator *op)
/* All curves failed due to the same error. */
if (status.error_vertex_keys) {
BKE_report(op->reports, RPT_ERROR, "Cannot separate curves with vertex keys");
BKE_report(op->reports, RPT_ERROR, "Cannot separate curves with shape keys");
}
else {
BLI_assert(status.error_generic);

View File

@ -23,6 +23,7 @@ set(INC
set(SRC
intern/curves_add.cc
intern/curves_data.cc
intern/curves_edit.cc
intern/curves_ops.cc
intern/curves_selection.cc
intern/curves_undo.cc

View File

@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edcurves
*/
#include "BLI_index_mask_ops.hh"
#include "BKE_curves.hh"
#include "ED_curves.h"
namespace blender::ed::curves {
bool remove_selection(bke::CurvesGeometry &curves, const eAttrDomain selection_domain)
{
const bke::AttributeAccessor attributes = curves.attributes();
const VArray<bool> selection = attributes.lookup_or_default<bool>(
".selection", selection_domain, true);
const int domain_size_orig = attributes.domain_size(selection_domain);
Vector<int64_t> indices;
const IndexMask mask = index_mask_ops::find_indices_from_virtual_array(
selection.index_range(), selection, 4096, indices);
switch (selection_domain) {
case ATTR_DOMAIN_POINT:
curves.remove_points(mask);
break;
case ATTR_DOMAIN_CURVE:
curves.remove_curves(mask);
break;
default:
BLI_assert_unreachable();
}
return attributes.domain_size(selection_domain) != domain_size_orig;
}
} // namespace blender::ed::curves

View File

@ -1008,6 +1008,60 @@ static void CURVES_OT_select_linked(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int select_more_exec(bContext *C, wmOperator * /*op*/)
{
VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
for (Curves *curves_id : unique_curves) {
CurvesGeometry &curves = curves_id->geometry.wrap();
select_adjacent(curves, false);
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
}
return OPERATOR_FINISHED;
}
static void CURVES_OT_select_more(wmOperatorType *ot)
{
ot->name = "Select More";
ot->idname = __func__;
ot->description = "Grow the selection by one point";
ot->exec = select_more_exec;
ot->poll = editable_curves_point_domain_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int select_less_exec(bContext *C, wmOperator * /*op*/)
{
VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
for (Curves *curves_id : unique_curves) {
CurvesGeometry &curves = curves_id->geometry.wrap();
select_adjacent(curves, true);
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
}
return OPERATOR_FINISHED;
}
static void CURVES_OT_select_less(wmOperatorType *ot)
{
ot->name = "Select Less";
ot->idname = __func__;
ot->description = "Shrink the selection by one point";
ot->exec = select_less_exec;
ot->poll = editable_curves_point_domain_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
namespace surface_set {
static bool surface_set_poll(bContext *C)
@ -1097,24 +1151,7 @@ static int delete_exec(bContext *C, wmOperator * /*op*/)
{
for (Curves *curves_id : get_unique_editable_curves(*C)) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const eAttrDomain domain = eAttrDomain(curves_id->selection_domain);
const bke::AttributeAccessor attributes = curves.attributes();
const VArray<bool> selection = attributes.lookup_or_default<bool>(".selection", domain, false);
const int domain_size_orig = attributes.domain_size(domain);
Vector<int64_t> indices;
const IndexMask mask = index_mask_ops::find_indices_from_virtual_array(
selection.index_range(), selection, 4096, indices);
switch (domain) {
case ATTR_DOMAIN_POINT:
curves.remove_points(mask);
break;
case ATTR_DOMAIN_CURVE:
curves.remove_curves(mask);
break;
default:
BLI_assert_unreachable();
}
if (attributes.domain_size(domain) != domain_size_orig) {
if (remove_selection(curves, eAttrDomain(curves_id->selection_domain))) {
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
}
@ -1150,6 +1187,8 @@ void ED_operatortypes_curves()
WM_operatortype_append(CURVES_OT_select_random);
WM_operatortype_append(CURVES_OT_select_end);
WM_operatortype_append(CURVES_OT_select_linked);
WM_operatortype_append(CURVES_OT_select_more);
WM_operatortype_append(CURVES_OT_select_less);
WM_operatortype_append(CURVES_OT_surface_set);
WM_operatortype_append(CURVES_OT_delete);
}

View File

@ -267,6 +267,85 @@ void select_linked(bke::CurvesGeometry &curves)
selection.finish();
}
void select_adjacent(bke::CurvesGeometry &curves, const bool deselect)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
curves, ATTR_DOMAIN_POINT, CD_PROP_BOOL);
const VArray<bool> cyclic = curves.cyclic();
if (deselect) {
invert_selection(selection.span);
}
if (selection.span.type().is<bool>()) {
MutableSpan<bool> selection_typed = selection.span.typed<bool>();
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
/* Handle all cases in the forward direction. */
for (int point_i = points.first(); point_i < points.last(); point_i++) {
if (!selection_typed[point_i] && selection_typed[point_i + 1]) {
selection_typed[point_i] = true;
}
}
/* Handle all cases in the backwards direction. */
for (int point_i = points.last(); point_i > points.first(); point_i--) {
if (!selection_typed[point_i] && selection_typed[point_i - 1]) {
selection_typed[point_i] = true;
}
}
/* Handle cyclic curve case. */
if (cyclic[curve_i]) {
if (selection_typed[points.first()] != selection_typed[points.last()]) {
selection_typed[points.first()] = true;
selection_typed[points.last()] = true;
}
}
}
});
}
else if (selection.span.type().is<float>()) {
MutableSpan<float> selection_typed = selection.span.typed<float>();
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
/* Handle all cases in the forward direction. */
for (int point_i = points.first(); point_i < points.last(); point_i++) {
if ((selection_typed[point_i] == 0.0f) && (selection_typed[point_i + 1] > 0.0f)) {
selection_typed[point_i] = 1.0f;
}
}
/* Handle all cases in the backwards direction. */
for (int point_i = points.last(); point_i > points.first(); point_i--) {
if ((selection_typed[point_i] == 0.0f) && (selection_typed[point_i - 1] > 0.0f)) {
selection_typed[point_i] = 1.0f;
}
}
/* Handle cyclic curve case. */
if (cyclic[curve_i]) {
if (selection_typed[points.first()] != selection_typed[points.last()]) {
selection_typed[points.first()] = 1.0f;
selection_typed[points.last()] = 1.0f;
}
}
}
});
}
if (deselect) {
invert_selection(selection.span);
}
selection.finish();
}
void select_random(bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
uint32_t random_seed,

View File

@ -135,6 +135,11 @@ void select_ends(bke::CurvesGeometry &curves, int amount, bool end_points);
*/
void select_linked(bke::CurvesGeometry &curves);
/**
* (De)select all the adjacent points of the current selected points.
*/
void select_adjacent(bke::CurvesGeometry &curves, bool deselect);
/**
* Select random points or curves.
*
@ -185,5 +190,17 @@ bool select_circle(const ViewContext &vc,
eSelectOp sel_op);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Editing
* \{ */
/**
* Remove (dissolve) selected curves or points based on the ".selection" attribute.
* \returns true if any point or curve was removed.
*/
bool remove_selection(bke::CurvesGeometry &curves, eAttrDomain selection_domain);
/** \} */
} // namespace blender::ed::curves
#endif

View File

@ -239,6 +239,17 @@ enum {
UI_BUT_OVERRIDDEN = 1u << 31u,
};
/** #uiBut.dragflag */
enum {
/** By default only the left part of a button triggers dragging. A questionable design to make
* the icon but not other parts of the button draggable. Set this flag so the entire button can
* be dragged. */
UI_BUT_DRAG_FULL_BUT = (1 << 0),
/* --- Internal flags. --- */
UI_BUT_DRAGPOIN_FREE = (1 << 1),
};
/* Default font size for normal text. */
#define UI_DEFAULT_TEXT_POINTS 11.0f
@ -881,6 +892,9 @@ bool UI_but_flag_is_set(uiBut *but, int flag);
void UI_but_drawflag_enable(uiBut *but, int flag);
void UI_but_drawflag_disable(uiBut *but, int flag);
void UI_but_dragflag_enable(uiBut *but, int flag);
void UI_but_dragflag_disable(uiBut *but, int flag);
void UI_but_disable(uiBut *but, const char *disabled_hint);
void UI_but_type_set_menu_from_pulldown(uiBut *but);
@ -1789,9 +1803,12 @@ void UI_but_drag_set_id(uiBut *but, struct ID *id);
/**
* Set an image to display while dragging. This works for any drag type (`WM_DRAG_XXX`).
* Not to be confused with #UI_but_drag_set_image(), which sets up dragging of an image.
*
* Sets #UI_BUT_DRAG_FULL_BUT so the full button can be dragged.
*/
void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, float scale);
/**
* Sets #UI_BUT_DRAG_FULL_BUT so the full button can be dragged.
* \param asset: May be passed from a temporary variable, drag data only stores a copy of this.
*/
void UI_but_drag_set_asset(uiBut *but,
@ -1808,6 +1825,8 @@ void UI_but_drag_set_name(uiBut *but, const char *name);
* Value from button itself.
*/
void UI_but_drag_set_value(uiBut *but);
/** Sets #UI_BUT_DRAG_FULL_BUT so the full button can be dragged. */
void UI_but_drag_set_image(
uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, bool use_free);

View File

@ -4224,6 +4224,10 @@ static uiBut *ui_def_but(uiBlock *block,
but->flag |= UI_BUT_UNDO;
}
if (ELEM(but->type, UI_BTYPE_COLOR)) {
but->dragflag |= UI_BUT_DRAG_FULL_BUT;
}
BLI_addtail(&block->buttons, but);
if (block->curlayout) {
@ -5890,6 +5894,16 @@ void UI_but_drawflag_disable(uiBut *but, int flag)
but->drawflag &= ~flag;
}
void UI_but_dragflag_enable(uiBut *but, int flag)
{
but->dragflag |= flag;
}
void UI_but_dragflag_disable(uiBut *but, int flag)
{
but->dragflag &= ~flag;
}
void UI_but_disable(uiBut *but, const char *disabled_hint)
{
UI_but_flag_enable(but, UI_BUT_DISABLED);

View File

@ -24,6 +24,7 @@ void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, const float scale)
{
but->imb = imb;
but->imb_scale = scale;
UI_but_dragflag_enable(but, UI_BUT_DRAG_FULL_BUT);
}
void UI_but_drag_set_asset(uiBut *but,

View File

@ -86,11 +86,6 @@ enum {
/* WARNING: rest of #uiBut.flag in UI_interface.h */
};
/** #uiBut.dragflag */
enum {
UI_BUT_DRAGPOIN_FREE = (1 << 0),
};
/** #uiBut.pie_dir */
enum RadialDirection {
UI_RADIAL_NONE = -1,

View File

@ -247,7 +247,7 @@ bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEv
BLI_rcti_rctf_copy(&rect, &but->rect);
if (but->imb || but->type == UI_BTYPE_COLOR) {
if (but->dragflag & UI_BUT_DRAG_FULL_BUT) {
/* use button size itself */
}
else if (but->drawflag & UI_BUT_ICON_LEFT) {

View File

@ -92,15 +92,9 @@ void attribute_search_add_items(StringRefNull str,
StringSearch *search = BLI_string_search_new();
for (const GeometryAttributeInfo *item : infos) {
/* Don't show the legacy "normal" attribute. */
if (item->name == "normal" && item->domain == ATTR_DOMAIN_FACE) {
continue;
}
if (!bke::allow_procedural_attribute_access(item->name)) {
continue;
}
BLI_string_search_add(search, item->name.c_str(), (void *)item, 0);
}

View File

@ -285,7 +285,7 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op)
params.add_solidify = RNA_boolean_get(op->ptr, "add_solidify");
/* Push an undo step prior to extraction.
* Note: A second push happens after the operator due to
* NOTE: A second push happens after the operator due to
* the OPTYPE_UNDO flag; having an initial undo step here
* is just needed to preserve the active object pointer.
*

View File

@ -1534,8 +1534,13 @@ void ED_mesh_split_faces(Mesh *mesh)
const Span<MPoly> polys = mesh->polys();
const Span<MLoop> loops = mesh->loops();
const float split_angle = (mesh->flag & ME_AUTOSMOOTH) != 0 ? mesh->smoothresh : float(M_PI);
const bke::AttributeAccessor attributes = mesh->attributes();
const VArray<bool> mesh_sharp_edges = attributes.lookup_or_default<bool>(
"sharp_edge", ATTR_DOMAIN_EDGE, false);
Array<bool> sharp_edges(mesh->totedge);
mesh_sharp_edges.materialize(sharp_edges);
Array<bool> sharp_edges(mesh->totedge, false);
BKE_edges_sharp_from_angle_set(mesh->totedge,
loops.data(),
loops.size(),

View File

@ -3446,55 +3446,63 @@ static char *vertex_group_lock_description(bContext * /*C*/,
int action = RNA_enum_get(params, "action");
int mask = RNA_enum_get(params, "mask");
const char *action_str, *target_str;
/* NOTE: constructing the following string literals can be done in a less verbose way,
* however the resulting strings can't be usefully translated, (via `TIP_`). */
switch (action) {
case VGROUP_LOCK:
action_str = TIP_("Lock");
switch (mask) {
case VGROUP_MASK_ALL:
return BLI_strdup(TIP_("Lock all vertex groups of the active object"));
case VGROUP_MASK_SELECTED:
return BLI_strdup(TIP_("Lock selected vertex groups of the active object"));
case VGROUP_MASK_UNSELECTED:
return BLI_strdup(TIP_("Lock unselected vertex groups of the active object"));
case VGROUP_MASK_INVERT_UNSELECTED:
return BLI_strdup(
TIP_("Lock selected and unlock unselected vertex groups of the active object"));
}
break;
case VGROUP_UNLOCK:
action_str = TIP_("Unlock");
switch (mask) {
case VGROUP_MASK_ALL:
return BLI_strdup(TIP_("Unlock all vertex groups of the active object"));
case VGROUP_MASK_SELECTED:
return BLI_strdup(TIP_("Unlock selected vertex groups of the active object"));
case VGROUP_MASK_UNSELECTED:
return BLI_strdup(TIP_("Unlock unselected vertex groups of the active object"));
case VGROUP_MASK_INVERT_UNSELECTED:
return BLI_strdup(
TIP_("Unlock selected and lock unselected vertex groups of the active object"));
}
break;
case VGROUP_TOGGLE:
action_str = TIP_("Toggle locks of");
switch (mask) {
case VGROUP_MASK_ALL:
return BLI_strdup(TIP_("Toggle locks of all vertex groups of the active object"));
case VGROUP_MASK_SELECTED:
return BLI_strdup(TIP_("Toggle locks of selected vertex groups of the active object"));
case VGROUP_MASK_UNSELECTED:
return BLI_strdup(TIP_("Toggle locks of unselected vertex groups of the active object"));
case VGROUP_MASK_INVERT_UNSELECTED:
return BLI_strdup(TIP_(
"Toggle locks of all and invert unselected vertex groups of the active object"));
}
break;
case VGROUP_INVERT:
action_str = TIP_("Invert locks of");
break;
default:
return nullptr;
}
switch (mask) {
case VGROUP_MASK_ALL:
target_str = TIP_("all");
break;
case VGROUP_MASK_SELECTED:
target_str = TIP_("selected");
break;
case VGROUP_MASK_UNSELECTED:
target_str = TIP_("unselected");
break;
case VGROUP_MASK_INVERT_UNSELECTED:
switch (action) {
case VGROUP_INVERT:
target_str = TIP_("selected");
break;
case VGROUP_LOCK:
target_str = TIP_("selected and unlock unselected");
break;
case VGROUP_UNLOCK:
target_str = TIP_("selected and lock unselected");
break;
default:
target_str = TIP_("all and invert unselected");
switch (mask) {
case VGROUP_MASK_ALL:
return BLI_strdup(TIP_("Invert locks of all vertex groups of the active object"));
case VGROUP_MASK_SELECTED:
case VGROUP_MASK_INVERT_UNSELECTED:
return BLI_strdup(TIP_("Invert locks of selected vertex groups of the active object"));
case VGROUP_MASK_UNSELECTED:
return BLI_strdup(TIP_("Invert locks of unselected vertex groups of the active object"));
}
break;
default:
return nullptr;
}
return BLI_sprintfN(TIP_("%s %s vertex groups of the active object"), action_str, target_str);
return nullptr;
}
void OBJECT_OT_vertex_group_lock(wmOperatorType *ot)

Some files were not shown because too many files have changed in this diff Show More