Files
goo-engine/source/blender/editors/space_view3d/view3d_gizmo_ruler.cc
T
Philipp Oeser 084cb40e98 Fix #108722 : Measures not cleared when removing annotation layer
Removing the layer would leave the ruler gizmos on screen, only way to
update was changing to another tool and back. Now remove all
corresponding gizmo ruler items found (also in different windows/scenes)
when removing the ruler annotation layer.

Also add a proper notifier that grease pencil was edited (this way we
also get immediate update when a ruler annotation layer was added --
which was also missing).

Pull Request: https://projects.blender.org/blender/blender/pulls/135571
2025-03-07 09:52:01 +01:00

1492 lines
45 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spview3d
*/
#include "BLI_listbase.h"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_rotation.h"
#include "BLI_math_vector_types.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_context.hh"
#include "BKE_gpencil_legacy.h"
#include "BKE_layer.hh"
#include "BKE_report.hh"
#include "BKE_scene.hh"
#include "BKE_screen.hh"
#include "BKE_unit.hh"
#include "DNA_gpencil_legacy_types.h"
#include "DNA_object_types.h"
#include "DNA_view3d_types.h"
#include "ED_gizmo_library.hh"
#include "ED_gizmo_utils.hh"
#include "ED_screen.hh"
#include "ED_transform.hh"
#include "ED_transform_snap_object_context.hh"
#include "ED_view3d.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "MEM_guardedalloc.h"
#include "RNA_access.hh"
#include "WM_api.hh"
#include "WM_toolsystem.hh"
#include "WM_types.hh"
#include "DEG_depsgraph_query.hh"
#include "view3d_intern.hh" /* own include */
#include "GPU_immediate.hh"
#include "GPU_immediate_util.hh"
#include "GPU_matrix.hh"
#include "GPU_state.hh"
#include "BLF_api.hh"
using blender::float2;
using blender::float2x2;
using blender::float3;
using blender::float3x2;
using blender::float3x3;
using blender::float4;
/**
* Supporting transform features could be removed if the actual transform system is used.
* Keep the option open since each transform feature is duplicating logic.
*/
#define USE_AXIS_CONSTRAINTS
static const char *view3d_gzgt_ruler_id = "VIEW3D_GGT_ruler";
#define MVAL_MAX_PX_DIST 12.0f
/* -------------------------------------------------------------------- */
/* Ruler Item (we can have many) */
enum {
/** Use protractor. */
RULERITEM_USE_ANGLE = (1 << 0),
/** Protractor vertex is selected (deleting removes it). */
RULERITEM_USE_ANGLE_ACTIVE = (1 << 1),
};
/* keep smaller than selection, since we may want click elsewhere without selecting a ruler */
#define RULER_PICK_DIST 12.0f
#define RULER_PICK_DIST_SQ (RULER_PICK_DIST * RULER_PICK_DIST)
/* not clicking on a point */
#define PART_LINE 0xff
/* -------------------------------------------------------------------- */
/* Ruler Info (wmGizmoGroup customdata) */
enum {
RULER_STATE_NORMAL = 0,
RULER_STATE_DRAG,
};
#ifdef USE_AXIS_CONSTRAINTS
/* Constrain axes */
enum {
CONSTRAIN_AXIS_NONE = -1,
CONSTRAIN_AXIS_X = 0,
CONSTRAIN_AXIS_Y = 1,
CONSTRAIN_AXIS_Z = 2,
};
/**
* Constraining modes.
* Off / Scene orientation / Global (or Local if Scene orientation is Global).
*/
enum {
CONSTRAIN_MODE_OFF = 0,
CONSTRAIN_MODE_1 = 1,
CONSTRAIN_MODE_2 = 2,
};
#endif /* USE_AXIS_CONSTRAINTS */
struct RulerItem;
struct RulerInfo {
RulerItem *item_active;
int flag;
int snap_flag;
int state;
#ifdef USE_AXIS_CONSTRAINTS
short constrain_axis, constrain_mode;
#endif
/* wm state */
wmWindowManager *wm;
wmWindow *win;
ScrArea *area;
ARegion *region; /* re-assigned every modal update */
/* Track changes in state. */
struct {
#ifndef USE_SNAP_DETECT_FROM_KEYMAP_HACK
bool do_snap;
#endif
bool do_thickness;
} drag_state_prev;
struct {
wmGizmo *gizmo;
PropertyRNA *prop_prevpoint;
PropertyRNA *prop_snap_source_type;
} snap_data;
};
/* -------------------------------------------------------------------- */
/* Ruler Item (two or three points) */
struct RulerItem {
wmGizmo gz;
/** World-space coords, middle being optional. */
float3x3 co;
eSnapMode snap_type[3];
int flag;
int raycast_dir; /* RULER_DIRECTION_* */
};
struct RulerInteraction {
/* selected coord */
char co_index; /* 0 -> 2 */
float3 drag_start_co;
};
/* -------------------------------------------------------------------- */
/** \name Internal Ruler Utilities
* \{ */
static RulerItem *ruler_item_add(wmGizmoGroup *gzgroup)
{
/* could pass this as an arg */
const wmGizmoType *gzt_ruler = WM_gizmotype_find("VIEW3D_GT_ruler_item", true);
RulerItem *ruler_item = (RulerItem *)WM_gizmo_new_ptr(gzt_ruler, gzgroup, nullptr);
WM_gizmo_set_flag(&ruler_item->gz, WM_GIZMO_DRAW_MODAL, true);
return ruler_item;
}
static void ruler_item_remove(bContext *C, wmGizmoGroup *gzgroup, RulerItem *ruler_item)
{
RulerInfo *ruler_info = static_cast<RulerInfo *>(gzgroup->customdata);
if (ruler_info->item_active == ruler_item) {
ruler_info->item_active = nullptr;
}
WM_gizmo_unlink(&gzgroup->gizmos, gzgroup->parent_gzmap, &ruler_item->gz, C);
}
static void ruler_item_as_string(
RulerItem *ruler_item, const UnitSettings &unit, char *numstr, size_t numstr_size, int prec)
{
if (ruler_item->flag & RULERITEM_USE_ANGLE) {
const float ruler_angle = angle_v3v3v3(
ruler_item->co[0], ruler_item->co[1], ruler_item->co[2]);
if (unit.system == USER_UNIT_NONE) {
BLI_snprintf(
numstr, numstr_size, "%.*f" BLI_STR_UTF8_DEGREE_SIGN, prec, RAD2DEGF(ruler_angle));
}
else {
BKE_unit_value_as_string(
numstr, numstr_size, double(ruler_angle), prec, B_UNIT_ROTATION, unit, false);
}
}
else {
const float ruler_len = len_v3v3(ruler_item->co[0], ruler_item->co[2]);
if (unit.system == USER_UNIT_NONE) {
BLI_snprintf(numstr, numstr_size, "%.*f", prec, ruler_len);
}
else {
BKE_unit_value_as_string_scaled(
numstr, numstr_size, ruler_len, prec, B_UNIT_LENGTH, unit, false);
}
}
}
static bool view3d_ruler_pick(wmGizmoGroup *gzgroup,
RulerItem *ruler_item,
const float2 mval,
int *r_co_index)
{
RulerInfo *ruler_info = static_cast<RulerInfo *>(gzgroup->customdata);
ARegion *region = ruler_info->region;
bool found = false;
float dist_best = RULER_PICK_DIST_SQ;
int co_index_best = -1;
{
float3x2 co_ss;
float dist;
int j;
/* should these be checked? - ok for now not to */
for (j = 0; j < 3; j++) {
ED_view3d_project_float_global(region, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
}
if (ruler_item->flag & RULERITEM_USE_ANGLE) {
dist = min_ff(dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[1]),
dist_squared_to_line_segment_v2(mval, co_ss[1], co_ss[2]));
if (dist < dist_best) {
dist_best = dist;
found = true;
{
const float3 dist_points = {
blender::math::distance_squared(co_ss[0], mval),
blender::math::distance_squared(co_ss[1], mval),
blender::math::distance_squared(co_ss[2], mval),
};
if (min_fff(UNPACK3(dist_points)) < RULER_PICK_DIST_SQ) {
co_index_best = min_axis_v3(dist_points);
}
else {
co_index_best = -1;
}
}
}
}
else {
dist = dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[2]);
if (dist < dist_best) {
dist_best = dist;
found = true;
{
const float2 dist_points = {
blender::math::distance_squared(co_ss[0], mval),
blender::math::distance_squared(co_ss[2], mval),
};
if (min_ff(UNPACK2(dist_points)) < RULER_PICK_DIST_SQ) {
co_index_best = (dist_points[0] < dist_points[1]) ? 0 : 2;
}
else {
co_index_best = -1;
}
}
}
}
}
UNUSED_VARS(dist_best);
*r_co_index = co_index_best;
return found;
}
/**
* Ensure the 'snap_context' is only cached while dragging,
* needed since the user may toggle modes between tool use.
*/
static void ruler_state_set(RulerInfo *ruler_info, int state)
{
if (state == ruler_info->state) {
return;
}
if (state == RULER_STATE_NORMAL) {
WM_gizmo_set_flag(ruler_info->snap_data.gizmo, WM_GIZMO_DRAW_VALUE, false);
RNA_property_float_set_array(
ruler_info->snap_data.gizmo->ptr, ruler_info->snap_data.prop_prevpoint, nullptr);
}
else if (state == RULER_STATE_DRAG) {
memset(&ruler_info->drag_state_prev, 0x0, sizeof(ruler_info->drag_state_prev));
/* Force the snap cursor to appear even though it is not highlighted. */
WM_gizmo_set_flag(ruler_info->snap_data.gizmo, WM_GIZMO_DRAW_VALUE, true);
}
else {
BLI_assert(0);
}
ruler_info->state = state;
}
static void view3d_ruler_item_project(RulerInfo *ruler_info, float3 &r_co, const int xy[2])
{
ED_view3d_win_to_3d_int(static_cast<const View3D *>(ruler_info->area->spacedata.first),
ruler_info->region,
r_co,
xy,
r_co);
}
/**
* Use for mouse-move events.
*/
static bool view3d_ruler_item_mousemove(const bContext *C,
Depsgraph *depsgraph,
RulerInfo *ruler_info,
RulerItem *ruler_item,
const int mval[2],
const bool do_thickness,
const bool do_snap)
{
wmGizmo *snap_gizmo = ruler_info->snap_data.gizmo;
constexpr float eps_bias = 0.0002f;
float dist_px = MVAL_MAX_PX_DIST * U.pixelsize; /* snap dist */
if (ruler_item) {
RulerInteraction *inter = static_cast<RulerInteraction *>(ruler_item->gz.interaction_data);
float3 &co = ruler_item->co[inter->co_index];
eSnapMode *snap_source_type = &ruler_item->snap_type[inter->co_index];
/* restore the initial depth */
co = inter->drag_start_co;
view3d_ruler_item_project(ruler_info, co, mval);
if (do_thickness && inter->co_index != 1) {
Scene *scene = DEG_get_input_scene(depsgraph);
View3D *v3d = static_cast<View3D *>(ruler_info->area->spacedata.first);
SnapObjectContext *snap_context = ED_gizmotypes_snap_3d_context_ensure(scene, snap_gizmo);
const float2 mval_fl = {float(mval[0]), float(mval[1])};
float3 ray_normal;
float3 ray_start;
float3 &co_other = ruler_item->co[inter->co_index == 0 ? 2 : 0];
SnapObjectParams snap_object_params{};
snap_object_params.snap_target_select = SCE_SNAP_TARGET_ALL;
snap_object_params.edit_mode_type = SNAP_GEOM_CAGE;
eSnapMode hit = ED_transform_snap_object_project_view3d(snap_context,
depsgraph,
ruler_info->region,
v3d,
SCE_SNAP_TO_FACE,
&snap_object_params,
nullptr,
mval_fl,
nullptr,
&dist_px,
co,
ray_normal);
if (hit) {
/* add some bias */
ray_start = co - ray_normal * eps_bias;
ED_transform_snap_object_project_ray(snap_context,
depsgraph,
v3d,
&snap_object_params,
ray_start,
-ray_normal,
nullptr,
co_other,
nullptr);
}
}
else {
View3D *v3d = static_cast<View3D *>(ruler_info->area->spacedata.first);
if (do_snap) {
float3 *prev_point = nullptr;
eSnapMode snap_type;
BLI_assert(ED_gizmotypes_snap_3d_is_enabled(snap_gizmo));
if (inter->co_index != 1) {
if (ruler_item->flag & RULERITEM_USE_ANGLE) {
prev_point = &ruler_item->co[1];
snap_type = ruler_item->snap_type[1];
}
else if (inter->co_index == 0) {
prev_point = &ruler_item->co[2];
snap_type = ruler_item->snap_type[2];
}
else {
prev_point = &ruler_item->co[0];
snap_type = ruler_item->snap_type[0];
}
}
if (prev_point != nullptr) {
RNA_property_float_set_array(
snap_gizmo->ptr, ruler_info->snap_data.prop_prevpoint, *prev_point);
RNA_property_enum_set(
snap_gizmo->ptr, ruler_info->snap_data.prop_snap_source_type, snap_type);
}
ED_gizmotypes_snap_3d_data_get(C, snap_gizmo, co, nullptr, nullptr, snap_source_type);
}
#ifdef USE_AXIS_CONSTRAINTS
if (!(ruler_item->flag & RULERITEM_USE_ANGLE) &&
ruler_info->constrain_mode != CONSTRAIN_MODE_OFF)
{
Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
RegionView3D *rv3d = static_cast<RegionView3D *>(ruler_info->region->regiondata);
BKE_view_layer_synced_ensure(scene, view_layer);
Object *ob = BKE_view_layer_active_object_get(view_layer);
Object *obedit = OBEDIT_FROM_OBACT(ob);
short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
if (ruler_info->constrain_mode == CONSTRAIN_MODE_2) {
orient_index = (orient_index == V3D_ORIENT_GLOBAL) ? V3D_ORIENT_LOCAL :
V3D_ORIENT_GLOBAL;
}
const int pivot_point = scene->toolsettings->transform_pivot_point;
float3x3 mat;
ED_transform_calc_orientation_from_type_ex(
scene, view_layer, v3d, rv3d, ob, obedit, orient_index, pivot_point, mat.ptr());
ruler_item->co = blender::math::invert(mat) * ruler_item->co;
/* Loop through the axes and constrain the dragged point to the current constrained axis.
*/
for (int i = 0; i <= 2; i++) {
if (ruler_info->constrain_axis != i) {
ruler_item->co[inter->co_index][i] = ruler_item->co[(inter->co_index == 0) ? 2 : 0][i];
}
}
ruler_item->co = mat * ruler_item->co;
}
#endif
}
return true;
}
return false;
}
/**
* When the gizmo-group has been created immediately before running an operator
* to manipulate rulers, it's possible the new gizmo-group has not yet been initialized.
* in 3.0 this happened because left-click drag would both select and add a new ruler,
* significantly increasing the likelihood of this happening.
* Workaround this crash by checking the gizmo's custom-data has not been cleared.
* The key-map has also been modified not to trigger this bug, see #95591.
*/
static bool gizmo_ruler_check_for_operator(const wmGizmoGroup *gzgroup)
{
return gzgroup->customdata != nullptr;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Ruler/Grease Pencil Conversion
* \{ */
/* Helper: Find the layer created as ruler. */
static bGPDlayer *view3d_ruler_layer_get(bGPdata *gpd)
{
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl->flag & GP_LAYER_IS_RULER) {
return gpl;
}
}
return nullptr;
}
static RulerItem *gzgroup_ruler_item_first_get(wmGizmoGroup *gzgroup)
{
#ifndef NDEBUG
RulerInfo *ruler_info = static_cast<RulerInfo *>(gzgroup->customdata);
BLI_assert(gzgroup->gizmos.first == ruler_info->snap_data.gizmo);
#endif
return (RulerItem *)((wmGizmo *)gzgroup->gizmos.first)->next;
}
#define RULER_ID "RulerData3D"
/* GP data creation has to happen before the undo step is stored.
* See also #116734. */
static void view3d_ruler_gpencil_ensure(bContext *C)
{
// RulerInfo *ruler_info = gzgroup->customdata;
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
if (scene->gpd == nullptr) {
scene->gpd = BKE_gpencil_data_addnew(bmain, "Annotations");
DEG_id_tag_update_ex(bmain, &scene->id, ID_RECALC_SYNC_TO_EVAL);
DEG_relations_tag_update(bmain);
}
}
static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup)
{
// RulerInfo *ruler_info = gzgroup->customdata;
Scene *scene = CTX_data_scene(C);
bGPdata *gpd;
bGPDlayer *gpl;
bGPDframe *gpf;
bGPDstroke *gps;
RulerItem *ruler_item;
const char *ruler_name = RULER_ID;
bool changed = false;
BLI_assert(scene->gpd != nullptr);
gpd = scene->gpd;
gpl = view3d_ruler_layer_get(gpd);
if (gpl == nullptr) {
gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false, false);
copy_v4_v4(gpl->color, U.gpencil_new_layer_col);
gpl->thickness = 1;
gpl->flag |= GP_LAYER_HIDE | GP_LAYER_IS_RULER;
}
gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_NEW);
BKE_gpencil_free_strokes(gpf);
for (ruler_item = gzgroup_ruler_item_first_get(gzgroup); ruler_item;
ruler_item = (RulerItem *)ruler_item->gz.next)
{
bGPDspoint *pt;
int j;
/* allocate memory for a new stroke */
gps = (bGPDstroke *)MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
if (ruler_item->flag & RULERITEM_USE_ANGLE) {
gps->totpoints = 3;
pt = gps->points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * gps->totpoints,
"gp_stroke_points");
for (j = 0; j < 3; j++) {
copy_v3_v3(&pt->x, ruler_item->co[j]);
pt->pressure = 1.0f;
pt->strength = 1.0f;
pt++;
}
}
else {
gps->totpoints = 2;
pt = gps->points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * gps->totpoints,
"gp_stroke_points");
for (j = 0; j < 3; j += 2) {
copy_v3_v3(&pt->x, ruler_item->co[j]);
pt->pressure = 1.0f;
pt->strength = 1.0f;
pt++;
}
}
gps->flag = GP_STROKE_3DSPACE;
gps->thickness = 3;
gps->hardness = 1.0f;
gps->fill_opacity_fac = 1.0f;
copy_v2_fl(gps->aspect_ratio, 1.0f);
gps->uv_scale = 1.0f;
BLI_addtail(&gpf->strokes, gps);
changed = true;
}
return changed;
}
static bool view3d_ruler_from_gpencil(const bContext *C, wmGizmoGroup *gzgroup)
{
Scene *scene = CTX_data_scene(C);
bool changed = false;
if (scene->gpd) {
bGPDlayer *gpl;
gpl = view3d_ruler_layer_get(scene->gpd);
if (gpl) {
bGPDframe *gpf;
gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV);
if (gpf) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
bGPDspoint *pt = gps->points;
int j;
RulerItem *ruler_item = nullptr;
if (gps->totpoints == 3) {
ruler_item = ruler_item_add(gzgroup);
for (j = 0; j < 3; j++) {
copy_v3_v3(ruler_item->co[j], &pt->x);
pt++;
}
ruler_item->flag |= RULERITEM_USE_ANGLE;
changed = true;
}
else if (gps->totpoints == 2) {
ruler_item = ruler_item_add(gzgroup);
for (j = 0; j < 3; j += 2) {
copy_v3_v3(ruler_item->co[j], &pt->x);
pt++;
}
changed = true;
}
}
}
}
}
return changed;
}
void ED_view3d_gizmo_ruler_remove_by_gpencil_layer(bContext *C, bGPDlayer *gpl)
{
wmWindowManager *wm = CTX_wm_manager(C);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
const Scene *scene = WM_window_get_active_scene(win);
if (!scene->gpd) {
continue;
}
bGPDlayer *gpl_scene = view3d_ruler_layer_get(scene->gpd);
if (gpl_scene != gpl) {
continue;
}
const bScreen *screen = WM_window_get_active_screen(win);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype != SPACE_VIEW3D) {
continue;
}
ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
if (!region) {
continue;
}
wmGizmoMap *gzmap = region->runtime->gizmo_map;
wmGizmoGroup *gzgroup = WM_gizmomap_group_find(gzmap, view3d_gzgt_ruler_id);
RulerItem *ruler_item;
while ((ruler_item = gzgroup_ruler_item_first_get(gzgroup))) {
ruler_item_remove(C, gzgroup, ruler_item);
}
ED_region_tag_redraw_editor_overlays(region);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Ruler Item Gizmo Type
* \{ */
static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
{
Scene *scene = CTX_data_scene(C);
const UnitSettings &unit = scene->unit;
RulerInfo *ruler_info = static_cast<RulerInfo *>(gz->parent_gzgroup->customdata);
RulerItem *ruler_item = (RulerItem *)gz;
ARegion *region = ruler_info->region;
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
const float cap_size = 4.0f * UI_SCALE_FAC;
const float bg_margin = 4.0f * UI_SCALE_FAC;
const float arc_size = 64.0f * UI_SCALE_FAC;
constexpr int arc_steps = 24;
const float4 color_act = {1.0f, 1.0f, 1.0f, 1.0f};
const float4 color_base = {0.0f, 0.0f, 0.0f, 1.0f};
uchar color_text[3];
uchar color_wire[3];
float4 color_back = {1.0f, 1.0f, 1.0f, 0.5f};
/* Pixel Space. */
GPU_matrix_push_projection();
GPU_matrix_push();
GPU_matrix_identity_set();
wmOrtho2_region_pixelspace(region);
/* anti-aliased lines for more consistent appearance */
GPU_line_smooth(true);
GPU_line_width(1.0f);
BLF_enable(blf_mono_font, BLF_ROTATION);
BLF_size(blf_mono_font, 14.0f * UI_SCALE_FAC);
BLF_rotation(blf_mono_font, 0.0f);
UI_GetThemeColor3ubv(TH_TEXT, color_text);
UI_GetThemeColor3ubv(TH_WIRE, color_wire);
/* Avoid white on white text. (TODO: Fix by using theme). */
if (int(color_text[0]) + int(color_text[1]) + int(color_text[2]) > 127 * 3 * 0.6f) {
copy_v3_fl(color_back, 0.0f);
}
const bool is_act = (ruler_info->item_active == ruler_item);
float2 dir_ruler;
float3x2 co_ss;
bool proj_ok[3];
int j;
/* Check if each corner is behind the near plane. If it is, we do not draw certain lines. */
for (j = 0; j < 3; j++) {
eV3DProjStatus status = ED_view3d_project_float_global(
region, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_CLIP_NEAR);
proj_ok[j] = (status == V3D_PROJ_RET_OK);
}
/* 3d drawing. */
GPU_matrix_push_projection();
GPU_matrix_push();
GPU_matrix_projection_set(rv3d->winmat);
GPU_matrix_set(rv3d->viewmat);
GPU_blend(GPU_BLEND_ALPHA);
const uint shdr_pos_3d = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
if (ruler_item->flag & RULERITEM_USE_ANGLE) {
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float4 viewport_size(0.0f);
GPU_viewport_size_get_f(viewport_size);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
immUniform1i("colors_len", 2); /* "advanced" mode */
const float4 &col = is_act ? color_act : color_base;
immUniform4f("color", 0.67f, 0.67f, 0.67f, 1.0f);
immUniform4fv("color2", col);
immUniform1f("dash_width", 6.0f);
immUniform1f("udash_factor", 0.5f);
immBegin(GPU_PRIM_LINE_STRIP, 3);
immVertex3fv(shdr_pos_3d, ruler_item->co[0]);
immVertex3fv(shdr_pos_3d, ruler_item->co[1]);
immVertex3fv(shdr_pos_3d, ruler_item->co[2]);
immEnd();
immUnbindProgram();
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* arc */
{
float3 dir_tmp;
float3 ar_coord;
float3 dir_a;
float3 dir_b;
float4 quat;
float3 axis;
float angle;
const float px_scale = (ED_view3d_pixel_size_no_ui_scale(rv3d, ruler_item->co[1]) *
min_fff(arc_size,
blender::math::distance(co_ss[0], co_ss[1]) / 2.0f,
blender::math::distance(co_ss[2], co_ss[1]) / 2.0f));
dir_a = blender::math::normalize(ruler_item->co[0] - ruler_item->co[1]);
dir_b = blender::math::normalize(ruler_item->co[2] - ruler_item->co[1]);
axis = blender::math::cross(dir_a, dir_b);
angle = angle_normalized_v3v3(dir_a, dir_b);
axis_angle_to_quat(quat, axis, angle / arc_steps);
dir_tmp = dir_a;
immUniformColor3ubv(color_wire);
immBegin(GPU_PRIM_LINE_STRIP, arc_steps + 1);
for (j = 0; j <= arc_steps; j++) {
ar_coord = ruler_item->co[1] + dir_tmp * px_scale;
mul_qt_v3(quat, dir_tmp);
immVertex3fv(shdr_pos_3d, ar_coord);
}
immEnd();
}
immUnbindProgram();
}
else {
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
immUniform1i("colors_len", 2); /* "advanced" mode */
const float *col = is_act ? color_act : color_base;
immUniform4f("color", 0.67f, 0.67f, 0.67f, 1.0f);
immUniform4fv("color2", col);
immUniform1f("dash_width", 6.0f);
immUniform1f("udash_factor", 0.5f);
immBegin(GPU_PRIM_LINES, 2);
immVertex3fv(shdr_pos_3d, ruler_item->co[0]);
immVertex3fv(shdr_pos_3d, ruler_item->co[2]);
immEnd();
immUnbindProgram();
}
/* 2d drawing. */
GPU_matrix_pop();
GPU_matrix_pop_projection();
const uint shdr_pos_2d = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
if (ruler_item->flag & RULERITEM_USE_ANGLE) {
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* capping */
{
float2 cap;
dir_ruler = co_ss[0] - co_ss[1];
float2 rot_90_vec_a = blender::math::normalize(float2{-dir_ruler[1], dir_ruler[0]});
dir_ruler = co_ss[1] - co_ss[2];
float2 rot_90_vec_b = blender::math::normalize(float2{-dir_ruler[1], dir_ruler[0]});
GPU_blend(GPU_BLEND_ALPHA);
if (proj_ok[1] && is_act && (ruler_item->flag & RULERITEM_USE_ANGLE_ACTIVE)) {
GPU_line_width(3.0f);
immUniformColor3fv(color_act);
immBegin(GPU_PRIM_LINES, 4);
/* angle vertex */
immVertex2f(shdr_pos_2d, co_ss[1][0] - cap_size, co_ss[1][1] - cap_size);
immVertex2f(shdr_pos_2d, co_ss[1][0] + cap_size, co_ss[1][1] + cap_size);
immVertex2f(shdr_pos_2d, co_ss[1][0] - cap_size, co_ss[1][1] + cap_size);
immVertex2f(shdr_pos_2d, co_ss[1][0] + cap_size, co_ss[1][1] - cap_size);
immEnd();
GPU_line_width(1.0f);
}
immUniformColor3ubv(color_wire);
if (proj_ok[0] || proj_ok[2] || proj_ok[1]) {
immBegin(GPU_PRIM_LINES, proj_ok[0] * 2 + proj_ok[2] * 2 + proj_ok[1] * 4);
if (proj_ok[0]) {
cap = co_ss[0] + rot_90_vec_a * cap_size;
immVertex2fv(shdr_pos_2d, cap);
cap = co_ss[0] - rot_90_vec_a * cap_size;
immVertex2fv(shdr_pos_2d, cap);
}
if (proj_ok[2]) {
cap = co_ss[2] + rot_90_vec_b * cap_size;
immVertex2fv(shdr_pos_2d, cap);
cap = co_ss[2] - rot_90_vec_b * cap_size;
immVertex2fv(shdr_pos_2d, cap);
}
/* angle vertex */
if (proj_ok[1]) {
immVertex2f(shdr_pos_2d, co_ss[1][0] - cap_size, co_ss[1][1] - cap_size);
immVertex2f(shdr_pos_2d, co_ss[1][0] + cap_size, co_ss[1][1] + cap_size);
immVertex2f(shdr_pos_2d, co_ss[1][0] - cap_size, co_ss[1][1] + cap_size);
immVertex2f(shdr_pos_2d, co_ss[1][0] + cap_size, co_ss[1][1] - cap_size);
}
immEnd();
}
GPU_blend(GPU_BLEND_NONE);
}
/* text */
char numstr[256];
float2 numstr_size;
float posit[2];
const int prec = 2; /* XXX, todo, make optional */
ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
/* Center text. */
posit[0] = co_ss[1][0] - (numstr_size[0] / 2.0f);
posit[1] = co_ss[1][1] - (numstr_size[1] / 2.0f);
/* Adjust text position to help readability. */
sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[1]);
float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]};
normalize_v2(rot_90_vec);
posit[1] += rot_90_vec[0] * numstr_size[1];
posit[0] += (rot_90_vec[1] < 0) ? numstr_size[0] : -numstr_size[0];
/* draw text (bg) */
if (proj_ok[1]) {
immUniformColor4fv(color_back);
GPU_blend(GPU_BLEND_ALPHA);
immRectf(shdr_pos_2d,
posit[0] - bg_margin,
posit[1] - bg_margin,
posit[0] + bg_margin + numstr_size[0],
posit[1] + bg_margin + numstr_size[1]);
GPU_blend(GPU_BLEND_NONE);
}
immUnbindProgram();
/* draw text */
if (proj_ok[1]) {
BLF_color3ubv(blf_mono_font, color_text);
BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
BLF_rotation(blf_mono_font, 0.0f);
BLF_draw(blf_mono_font, numstr, sizeof(numstr));
}
}
else {
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
dir_ruler = co_ss[0] - co_ss[2];
float2 rot_90_vec = blender::math::normalize(float2{-dir_ruler[1], dir_ruler[0]});
/* capping */
{
float2 cap;
GPU_blend(GPU_BLEND_ALPHA);
immUniformColor3ubv(color_wire);
if (proj_ok[0] || proj_ok[2]) {
immBegin(GPU_PRIM_LINES, proj_ok[0] * 2 + proj_ok[2] * 2);
if (proj_ok[0]) {
cap = co_ss[0] + rot_90_vec * cap_size;
immVertex2fv(shdr_pos_2d, cap);
cap = co_ss[0] - rot_90_vec * cap_size;
immVertex2fv(shdr_pos_2d, cap);
}
if (proj_ok[2]) {
cap = co_ss[2] + rot_90_vec * cap_size;
immVertex2fv(shdr_pos_2d, cap);
cap = co_ss[2] - rot_90_vec * cap_size;
immVertex2fv(shdr_pos_2d, cap);
}
immEnd();
}
GPU_blend(GPU_BLEND_NONE);
}
/* text */
char numstr[256];
float2 numstr_size;
const int prec = 6; /* XXX, todo, make optional */
float2 posit;
ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
posit = (co_ss[0] + co_ss[2]) / 2.0f;
/* center text */
posit -= numstr_size / 2.0f;
/* Adjust text position if this helps readability. */
const float len = len_v2v2(co_ss[0], co_ss[2]);
if ((len < (numstr_size[1] * 2.5f)) ||
((len < (numstr_size[0] + bg_margin + bg_margin)) && (fabs(rot_90_vec[0]) < 0.5f)))
{
/* Super short, or quite short and also shallow angle. Position below line. */
posit[1] = std::min(co_ss[0][1], co_ss[2][1]) - numstr_size[1] - bg_margin - bg_margin;
}
else if (fabs(rot_90_vec[0]) < 0.2f) {
/* Very shallow angle. Shift down by text height. */
posit[1] -= numstr_size[1];
}
/* draw text (bg) */
if (proj_ok[0] && proj_ok[2]) {
immUniformColor4fv(color_back);
GPU_blend(GPU_BLEND_ALPHA);
immRectf(shdr_pos_2d,
posit[0] - bg_margin,
posit[1] - bg_margin,
posit[0] + bg_margin + numstr_size[0],
posit[1] + bg_margin + numstr_size[1]);
GPU_blend(GPU_BLEND_NONE);
}
immUnbindProgram();
/* draw text */
if (proj_ok[0] && proj_ok[2]) {
BLF_color3ubv(blf_mono_font, color_text);
BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
BLF_draw(blf_mono_font, numstr, sizeof(numstr));
}
}
GPU_line_smooth(false);
BLF_disable(blf_mono_font, BLF_ROTATION);
GPU_matrix_pop();
GPU_matrix_pop_projection();
}
static int gizmo_ruler_test_select(bContext * /*C*/, wmGizmo *gz, const int mval[2])
{
RulerItem *ruler_item_pick = (RulerItem *)gz;
const float mval_fl[2] = {float(mval[0]), float(mval[1])};
int co_index;
/* select and drag */
if (view3d_ruler_pick(gz->parent_gzgroup, ruler_item_pick, mval_fl, &co_index)) {
if (co_index == -1) {
if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
return PART_LINE;
}
}
else {
return co_index;
}
}
return -1;
}
static int gizmo_ruler_modal(bContext *C,
wmGizmo *gz,
const wmEvent *event,
eWM_GizmoFlagTweak tweak_flag)
{
bool do_draw = false;
int exit_code = OPERATOR_RUNNING_MODAL;
RulerInfo *ruler_info = static_cast<RulerInfo *>(gz->parent_gzgroup->customdata);
RulerItem *ruler_item = (RulerItem *)gz;
ARegion *region = CTX_wm_region(C);
bool do_cursor_update = (event->val == KM_RELEASE) || (event->type == MOUSEMOVE);
ruler_info->region = region;
#ifdef USE_AXIS_CONSTRAINTS
if ((event->val == KM_PRESS) && ELEM(event->type, EVT_XKEY, EVT_YKEY, EVT_ZKEY)) {
/* Go to Mode 1 if a new axis is selected. */
if (event->type == EVT_XKEY && ruler_info->constrain_axis != CONSTRAIN_AXIS_X) {
ruler_info->constrain_axis = CONSTRAIN_AXIS_X;
ruler_info->constrain_mode = CONSTRAIN_MODE_1;
}
else if (event->type == EVT_YKEY && ruler_info->constrain_axis != CONSTRAIN_AXIS_Y) {
ruler_info->constrain_axis = CONSTRAIN_AXIS_Y;
ruler_info->constrain_mode = CONSTRAIN_MODE_1;
}
else if (event->type == EVT_ZKEY && ruler_info->constrain_axis != CONSTRAIN_AXIS_Z) {
ruler_info->constrain_axis = CONSTRAIN_AXIS_Z;
ruler_info->constrain_mode = CONSTRAIN_MODE_1;
}
else {
/* Cycle to the next mode if the same key is pressed again. */
if (ruler_info->constrain_mode != CONSTRAIN_MODE_2) {
ruler_info->constrain_mode++;
}
else {
ruler_info->constrain_mode = CONSTRAIN_MODE_OFF;
ruler_info->constrain_axis = CONSTRAIN_AXIS_NONE;
}
}
do_cursor_update = true;
}
#endif
#ifndef USE_SNAP_DETECT_FROM_KEYMAP_HACK
const bool do_snap = !(tweak_flag & WM_GIZMO_TWEAK_SNAP);
#else
/* Ensure snap is up to date. */
ED_gizmotypes_snap_3d_data_get(
C, ruler_info->snap_data.gizmo, nullptr, nullptr, nullptr, nullptr);
const bool do_snap = ED_gizmotypes_snap_3d_is_enabled(ruler_info->snap_data.gizmo);
#endif
const bool do_thickness = tweak_flag & WM_GIZMO_TWEAK_PRECISE;
if (ruler_info->drag_state_prev.do_thickness != do_thickness) {
do_cursor_update = true;
}
if (do_cursor_update) {
if (ruler_info->state == RULER_STATE_DRAG) {
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
if (view3d_ruler_item_mousemove(
C, depsgraph, ruler_info, ruler_item, event->mval, do_thickness, do_snap))
{
do_draw = true;
}
}
}
ruler_info->drag_state_prev.do_thickness = do_thickness;
if (do_draw) {
ED_region_tag_redraw_editor_overlays(region);
}
return exit_code;
}
static int gizmo_ruler_invoke(bContext *C, wmGizmo *gz, const wmEvent *event)
{
wmGizmoGroup *gzgroup = gz->parent_gzgroup;
RulerInfo *ruler_info = static_cast<RulerInfo *>(gzgroup->customdata);
RulerItem *ruler_item_pick = (RulerItem *)gz;
RulerInteraction *inter = (RulerInteraction *)MEM_callocN(sizeof(RulerInteraction), __func__);
gz->interaction_data = inter;
ARegion *region = ruler_info->region;
float mval_fl[2];
WM_event_drag_start_mval_fl(event, region, mval_fl);
#ifdef USE_AXIS_CONSTRAINTS
ruler_info->constrain_axis = CONSTRAIN_AXIS_NONE;
ruler_info->constrain_mode = CONSTRAIN_MODE_OFF;
#endif
/* select and drag */
if (gz->highlight_part == PART_LINE) {
if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
/* Add Center Point */
ruler_item_pick->flag |= RULERITEM_USE_ANGLE;
inter->co_index = 1;
ruler_state_set(ruler_info, RULER_STATE_DRAG);
/* find the factor */
{
float2x2 co_ss;
float fac;
ED_view3d_project_float_global(
region, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP);
ED_view3d_project_float_global(
region, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP);
fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]);
CLAMP(fac, 0.0f, 1.0f);
ruler_item_pick->co[1] = blender::math::interpolate(
ruler_item_pick->co[0], ruler_item_pick->co[2], fac);
}
/* update the new location */
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
view3d_ruler_item_mousemove(
C, depsgraph, ruler_info, ruler_item_pick, event->mval, false, false);
}
}
else {
inter->co_index = gz->highlight_part;
ruler_state_set(ruler_info, RULER_STATE_DRAG);
/* store the initial depth */
inter->drag_start_co = ruler_item_pick->co[inter->co_index];
}
if (inter->co_index == 1) {
ruler_item_pick->flag |= RULERITEM_USE_ANGLE_ACTIVE;
}
else {
ruler_item_pick->flag &= ~RULERITEM_USE_ANGLE_ACTIVE;
}
{
/* Set Snap prev point. */
float3 *prev_point;
eSnapMode snap_type;
if (ruler_item_pick->flag & RULERITEM_USE_ANGLE) {
prev_point = (inter->co_index != 1) ? &ruler_item_pick->co[1] : nullptr;
snap_type = (inter->co_index != 1) ? ruler_item_pick->snap_type[1] : SCE_SNAP_TO_VERTEX;
}
else if (inter->co_index == 0) {
prev_point = &ruler_item_pick->co[2];
snap_type = ruler_item_pick->snap_type[2];
}
else {
prev_point = &ruler_item_pick->co[0];
snap_type = ruler_item_pick->snap_type[2];
}
if (prev_point) {
RNA_property_float_set_array(
ruler_info->snap_data.gizmo->ptr, ruler_info->snap_data.prop_prevpoint, *prev_point);
RNA_property_enum_set(ruler_info->snap_data.gizmo->ptr,
ruler_info->snap_data.prop_snap_source_type,
snap_type);
}
else {
RNA_property_unset(ruler_info->snap_data.gizmo->ptr, ruler_info->snap_data.prop_prevpoint);
}
}
ruler_info->item_active = ruler_item_pick;
/* Ensures there is a valid GPencil data in current scene. */
view3d_ruler_gpencil_ensure(C);
return OPERATOR_RUNNING_MODAL;
}
static void gizmo_ruler_exit(bContext *C, wmGizmo *gz, const bool cancel)
{
wmGizmoGroup *gzgroup = gz->parent_gzgroup;
RulerInfo *ruler_info = static_cast<RulerInfo *>(gzgroup->customdata);
if (!cancel) {
if (ruler_info->state == RULER_STATE_DRAG) {
RNA_property_unset(ruler_info->snap_data.gizmo->ptr, ruler_info->snap_data.prop_prevpoint);
ruler_state_set(ruler_info, RULER_STATE_NORMAL);
}
/* We could convert only the current gizmo, for now just re-generate. */
if (view3d_ruler_to_gpencil(C, gzgroup)) {
/* For immediate update when a ruler annotation layer was added. */
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
}
}
MEM_SAFE_FREE(gz->interaction_data);
ruler_state_set(ruler_info, RULER_STATE_NORMAL);
}
static int gizmo_ruler_cursor_get(wmGizmo *gz)
{
if (gz->highlight_part == PART_LINE) {
return WM_CURSOR_CROSS;
}
return WM_CURSOR_NSEW_SCROLL;
}
void VIEW3D_GT_ruler_item(wmGizmoType *gzt)
{
/* identifiers */
gzt->idname = "VIEW3D_GT_ruler_item";
/* api callbacks */
gzt->draw = gizmo_ruler_draw;
gzt->test_select = gizmo_ruler_test_select;
gzt->modal = gizmo_ruler_modal;
gzt->invoke = gizmo_ruler_invoke;
gzt->exit = gizmo_ruler_exit;
gzt->cursor_get = gizmo_ruler_cursor_get;
gzt->struct_size = sizeof(RulerItem);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Ruler Gizmo Group
* \{ */
static void WIDGETGROUP_ruler_setup(const bContext *C, wmGizmoGroup *gzgroup)
{
RulerInfo *ruler_info = (RulerInfo *)MEM_callocN(sizeof(RulerInfo), __func__);
wmGizmo *gizmo;
{
/* The gizmo snap has to be the first gizmo. */
const wmGizmoType *gzt_snap;
gzt_snap = WM_gizmotype_find("GIZMO_GT_snap_3d", true);
gizmo = WM_gizmo_new_ptr(gzt_snap, gzgroup, nullptr);
ED_gizmotypes_snap_3d_flag_set(gizmo, V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE);
WM_gizmo_set_color(gizmo, blender::float4(1.0f));
wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_ruler_add", true);
WM_gizmo_operator_set(gizmo, 0, ot, nullptr);
}
if (view3d_ruler_from_gpencil(C, gzgroup)) {
/* nop */
}
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
ruler_info->wm = wm;
ruler_info->win = win;
ruler_info->area = area;
ruler_info->region = region;
ruler_info->snap_data.gizmo = gizmo;
ruler_info->snap_data.prop_prevpoint = RNA_struct_find_property(gizmo->ptr, "prev_point");
ruler_info->snap_data.prop_snap_source_type = RNA_struct_find_property(gizmo->ptr,
"snap_source_type");
gzgroup->customdata = ruler_info;
}
void VIEW3D_GGT_ruler(wmGizmoGroupType *gzgt)
{
gzgt->name = "Ruler Widgets";
gzgt->idname = view3d_gzgt_ruler_id;
gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_SCALE | WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL;
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
gzgt->poll = ED_gizmo_poll_or_unlink_delayed_from_tool;
gzgt->setup = WIDGETGROUP_ruler_setup;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Add Ruler Operator
* \{ */
static bool view3d_ruler_poll(bContext *C)
{
bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context(C);
if ((tref_rt == nullptr) || !STREQ(view3d_gzgt_ruler_id, tref_rt->gizmo_group) ||
CTX_wm_region_view3d(C) == nullptr)
{
return false;
}
return true;
}
static int view3d_ruler_add_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)) {
BKE_report(op->reports, RPT_WARNING, "Gizmos hidden in this view");
return OPERATOR_CANCELLED;
}
wmGizmoMap *gzmap = region->runtime->gizmo_map;
wmGizmoGroup *gzgroup = WM_gizmomap_group_find(gzmap, view3d_gzgt_ruler_id);
if (!gizmo_ruler_check_for_operator(gzgroup)) {
return OPERATOR_CANCELLED;
}
int mval[2];
WM_event_drag_start_mval(event, region, mval);
/* Create new line */
RulerItem *ruler_item;
ruler_item = ruler_item_add(gzgroup);
/* This is a little weak, but there is no real good way to tweak directly. */
WM_gizmo_highlight_set(gzmap, &ruler_item->gz);
if (WM_operator_name_call(
C, "GIZMOGROUP_OT_gizmo_tweak", WM_OP_INVOKE_REGION_WIN, nullptr, event) ==
OPERATOR_RUNNING_MODAL)
{
RulerInfo *ruler_info = static_cast<RulerInfo *>(gzgroup->customdata);
RulerInteraction *inter = static_cast<RulerInteraction *>(ruler_item->gz.interaction_data);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
inter->co_index = 0;
#ifndef USE_SNAP_DETECT_FROM_KEYMAP_HACK
/* Snap the first point added, not essential but handy. */
const bool do_snap = true;
#else
const bool do_snap = ED_gizmotypes_snap_3d_is_enabled(ruler_info->snap_data.gizmo);
#endif
view3d_ruler_item_mousemove(C, depsgraph, ruler_info, ruler_item, mval, false, do_snap);
copy_v3_v3(inter->drag_start_co, ruler_item->co[inter->co_index]);
RNA_property_float_set_array(ruler_info->snap_data.gizmo->ptr,
ruler_info->snap_data.prop_prevpoint,
inter->drag_start_co);
RNA_property_enum_set(ruler_info->snap_data.gizmo->ptr,
ruler_info->snap_data.prop_snap_source_type,
ruler_item->snap_type[inter->co_index]);
copy_v3_v3(ruler_item->co[2], ruler_item->co[0]);
ruler_item->gz.highlight_part = inter->co_index = 2;
}
return OPERATOR_FINISHED;
}
void VIEW3D_OT_ruler_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Ruler Add";
ot->idname = "VIEW3D_OT_ruler_add";
ot->description = "Add ruler";
ot->invoke = view3d_ruler_add_invoke;
ot->poll = view3d_ruler_poll;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Remove Ruler Operator
* \{ */
static int view3d_ruler_remove_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)) {
BKE_report(op->reports, RPT_WARNING, "Gizmos hidden in this view");
return OPERATOR_PASS_THROUGH;
}
wmGizmoMap *gzmap = region->runtime->gizmo_map;
wmGizmoGroup *gzgroup = WM_gizmomap_group_find(gzmap, view3d_gzgt_ruler_id);
if (gzgroup) {
if (!gizmo_ruler_check_for_operator(gzgroup)) {
return OPERATOR_CANCELLED;
}
RulerInfo *ruler_info = static_cast<RulerInfo *>(gzgroup->customdata);
if (ruler_info->item_active) {
RulerItem *ruler_item = ruler_info->item_active;
if ((ruler_item->flag & RULERITEM_USE_ANGLE) &&
(ruler_item->flag & RULERITEM_USE_ANGLE_ACTIVE))
{
ruler_item->flag &= ~(RULERITEM_USE_ANGLE | RULERITEM_USE_ANGLE_ACTIVE);
}
else {
ruler_item_remove(C, gzgroup, ruler_item);
}
/* Update the annotation layer. */
view3d_ruler_gpencil_ensure(C);
view3d_ruler_to_gpencil(C, gzgroup);
ED_region_tag_redraw_editor_overlays(region);
return OPERATOR_FINISHED;
}
}
return OPERATOR_PASS_THROUGH;
}
void VIEW3D_OT_ruler_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Ruler Remove";
ot->idname = "VIEW3D_OT_ruler_remove";
ot->invoke = view3d_ruler_remove_invoke;
ot->poll = view3d_ruler_poll;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
}
/** \} */