Blender V5.0
grease_pencil_primitive.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9
10#include <fmt/format.h>
11
12#include <cstring>
13
14#include "BKE_attribute.hh"
15#include "BKE_brush.hh"
16#include "BKE_colortools.hh"
17#include "BKE_context.hh"
18#include "BKE_curves.hh"
19#include "BKE_grease_pencil.hh"
20#include "BKE_material.hh"
21#include "BKE_paint.hh"
22#include "BKE_screen.hh"
23
24#include "WM_api.hh"
25#include "WM_types.hh"
26
27#include "RNA_access.hh"
28#include "RNA_define.hh"
29#include "RNA_enum_types.hh"
30
31#include "DEG_depsgraph.hh"
32
33#include "DNA_brush_types.h"
34#include "DNA_material_types.h"
35
36#include "ED_grease_pencil.hh"
37#include "ED_screen.hh"
38#include "ED_space_api.hh"
39#include "ED_view3d.hh"
40
41#include "BLI_array_utils.hh"
42#include "BLI_math_matrix.hh"
43#include "BLI_rand.hh"
44#include "BLI_vector.hh"
45
46#include "BLT_translation.hh"
47
48#include "GPU_immediate.hh"
49#include "GPU_state.hh"
50
51#include "UI_resources.hh"
52
54
55enum class PrimitiveType : int8_t {
56 Line = 0,
58 Arc = 2,
59 Curve = 3,
60 Box = 4,
61 Circle = 5,
62};
63
64enum class OperatorMode : int8_t {
65 Idle = 0,
67 /* Set the active control point to the mouse. */
68 Grab = 2,
69 /* Move the active control point. */
70 Drag = 3,
71 /* Move all control points. */
73 /* Rotate all control points. */
75 /* Scale all control points. */
77 /* Change brush radius. */
79 /* Change brush opacity. */
81};
82
83enum class ControlPointType : int8_t {
84 /* The points that are at the end of segments. */
86 /* The points inside of the segments not including the end points. */
88};
89
103
104static constexpr float ui_primary_point_draw_size_px = 8.0f;
105static constexpr float ui_secondary_point_draw_size_px = 5.0f;
106static constexpr float ui_tertiary_point_draw_size_px = 3.0f;
107static constexpr float ui_point_hit_size_px = 20.0f;
108static constexpr float ui_point_max_hit_size_px = 600.0f;
109
110/* These three points are only used for `Box` and `Circle` type. */
111static constexpr int control_point_first = 0;
112static constexpr int control_point_center = 1;
113static constexpr int control_point_last = 2;
114
117 /* For drawing preview loop. */
120
122 /* Stored in layer space. */
124 /* Store the control points temporally. */
127
131 /* Helper class to project screen space coordinates to 3D. */
133
137 std::optional<ColorGeometry4f> vertex_color;
138 std::optional<ColorGeometry4f> fill_color;
142 float softness;
146
154
158
159 /* Reference mouse position for initial radial control value. */
161 /* Initial value of radius or opacity. */
162 std::variant<int, float> initial_value;
163
165};
166
168{
169 switch (ptd.type) {
171 case PrimitiveType::Line: {
172 return 1;
173 }
176 case PrimitiveType::Arc: {
177 return 2;
178 }
180 return 3;
181 }
182 }
183
185 return 0;
186}
187
189{
190 BLI_assert(point != -1);
193 }
194
195 const int num_shared_points = control_points_per_segment(ptd);
196 if (math::mod(point, num_shared_points) == 0) {
198 }
200}
201
204 MutableSpan<float> sizes)
205{
206 ColorGeometry4f color_gizmo_primary;
207 ColorGeometry4f color_gizmo_secondary;
208 ColorGeometry4f color_gizmo_a;
209 ColorGeometry4f color_gizmo_b;
210 UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color_gizmo_primary);
211 UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color_gizmo_secondary);
212 UI_GetThemeColor4fv(TH_GIZMO_A, color_gizmo_a);
213 UI_GetThemeColor4fv(TH_GIZMO_B, color_gizmo_b);
214
215 const float size_primary = ui_primary_point_draw_size_px;
216 const float size_secondary = ui_secondary_point_draw_size_px;
217 const float size_tertiary = ui_tertiary_point_draw_size_px;
218
219 if (ptd.segments == 0) {
220 colors.fill(color_gizmo_primary);
221 sizes.fill(size_primary);
222 return;
223 }
224
226 colors.fill(color_gizmo_primary);
227 sizes.fill(size_primary);
228
229 /* Set the center point's color. */
230 colors[control_point_center] = color_gizmo_b;
231 sizes[control_point_center] = size_secondary;
232 }
233 else {
234 colors.fill(color_gizmo_secondary);
235 sizes.fill(size_secondary);
236
237 for (const int i : colors.index_range()) {
238 const ControlPointType control_point_type = get_control_point_type(ptd, i);
239
240 if (control_point_type == ControlPointType::JoinPoint) {
241 colors[i] = color_gizmo_b;
242 sizes[i] = size_tertiary;
243 }
244 }
245
246 colors.last() = color_gizmo_primary;
247 sizes.last() = size_tertiary;
248
250 colors.last(1) = color_gizmo_secondary;
251 sizes.last(1) = size_primary;
252 }
253 }
254
255 const int active_index = ptd.active_control_point_index;
256 if (active_index != -1) {
257 sizes[active_index] *= 1.5;
258 colors[active_index] = math::interpolate(colors[active_index], color_gizmo_a, 0.5f);
259 }
260}
261
263{
264 GPUVertFormat *format3d = immVertexFormat();
265 const uint pos3d = GPU_vertformat_attr_add(
266 format3d, "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
267 const uint col3d = GPU_vertformat_attr_add(
268 format3d, "color", blender::gpu::VertAttrType::SFLOAT_32_32_32_32);
269 const uint siz3d = GPU_vertformat_attr_add(
270 format3d, "size", blender::gpu::VertAttrType::SFLOAT_32);
272
275
276 Array<ColorGeometry4f> colors(ptd.control_points.size());
277 Array<float> sizes(ptd.control_points.size());
278 control_point_colors_and_sizes(ptd, colors, sizes);
279
280 for (const int point : ptd.control_points.index_range()) {
282 ptd.control_points[point]);
283 const ColorGeometry4f color = colors[point];
284 const float size = sizes[point];
285
286 immAttr4f(col3d, color[0], color[1], color[2], color[3]);
287 immAttr1f(siz3d, size * 2.0f);
288 immVertex3fv(pos3d, world_pos);
289 }
290
291 immEnd();
294}
295
296static void grease_pencil_primitive_draw(const bContext * /*C*/, ARegion * /*region*/, void *arg)
297{
298 PrimitiveToolOperation &ptd = *reinterpret_cast<PrimitiveToolOperation *>(arg);
300}
301
303{
304 ptd.temp_segments = ptd.segments;
305 ptd.temp_control_points.resize(ptd.control_points.size());
306 array_utils::copy(ptd.control_points.as_span(), ptd.temp_control_points.as_mutable_span());
307}
308
310{
311 ptd.segments = ptd.temp_segments;
312 ptd.control_points.resize(ptd.temp_control_points.size());
313 array_utils::copy(ptd.temp_control_points.as_span(), ptd.control_points.as_mutable_span());
314}
315
317 Span<float2> control_points,
318 MutableSpan<float2> new_positions)
319{
320 const int subdivision = ptd.subdivision;
321 const int new_points_num = new_positions.size();
322
323 if (ptd.segments == 0) {
324 new_positions.fill(control_points.last());
325 return;
326 }
327
328 switch (ptd.type) {
331 for (const int i : new_positions.index_range().drop_back(1)) {
332 const float t = math::mod(i / float(subdivision + 1), 1.0f);
333 const int point = int(i / (subdivision + 1));
334 const int point_next = point + 1;
335 new_positions[i] = math::interpolate(control_points[point], control_points[point_next], t);
336 }
337 new_positions.last() = control_points.last();
338 return;
339 }
340 case PrimitiveType::Arc: {
341 const int num_shared_points = control_points_per_segment(ptd);
342 const int num_segments = ptd.segments;
343 for (const int segment_i : IndexRange(num_segments)) {
344 const float2 A = control_points[num_shared_points * segment_i + 0];
345 const float2 B = control_points[num_shared_points * segment_i + 1];
346 const float2 C = control_points[num_shared_points * segment_i + 2];
347 for (const int i : IndexRange(subdivision + 1)) {
348 const float t = i / float(subdivision + 1);
349 const float2 AB = math::interpolate(A, B, t);
350 const float2 BC = math::interpolate(B, C, t);
351 new_positions[i + segment_i * (subdivision + 1)] = math::interpolate(AB, BC, t);
352 }
353 }
354 new_positions.last() = control_points.last();
355 return;
356 }
358 const int num_shared_points = control_points_per_segment(ptd);
359 const int num_segments = ptd.segments;
360
361 for (const int segment_i : IndexRange(num_segments)) {
362 const float2 A = control_points[num_shared_points * segment_i + 0];
363 const float2 B = control_points[num_shared_points * segment_i + 1];
364 const float2 C = control_points[num_shared_points * segment_i + 2];
365 const float2 D = control_points[num_shared_points * segment_i + 3];
366 for (const int i : IndexRange(subdivision + 1)) {
367 const float t = i / float(subdivision + 1);
368 const float2 AB = math::interpolate(A, B, t);
369 const float2 BC = math::interpolate(B, C, t);
370 const float2 CD = math::interpolate(C, D, t);
371 const float2 ABBC = math::interpolate(AB, BC, t);
372 const float2 BCCD = math::interpolate(BC, CD, t);
373 new_positions[i + segment_i * (subdivision + 1)] = math::interpolate(ABBC, BCCD, t);
374 }
375 }
376 new_positions.last() = control_points.last();
377 return;
378 }
380 const float2 center = control_points[control_point_center];
381 const float2 offset = control_points[control_point_first] - center;
382 for (const int i : new_positions.index_range()) {
383 const float t = i / float(new_points_num);
384 const float a = t * math::numbers::pi * 2.0f;
385 new_positions[i] = offset * float2(sinf(a), cosf(a)) + center;
386 }
387 return;
388 }
389 case PrimitiveType::Box: {
390 const float2 center = control_points[control_point_center];
391 const float2 offset = control_points[control_point_first] - center;
392 /*
393 * Calculate the 4 corners of the box.
394 * Here's a diagram.
395 *
396 * +-----------+
397 * |A B|
398 * | |
399 * | center |
400 * | |
401 * |D C|
402 * +-----------+
403 *
404 */
405 const float2 A = center + offset * float2(1.0f, 1.0f);
406 const float2 B = center + offset * float2(-1.0f, 1.0f);
407 const float2 C = center + offset * float2(-1.0f, -1.0f);
408 const float2 D = center + offset * float2(1.0f, -1.0f);
409 const float2 corners[4] = {A, B, C, D};
410 for (const int i : new_positions.index_range()) {
411 const float t = math::mod(i / float(subdivision + 1), 1.0f);
412 const int point = int(i / (subdivision + 1));
413 const int point_next = math::mod(point + 1, 4);
414 new_positions[i] = math::interpolate(corners[point], corners[point_next], t);
415 }
416 return;
417 }
418 }
419}
420
426
428 MutableSpan<float2> new_positions)
429{
430 Array<float2> control_points_2d(ptd.control_points.size());
431 for (const int i : ptd.control_points.index_range()) {
432 control_points_2d[i] = primitive_local_to_screen(ptd, ptd.control_points[i]);
433 }
434
435 primitive_calulate_curve_positions(ptd, control_points_2d, new_positions);
436}
437
439{
440 const int subdivision = ptd.subdivision;
441
442 switch (ptd.type) {
447 case PrimitiveType::Arc: {
448 const int join_points = ptd.segments + 1;
449 return join_points + subdivision * ptd.segments;
450 break;
451 }
452 case PrimitiveType::Box: {
453 return 4 + subdivision * 4;
454 break;
455 }
456 }
457
459 return 0;
460}
461
463{
464 const bool on_back = ptd.on_back;
465 const int new_points_num = grease_pencil_primitive_curve_points_number(ptd);
466 const bool use_random = (ptd.settings->flag & GP_BRUSH_GROUP_RANDOM) != 0;
467
469 const int target_curve_index = on_back ? 0 : curves.curves_range().last();
470 ed::greasepencil::resize_single_curve(curves, on_back == false, new_points_num);
471
472 const IndexRange curve_points = curves.points_by_curve()[target_curve_index];
473
474 MutableSpan<float3> positions_3d = curves.positions_for_write().slice(curve_points);
475 Array<float2> positions_2d(new_points_num);
476
477 primitive_calulate_curve_positions_2d(ptd, positions_2d);
478 ptd.placement.project(positions_2d, positions_3d);
479
480 Set<std::string> point_attributes_to_skip;
481
482 MutableSpan<float> new_radii = ptd.drawing->radii_for_write().slice(curve_points);
483 MutableSpan<float> new_opacities = ptd.drawing->opacities_for_write().slice(curve_points);
484 MutableSpan<ColorGeometry4f> new_vertex_colors = ptd.drawing->vertex_colors_for_write().slice(
485 curve_points);
487 MutableSpan<float> new_rotations;
488 if (use_random && ptd.settings->uv_random > 0.0f) {
489 rotations = curves.attributes_for_write().lookup_or_add_for_write_span<float>(
490 "rotation", bke::AttrDomain::Point);
491 new_rotations = rotations.span.slice(curve_points);
492 }
493
494 const ToolSettings *ts = ptd.vc.scene->toolsettings;
495 const GP_Sculpt_Settings *gset = &ts->gp_sculpt;
496
497 /* Screen-space length along curve used as randomization parameter. */
498 Array<float> lengths(new_points_num);
499
500 for (const int point : curve_points.index_range()) {
501 float pressure = 1.0f;
502 /* Apply pressure curve. */
504 const float t = point / float(new_points_num - 1);
505 pressure = BKE_curvemapping_evaluateF(gset->cur_primitive, 0, t);
506 }
507
508 const float radius = ed::greasepencil::radius_from_input_sample(ptd.vc.rv3d,
509 ptd.region,
510 ptd.brush,
511 pressure,
512 positions_3d[point],
514 ptd.settings);
516 pressure, ptd.brush, ptd.settings);
517
518 if (point == 0) {
519 lengths[point] = 0.0f;
520 }
521 else {
522 const float distance_2d = math::distance(positions_2d[point - 1], positions_2d[point]);
523 lengths[point] = lengths[point - 1] + distance_2d;
524 }
525
526 new_radii[point] = ed::greasepencil::randomize_radius(
527 *ptd.settings, ptd.stroke_random_radius_factor, lengths[point], radius, pressure);
528 new_opacities[point] = ed::greasepencil::randomize_opacity(
529 *ptd.settings, ptd.stroke_random_opacity_factor, lengths[point], opacity, pressure);
530 if (ptd.vertex_color) {
531 std::optional<BrushColorJitterSettings> jitter_settings =
533 ptd.brush);
534 new_vertex_colors[point] = ed::greasepencil::randomize_color(*ptd.settings,
535 jitter_settings,
539 lengths[point],
540 *ptd.vertex_color,
541 pressure);
542 }
543 if (rotations) {
544 new_rotations[point] = ed::greasepencil::randomize_rotation(
545 *ptd.settings, ptd.stroke_random_rotation_factor, lengths[point], pressure);
546 }
547 }
548
549 point_attributes_to_skip.add_multiple({"position", "radius", "opacity"});
550 if (ptd.vertex_color) {
551 point_attributes_to_skip.add("vertex_color");
552 }
553 if (rotations) {
554 point_attributes_to_skip.add("rotation");
555 rotations.finish();
556 }
557
558 /* Initialize the rest of the attributes with default values. */
559 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
562 bke::attribute_filter_from_skip_ref(point_attributes_to_skip),
563 curve_points);
564
567 IndexRange::from_single(target_curve_index));
568}
569
571{
572 /* Resize the curves geometry so there is one more curve with a single point. */
574
575 const bool on_back = ptd.on_back;
577
578 const int target_curve_index = on_back ? 0 : (curves.curves_num() - 1);
579
580 Set<std::string> curve_attributes_to_skip;
581
582 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
584 "material_index", bke::AttrDomain::Curve);
585 materials.span[target_curve_index] = ptd.material_index;
586 materials.finish();
587 curve_attributes_to_skip.add("material_index");
588
590 "cyclic", bke::AttrDomain::Curve);
592 cyclic.span[target_curve_index] = is_cyclic;
593 cyclic.finish();
594 curve_attributes_to_skip.add("cyclic");
595
596 if (bke::SpanAttributeWriter<float> softness = attributes.lookup_or_add_for_write_span<float>(
597 "softness", bke::AttrDomain::Curve))
598 {
599 softness.span[target_curve_index] = ptd.softness;
600 softness.finish();
601 curve_attributes_to_skip.add("softness");
602 }
603
604 /* Only set the attribute if the type is not the default or if it already exists. */
605 if (ptd.settings->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("start_cap")) {
606 if (bke::SpanAttributeWriter<int8_t> start_caps =
607 attributes.lookup_or_add_for_write_span<int8_t>("start_cap", bke::AttrDomain::Curve))
608 {
609 start_caps.span[target_curve_index] = ptd.settings->caps_type;
610 start_caps.finish();
611 curve_attributes_to_skip.add("start_cap");
612 }
613 }
614
615 if (ptd.settings->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("end_cap")) {
617 attributes.lookup_or_add_for_write_span<int8_t>("end_cap", bke::AttrDomain::Curve))
618 {
619 end_caps.span[target_curve_index] = ptd.settings->caps_type;
620 end_caps.finish();
621 curve_attributes_to_skip.add("end_cap");
622 }
623 }
624
625 if (ptd.use_fill && (ptd.fill_opacity < 1.0f || attributes.contains("fill_opacity"))) {
626 if (bke::SpanAttributeWriter<float> fill_opacities =
627 attributes.lookup_or_add_for_write_span<float>(
628 "fill_opacity",
631 {
632 fill_opacities.span[target_curve_index] = ptd.fill_opacity;
633 fill_opacities.finish();
634 curve_attributes_to_skip.add("fill_opacity");
635 }
636 }
637
638 if (ptd.fill_color) {
639 ptd.drawing->fill_colors_for_write()[target_curve_index] = *ptd.fill_color;
640 curve_attributes_to_skip.add("fill_color");
641 }
642
644 "u_scale", bke::AttrDomain::Curve))
645 {
646 u_scale.span[target_curve_index] = 1.0f;
647 u_scale.finish();
648 curve_attributes_to_skip.add("u_scale");
649 }
650
651 curves.curve_types_for_write()[target_curve_index] = CURVE_TYPE_POLY;
652 curves.update_curve_types();
653 curve_attributes_to_skip.add("curve_type");
654
655 /* Initialize the rest of the attributes with default values. */
658 bke::attribute_filter_from_skip_ref(curve_attributes_to_skip),
659 on_back ? curves.curves_range().take_front(1) :
660 curves.curves_range().take_back(1));
662}
663
665{
667 const int target_curve_index = ptd.on_back ? 0 : (curves.curves_num() - 1);
668 curves.remove_curves(IndexRange::from_single(target_curve_index), {});
670}
671
672/* Helper: Draw status message while the user is running the operator. */
674 wmOperator *op,
676{
678 status.opmodal(IFACE_("Confirm"), op->type, int(ModalKeyMode::Confirm));
679 status.opmodal(IFACE_("Cancel"), op->type, int(ModalKeyMode::Cancel));
680 status.opmodal(IFACE_("Panning"), op->type, int(ModalKeyMode::Panning));
681 status.item(IFACE_("Align"), ICON_EVENT_SHIFT);
682 status.opmodal("", op->type, int(ModalKeyMode::IncreaseSubdivision));
683 status.opmodal("", op->type, int(ModalKeyMode::DecreaseSubdivision));
684 status.item(fmt::format("{} ({})", IFACE_("Subdivisions"), ptd.subdivision), ICON_NONE);
685
686 if (ptd.segments == 1) {
687 status.item(IFACE_("Center"), ICON_EVENT_ALT);
688 }
689
690 if (ELEM(ptd.type,
695 {
696 status.opmodal(IFACE_("Extrude"), op->type, int(ModalKeyMode::Extrude));
697 }
698
699 status.opmodal(IFACE_("Grab"), op->type, int(ModalKeyMode::Grab));
700 status.opmodal(IFACE_("Rotate"), op->type, int(ModalKeyMode::Rotate));
701 status.opmodal(IFACE_("Scale"), op->type, int(ModalKeyMode::Scale));
702}
703
705{
706 GreasePencil *grease_pencil = static_cast<GreasePencil *>(ptd.vc.obact->data);
707
708 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
709 WM_event_add_notifier(C, NC_GEOM | ND_DATA, grease_pencil);
710
712}
713
714/* Invoke handler: Initialize the operator. */
716 wmOperator *op,
717 const wmEvent *event)
718{
720 C, op, false);
721 if (retval != OPERATOR_RUNNING_MODAL) {
722 return retval;
723 }
724
725 /* If in tools region, wait till we get to the main (3D-space)
726 * region before allowing drawing to take place. */
728
729 wmWindow *win = CTX_wm_window(C);
730
731 /* Set cursor to indicate modal. */
733
735
736 /* Allocate new data. */
737 PrimitiveToolOperation *ptd_pointer = MEM_new<PrimitiveToolOperation>(__func__);
738 op->customdata = ptd_pointer;
739
740 PrimitiveToolOperation &ptd = *ptd_pointer;
741
742 ptd.vc = vc;
743 ptd.region = vc.region;
745 const float2 start_coords = float2(event->mval);
746
747 GreasePencil *grease_pencil = static_cast<GreasePencil *>(vc.obact->data);
748
749 /* Initialize helper class for projecting screen space coordinates. */
751 *vc.scene, *vc.region, *view3d, *vc.obact, grease_pencil->get_active_layer());
752 if (placement.use_project_to_surface()) {
754 }
755 else if (placement.use_project_to_stroke()) {
757 }
758
759 ptd.placement = placement;
760
761 ptd.vod = ED_view3d_navigation_init(C, nullptr);
762
763 ptd.start_position_2d = start_coords;
764 ptd.subdivision = RNA_int_get(op->ptr, "subdivision");
765 ptd.type = PrimitiveType(RNA_enum_get(op->ptr, "type"));
767 ptd.segments = 0;
769
771
773 ptd.segments++;
774 ptd.control_points.append_n_times(pos, control_points_per_segment(ptd));
777
778 Paint *paint = &vc.scene->toolsettings->gp_paint->paint;
779 ptd.brush = BKE_paint_brush(paint);
780 if (ptd.brush->gpencil_settings == nullptr) {
782 }
785
795
797 GP_Sculpt_Settings *gset = &ts->gp_sculpt;
798 /* Initialize pressure curve. */
801 }
802
804 CTX_data_main(C), vc.obact, ptd.brush);
806 ptd.use_fill = (material->gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
807
808 const bool use_vertex_color = (vc.scene->toolsettings->gp_paint->mode ==
810 if (use_vertex_color) {
811 ColorGeometry4f color_base;
812 copy_v3_v3(color_base, ptd.brush->color);
813 color_base.a = ptd.settings->vertex_factor;
815 std::make_optional(color_base) :
816 std::nullopt;
818 std::make_optional(color_base) :
819 std::nullopt;
820 }
821 else {
822 ptd.vertex_color = std::nullopt;
823 ptd.fill_color = std::nullopt;
824 }
825
826 ptd.fill_opacity = ptd.brush->alpha;
827 ptd.softness = 1.0 - ptd.settings->hardness;
828
830 vc.scene, ptd.region, ptd.start_position_2d, ptd.placement);
831
832 const bool use_random = (ptd.settings->flag & GP_BRUSH_GROUP_RANDOM) != 0;
833 if (use_random) {
835 ptd.stroke_random_radius_factor = ptd.rng.get_float() * 2.0f - 1.0f;
836 ptd.stroke_random_opacity_factor = ptd.rng.get_float() * 2.0f - 1.0f;
837 ptd.stroke_random_rotation_factor = ptd.rng.get_float() * 2.0f - 1.0f;
838 ptd.stroke_random_hue_factor = ptd.rng.get_float() * 2.0f - 1.0f;
839 ptd.stroke_random_sat_factor = ptd.rng.get_float() * 2.0f - 1.0f;
840 ptd.stroke_random_val_factor = ptd.rng.get_float() * 2.0f - 1.0f;
841 }
842
843 BLI_assert(grease_pencil->has_active_layer());
844 ptd.local_transform = grease_pencil->get_active_layer()->local_transform();
845 ptd.drawing = grease_pencil->get_editable_drawing_at(*grease_pencil->get_active_layer(),
846 vc.scene->r.cfra);
847
850
853
854 /* Updates indicator in header. */
856
857 /* Add a modal handler for this operator. */
859
861}
862
863/* Exit and free memory. */
864static void grease_pencil_primitive_exit(bContext *C, wmOperator *op, const bool cancelled)
865{
867
868 const Scene &scene = *CTX_data_scene(C);
869 const bool do_automerge_endpoints = (scene.toolsettings->gpencil_flags &
871 const bool on_back = (scene.toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
872
873 if (do_automerge_endpoints && !cancelled) {
874 const Object &ob = *ptd->vc.obact;
875 const GreasePencil *grease_pencil = static_cast<GreasePencil *>(ob.data);
876 const bke::greasepencil::Layer &active_layer = *grease_pencil->get_active_layer();
877
878 constexpr float merge_distance = 30.0f;
879 const float4x4 layer_to_world = active_layer.to_world_space(ob);
880 bke::greasepencil::Drawing &drawing = *ptd->drawing;
881 const bke::CurvesGeometry &src_curves = drawing.strokes();
882 const int active_curve = on_back ? src_curves.curves_range().first() :
883 src_curves.curves_range().last();
884 const IndexMask selection = IndexRange::from_single(active_curve);
885
887 *CTX_wm_region(C), src_curves, layer_to_world, merge_distance, selection, {});
888 drawing.tag_topology_changed();
889 }
890
891 /* Clear status message area. */
892 ED_workspace_status_text(C, nullptr);
893
895
896 /* Deactivate the extra drawing stuff in 3D-View. */
898
900
902
903 MEM_delete<PrimitiveToolOperation>(ptd);
904 /* Clear pointer. */
905 op->customdata = nullptr;
906}
907
909{
910 using namespace math;
911 return sign(p) * float2(1.0f / numbers::sqrt2) * length(p);
912}
913
914/* Using Chebyshev distance instead of Euclidean. */
916{
917 using namespace math;
918 return sign(p) * float2(std::max(abs(p[0]), abs(p[1])));
919}
920
921/* Snaps to the closest diagonal, horizontal or vertical. */
923{
924 using namespace math;
925 /* sin(pi/8) or sin of 22.5 degrees. */
926 const float sin225 = 0.3826834323650897717284599840304f;
927 return sign(p) * length(p) * normalize(sign(normalize(abs(p)) - sin225) + 1.0f);
928}
929
931 const wmEvent *event)
932{
933 using namespace math;
934 const float2 start = ptd.start_position_2d;
935 const float2 end = float2(event->mval);
936
937 const float2 dif = end - start;
938 float2 offset = dif;
939
940 if (event->modifier & KM_SHIFT) {
941 if (ptd.type == PrimitiveType::Box) {
942 offset = snap_diagonals_box(dif);
943 }
944 else if (ptd.type == PrimitiveType::Circle) {
945 offset = snap_diagonals(dif);
946 }
947 else { /* Line, Polyline, Arc and Curve. */
948 offset = snap_8_angles(dif);
949 }
950 }
951 offset *= 0.5f;
952
953 float2 center = start + offset;
954
955 if (event->modifier & KM_ALT && ptd.segments == 1) {
956 center = start;
957 offset *= 2.0f;
958 }
959
960 const float3 start_pos = ptd.placement.project(center - offset);
961 const float3 end_pos = ptd.placement.project(center + offset);
962
963 const int number_control_points = control_points_per_segment(ptd);
964 for (const int i : IndexRange(number_control_points + 1)) {
965 ptd.control_points.last(i) = interpolate(
966 end_pos, start_pos, (i / float(number_control_points)));
967 }
968}
969
971 const wmEvent *event)
972{
973 const float2 start = ptd.start_position_2d;
974 const float2 end = float2(event->mval);
975 const float2 dif = end - start;
976
977 for (const int point_index : ptd.control_points.index_range()) {
978 const float2 start_pos2 = primitive_local_to_screen(ptd, ptd.temp_control_points[point_index]);
979
980 float3 pos = ptd.placement.project(start_pos2 + dif);
981 ptd.control_points[point_index] = pos;
982 }
983}
984
986{
988 const float3 pos = ptd.placement.project(float2(event->mval));
990
992 return;
993 }
994
995 /* If the center point is been grabbed, move all points. */
998 return;
999 }
1000
1001 const int other_point = ptd.active_control_point_index == control_point_first ?
1004
1005 /* Get the location of the other control point. */
1006 const float2 other_point_2d = primitive_local_to_screen(ptd,
1007 ptd.temp_control_points[other_point]);
1008
1009 /* Set the center point to between the first and last point. */
1011 (other_point_2d + float2(event->mval)) / 2.0f);
1012}
1013
1015{
1017 const float2 start = ptd.start_position_2d;
1018 const float2 end = float2(event->mval);
1019 const float2 dif = end - start;
1020
1021 const float2 start_pos2 = primitive_local_to_screen(
1023
1024 const float3 pos = ptd.placement.project(start_pos2 + dif);
1026}
1027
1029{
1032 }
1033 float2 center_of_mass = float2(0.0f, 0.0f);
1034
1035 for (const int point_index : ptd.control_points.index_range()) {
1036 center_of_mass += primitive_local_to_screen(ptd, ptd.temp_control_points[point_index]);
1037 }
1038 center_of_mass /= ptd.control_points.size();
1039 return center_of_mass;
1040}
1041
1043 const wmEvent *event)
1044{
1045 const float2 start = ptd.start_position_2d;
1046 const float2 end = float2(event->mval);
1047
1048 const float2 center_of_mass = primitive_center_of_mass(ptd);
1049
1050 const float2 end_ = end - center_of_mass;
1051 const float2 start_ = start - center_of_mass;
1052 const float rotation = math::atan2(start_[0], start_[1]) - math::atan2(end_[0], end_[1]);
1053
1054 for (const int point_index : ptd.control_points.index_range()) {
1055 const float2 start_pos2 = primitive_local_to_screen(ptd, ptd.temp_control_points[point_index]);
1056
1057 const float2 dif = start_pos2 - center_of_mass;
1058 const float c = math::cos(rotation);
1059 const float s = math::sin(rotation);
1060 const float2x2 rot = float2x2(float2(c, s), float2(-s, c));
1061 const float2 pos2 = rot * dif + center_of_mass;
1062 const float3 pos = ptd.placement.project(pos2);
1063 ptd.control_points[point_index] = pos;
1064 }
1065}
1066
1068 const wmEvent *event)
1069{
1070 const float2 start = ptd.start_position_2d;
1071 const float2 end = float2(event->mval);
1072
1073 const float2 center_of_mass = primitive_center_of_mass(ptd);
1074
1075 const float scale = math::length(end - center_of_mass) / math::length(start - center_of_mass);
1076
1077 for (const int point_index : ptd.control_points.index_range()) {
1078 const float2 start_pos2 = primitive_local_to_screen(ptd, ptd.temp_control_points[point_index]);
1079
1080 const float2 pos2 = (start_pos2 - center_of_mass) * scale + center_of_mass;
1081 const float3 pos = ptd.placement.project(pos2);
1082 ptd.control_points[point_index] = pos;
1083 }
1084}
1085
1087{
1088 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1089 const int value = RNA_int_get(&brush_ptr, "size");
1090
1091 ptd.initial_value.emplace<int>(value);
1092 ptd.reference_position_2d = ptd.start_position_2d - float2(value, 0.0f);
1093}
1094
1096{
1097 const float display_size = 200.0f * UI_SCALE_FAC;
1098
1099 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1100 const float value = RNA_float_get(&brush_ptr, "strength");
1101
1102 ptd.initial_value.emplace<float>(value);
1103 ptd.reference_position_2d = ptd.start_position_2d - float2(value * display_size, 0.0f);
1104}
1105
1107{
1108 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1109 RNA_int_set(&brush_ptr, "size", std::get<int>(ptd.initial_value));
1110}
1111
1113{
1114 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1115 RNA_float_set(&brush_ptr, "strength", std::get<float>(ptd.initial_value));
1116}
1117
1119 const wmEvent *event)
1120{
1121 /* Clamp reference position if mouse moves past the limits. */
1122 const float2 mouse_co = float2(event->mval);
1123 ptd.reference_position_2d.x = std::min(ptd.reference_position_2d.x, mouse_co.x);
1124 const float2 delta = mouse_co - ptd.reference_position_2d;
1125 /* Clamp to work around brush property getting "stuck" on zero. */
1126 const int new_value = std::max(int(delta.x), 1);
1127
1128 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1129 RNA_int_set(&brush_ptr, "size", new_value);
1130}
1131
1133 const wmEvent *event)
1134{
1135 const float display_size = 200.0f * UI_SCALE_FAC;
1136
1137 /* Clamp reference position if mouse moves past the limits. */
1138 const float2 mouse_co = float2(event->mval);
1139 ptd.reference_position_2d.x = std::max(std::min(ptd.reference_position_2d.x, mouse_co.x),
1140 mouse_co.x - display_size);
1141 const float2 delta = mouse_co - ptd.reference_position_2d;
1142 const float new_value = delta.x / display_size;
1143
1144 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1145 RNA_float_set(&brush_ptr, "strength", new_value);
1146}
1147
1148static int primitive_check_ui_hover(const PrimitiveToolOperation &ptd, const wmEvent *event)
1149{
1150 float closest_distance_squared = std::numeric_limits<float>::max();
1151 int closest_point = -1;
1152
1153 for (const int i : ptd.control_points.index_range()) {
1154 const int point = (ptd.control_points.size() - 1) - i;
1155 const float2 pos_proj = primitive_local_to_screen(ptd, ptd.control_points[point]);
1156 const float radius_sq = ui_point_hit_size_px * ui_point_hit_size_px;
1157 const float distance_squared = math::distance_squared(pos_proj, float2(event->mval));
1158 /* If the mouse is over a control point. */
1159 if (distance_squared <= radius_sq) {
1160 return point;
1161 }
1162
1163 const ControlPointType control_point_type = get_control_point_type(ptd, point);
1164
1165 /* Save the closest handle point. */
1166 if (distance_squared < closest_distance_squared &&
1167 control_point_type == ControlPointType::HandlePoint &&
1169 {
1170 closest_point = point;
1171 closest_distance_squared = distance_squared;
1172 }
1173 }
1174
1175 if (closest_point != -1) {
1176 return closest_point;
1177 }
1178
1179 return -1;
1180}
1181
1184 const wmEvent *event)
1185{
1186 wmWindow *win = CTX_wm_window(C);
1187
1188 if (ptd.mode != OperatorMode::Idle) {
1190 return;
1191 }
1192
1193 const int ui_id = primitive_check_ui_hover(ptd, event);
1194 ptd.active_control_point_index = ui_id;
1195 if (ui_id == -1) {
1196 if (ptd.type == PrimitiveType::Polyline) {
1198 return;
1199 }
1200
1202 return;
1203 }
1204
1206}
1207
1209 wmOperator *op,
1211 const wmEvent *event)
1212{
1213 switch (event->val) {
1214 case int(ModalKeyMode::Cancel): {
1217
1218 return OPERATOR_CANCELLED;
1219 }
1220 case int(ModalKeyMode::Confirm): {
1221 grease_pencil_primitive_exit(C, op, false);
1222
1223 return OPERATOR_FINISHED;
1224 }
1225 case int(ModalKeyMode::Extrude): {
1226 if (ptd.mode == OperatorMode::Idle &&
1228 {
1231
1233 const float3 pos = ptd.placement.project(ptd.start_position_2d);
1234
1235 const int number_control_points = control_points_per_segment(ptd);
1236 ptd.control_points.append_n_times(pos, number_control_points);
1238 ptd.segments++;
1239
1241 }
1242
1243 if (ptd.type == PrimitiveType::Polyline &&
1245 {
1248
1251 const float3 pos = ptd.placement.project(float2(event->mval));
1252
1253 /* If we have only two points and they're the same then don't extrude new a point. */
1254 if (ptd.segments == 1 &&
1255 math::distance_squared(ptd.control_points.first(), ptd.control_points.last()) == 0.0f)
1256 {
1257 ptd.control_points.last() = pos;
1258 }
1259 else {
1260 ptd.control_points.append(pos);
1261 ptd.segments++;
1262 }
1263
1265 }
1266
1268 }
1269 case int(ModalKeyMode::Grab): {
1270 if (ptd.mode == OperatorMode::Idle) {
1271 ptd.start_position_2d = float2(event->mval);
1273
1275 }
1277 }
1278 case int(ModalKeyMode::Rotate): {
1279 if (ptd.mode == OperatorMode::Idle) {
1280 ptd.start_position_2d = float2(event->mval);
1282
1284 }
1286 }
1287 case int(ModalKeyMode::Scale): {
1288 if (ptd.mode == OperatorMode::Idle) {
1289 ptd.start_position_2d = float2(event->mval);
1291
1293 }
1295 }
1297 if (event->val != KM_RELEASE) {
1298 ptd.subdivision++;
1299 RNA_int_set(op->ptr, "subdivision", ptd.subdivision);
1300 }
1302 }
1304 if (event->val != KM_RELEASE) {
1305 ptd.subdivision--;
1306 ptd.subdivision = std::max(ptd.subdivision, 0);
1307 RNA_int_set(op->ptr, "subdivision", ptd.subdivision);
1308 }
1310 }
1311 case int(ModalKeyMode::ChangeRadius): {
1312 if (ptd.mode == OperatorMode::Idle) {
1313 ptd.start_position_2d = float2(event->mval);
1316
1318 }
1320 }
1321 case int(ModalKeyMode::ChangeOpacity): {
1322 if (ptd.mode == OperatorMode::Idle) {
1323 ptd.start_position_2d = float2(event->mval);
1326
1328 }
1330 }
1331 }
1332
1334}
1335
1337 const wmEvent *event)
1338{
1339 if (event->val == KM_RELEASE && ELEM(ptd.mode,
1348 {
1351 }
1352
1353 if (ptd.mode == OperatorMode::Idle && event->val == KM_PRESS) {
1354 const int ui_id = primitive_check_ui_hover(ptd, event);
1355 ptd.active_control_point_index = ui_id;
1356 if (ui_id == -1) {
1357 if (ptd.type != PrimitiveType::Polyline) {
1358 ptd.start_position_2d = float2(event->mval);
1360
1362
1364 }
1365 }
1366 else {
1367 const ControlPointType control_point_type = get_control_point_type(ptd, ui_id);
1368
1369 if (control_point_type == ControlPointType::JoinPoint) {
1373
1375 }
1376 else if (control_point_type == ControlPointType::HandlePoint) {
1377 ptd.start_position_2d = float2(event->mval);
1379
1381 }
1382
1384 }
1385 }
1386
1387 if (ptd.type == PrimitiveType::Polyline && ptd.mode == OperatorMode::Idle &&
1388 event->val == KM_PRESS)
1389 {
1392
1394 const float3 pos = ptd.placement.project(float2(event->mval));
1395
1396 /* If we have only two points and they're the same then don't extrude new a point. */
1397 if (ptd.segments == 1 &&
1398 math::distance_squared(ptd.control_points.first(), ptd.control_points.last()) == 0.0f)
1399 {
1400 ptd.control_points.last() = pos;
1401 }
1402 else {
1403 ptd.control_points.append(pos);
1404 ptd.segments++;
1405 }
1406 }
1407
1409}
1410
1412 const wmEvent *event)
1413{
1414 switch (ptd.mode) {
1417 break;
1418 }
1419 case OperatorMode::Grab: {
1421 break;
1422 }
1423 case OperatorMode::Drag: {
1425 break;
1426 }
1427 case OperatorMode::DragAll: {
1429 break;
1430 }
1433 break;
1434 }
1437 break;
1438 }
1441 break;
1442 }
1445 break;
1446 }
1447 case OperatorMode::Idle: {
1448 /* Do nothing. */
1449 break;
1450 }
1451 }
1452}
1453
1454/* Modal handler: Events handling during interactive part. */
1456 wmOperator *op,
1457 const wmEvent *event)
1458{
1459 PrimitiveToolOperation &ptd = *reinterpret_cast<PrimitiveToolOperation *>(op->customdata);
1460
1461 /* Check for confirm before navigation. */
1462 if (event->type == EVT_MODAL_MAP) {
1463 if (event->val == int(ModalKeyMode::Confirm)) {
1464 grease_pencil_primitive_exit(C, op, false);
1465
1466 return OPERATOR_FINISHED;
1467 }
1468 }
1469
1470 const float3 pos = ptd.control_points.first();
1471 if (ED_view3d_navigation_do(C, ptd.vod, event, pos)) {
1472 if (ptd.vc.rv3d->rflag & RV3D_NAVIGATING) {
1474
1477
1479 }
1480 }
1481
1484
1485 if (event->type == EVT_MODAL_MAP) {
1486 const wmOperatorStatus return_val = grease_pencil_primitive_event_modal_map(C, op, ptd, event);
1487 if (return_val != OPERATOR_RUNNING_MODAL) {
1488 return return_val;
1489 }
1490 }
1491
1492 switch (event->type) {
1493 case LEFTMOUSE: {
1494 const wmOperatorStatus return_val = grease_pencil_primitive_mouse_event(ptd, event);
1495 if (return_val != OPERATOR_RUNNING_MODAL) {
1496 return return_val;
1497 }
1498
1499 break;
1500 }
1501 case RIGHTMOUSE: {
1502 if (event->val != KM_PRESS) {
1503 break;
1504 }
1505
1506 if (ptd.mode == OperatorMode::Idle) {
1509
1510 return OPERATOR_CANCELLED;
1511 }
1512
1513 if (ptd.mode == OperatorMode::ChangeRadius) {
1515 }
1516 if (ptd.mode == OperatorMode::ChangeOpacity) {
1518 }
1519
1522 break;
1523 }
1524 default: {
1525 break;
1526 }
1527 }
1528
1529 /* Updating is done every event not just `MOUSEMOVE`. */
1532
1533 /* Updates indicator in header. */
1536
1537 /* Still running... */
1539}
1540
1541/* Cancel handler. */
1543{
1544 /* This is just a wrapper around exit() */
1546}
1547
1549 const int default_subdiv,
1550 const PrimitiveType default_type)
1551{
1552 static const EnumPropertyItem grease_pencil_primitive_type[] = {
1553 {int(PrimitiveType::Box), "BOX", 0, "Box", ""},
1554 {int(PrimitiveType::Line), "LINE", 0, "Line", ""},
1555 {int(PrimitiveType::Polyline), "POLYLINE", 0, "Polyline", ""},
1556 {int(PrimitiveType::Circle), "CIRCLE", 0, "Circle", ""},
1557 {int(PrimitiveType::Arc), "ARC", 0, "Arc", ""},
1558 {int(PrimitiveType::Curve), "CURVE", 0, "Curve", ""},
1559 {0, nullptr, 0, nullptr, nullptr},
1560 };
1561
1562 PropertyRNA *prop;
1563
1564 prop = RNA_def_int(ot->srna,
1565 "subdivision",
1566 default_subdiv,
1567 0,
1568 INT_MAX,
1569 "Subdivisions",
1570 "Number of subdivisions per segment",
1571 0,
1572 INT_MAX);
1574
1576 ot->srna, "type", grease_pencil_primitive_type, int(default_type), "Type", "Type of shape");
1577}
1578
1580{
1581 /* Identifiers. */
1582 ot->name = "Grease Pencil Line Shape";
1583 ot->idname = "GREASE_PENCIL_OT_primitive_line";
1584 ot->description = "Create predefined Grease Pencil stroke lines";
1585
1586 /* Callbacks. */
1590
1591 /* Flags. */
1593
1594 /* Properties and Flags. */
1596}
1597
1599{
1600 /* Identifiers. */
1601 ot->name = "Grease Pencil Polyline Shape";
1602 ot->idname = "GREASE_PENCIL_OT_primitive_polyline";
1603 ot->description = "Create predefined Grease Pencil stroke polylines";
1604
1605 /* Callbacks. */
1609
1610 /* Flags. */
1612
1613 /* Properties. */
1615}
1616
1618{
1619 /* Identifiers. */
1620 ot->name = "Grease Pencil Arc Shape";
1621 ot->idname = "GREASE_PENCIL_OT_primitive_arc";
1622 ot->description = "Create predefined Grease Pencil stroke arcs";
1623
1624 /* Callbacks. */
1628
1629 /* Flags. */
1631
1632 /* Properties. */
1634}
1635
1637{
1638 /* Identifiers. */
1639 ot->name = "Grease Pencil Curve Shape";
1640 ot->idname = "GREASE_PENCIL_OT_primitive_curve";
1641 ot->description = "Create predefined Grease Pencil stroke curve shapes";
1642
1643 /* Callbacks. */
1647
1648 /* Flags. */
1650
1651 /* Properties. */
1653}
1654
1656{
1657 /* Identifiers. */
1658 ot->name = "Grease Pencil Box Shape";
1659 ot->idname = "GREASE_PENCIL_OT_primitive_box";
1660 ot->description = "Create predefined Grease Pencil stroke boxes";
1661
1662 /* Callbacks. */
1666
1667 /* Flags. */
1669
1670 /* Properties. */
1672}
1673
1675{
1676 /* Identifiers. */
1677 ot->name = "Grease Pencil Circle Shape";
1678 ot->idname = "GREASE_PENCIL_OT_primitive_circle";
1679 ot->description = "Create predefined Grease Pencil stroke circles";
1680
1681 /* Callbacks. */
1685
1686 /* Flags. */
1688
1689 /* Properties. */
1691}
1692
1693} // namespace blender::ed::greasepencil
1694
1705
1707{
1708 using namespace blender::ed::greasepencil;
1709 static const EnumPropertyItem modal_items[] = {
1710 {int(ModalKeyMode::Cancel), "CANCEL", 0, "Cancel", ""},
1711 {int(ModalKeyMode::Confirm), "CONFIRM", 0, "Confirm", ""},
1712 {int(ModalKeyMode::Panning), "PANNING", 0, "Panning", ""},
1713 {int(ModalKeyMode::Extrude), "EXTRUDE", 0, "Extrude", ""},
1714 {int(ModalKeyMode::Grab), "GRAB", 0, "Grab", ""},
1715 {int(ModalKeyMode::Rotate), "ROTATE", 0, "Rotate", ""},
1716 {int(ModalKeyMode::Scale), "SCALE", 0, "Scale", ""},
1718 "INCREASE_SUBDIVISION",
1719 0,
1720 "Increase Subdivision",
1721 ""},
1723 "DECREASE_SUBDIVISION",
1724 0,
1725 "Decrease Subdivision",
1726 ""},
1727 {int(ModalKeyMode::ChangeRadius), "CHANGE_RADIUS", 0, "Change Radius", ""},
1728 {int(ModalKeyMode::ChangeOpacity), "CHANGE_OPACITY", 0, "Change Opacity", ""},
1729 {0, nullptr, 0, nullptr, nullptr},
1730 };
1731
1732 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Primitive Tool Modal Map");
1733
1734 /* This function is called for each space-type, only needs to add map once. */
1735 if (keymap && keymap->modal_items) {
1736 return;
1737 }
1738
1739 keymap = WM_modalkeymap_ensure(keyconf, "Primitive Tool Modal Map", modal_items);
1740
1741 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_line");
1742 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_polyline");
1743 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_arc");
1744 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_curve");
1745 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_box");
1746 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_circle");
1747}
std::optional< BrushColorJitterSettings > BKE_brush_color_jitter_get_settings(const Paint *paint, const Brush *brush)
Definition brush.cc:1170
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:648
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
void BKE_curvemapping_init(CurveMapping *cumap)
wmWindow * CTX_wm_window(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
Low-level operations for curves.
Low-level operations for grease pencil.
Material * BKE_grease_pencil_object_material_ensure_from_brush(Main *bmain, Object *ob, Brush *brush)
General operations, lookup, etc. for materials.
int BKE_object_material_index_get(Object *ob, const Material *ma)
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
#define D
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE void copy_v3_v3(float r[3], const float a[3])
unsigned int uint
#define ELEM(...)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ GP_BRUSH_GROUP_RANDOM
@ GPPAINT_MODE_STROKE
@ GPPAINT_MODE_FILL
@ GPPAINT_MODE_BOTH
@ GP_STROKE_CAP_TYPE_ROUND
@ GP_MATERIAL_FILL_SHOW
@ GPPAINT_FLAG_USE_VERTEXCOLOR
@ GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE
@ GP_TOOL_FLAG_PAINT_ONBACK
@ GP_TOOL_FLAG_AUTOMERGE_STROKE
#define UI_SCALE_FAC
@ RV3D_NAVIGATING
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OP_IS_MODAL_CURSOR_REGION
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1024
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
void * ED_region_draw_cb_activate(ARegionType *art, void(*draw)(const bContext *, ARegion *, void *), void *customdata, int type)
#define REGION_DRAW_POST_VIEW
bool ED_region_draw_cb_exit(ARegionType *art, void *handle)
blender::float2 ED_view3d_project_float_v2_m4(const ARegion *region, const float co[3], const blender::float4x4 &mat)
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
void ED_view3d_navigation_free(bContext *C, ViewOpsData *vod)
blender::float4x4 ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, const Object *ob)
bool ED_view3d_navigation_do(bContext *C, ViewOpsData *vod, const wmEvent *event, const float depth_loc_override[3])
ViewOpsData * ED_view3d_navigation_init(bContext *C, const wmKeyMapItem *kmi_merge)
void immEnd()
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
void immAttr1f(uint attr_id, float x)
GPUVertFormat * immVertexFormat()
void immAttr4f(uint attr_id, float x, float y, float z, float w)
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
@ GPU_PRIM_POINTS
@ GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR
void GPU_program_point_size(bool enable)
Definition gpu_state.cc:180
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
#define C
Definition RandGen.cpp:29
@ TH_GIZMO_B
@ TH_GIZMO_A
@ TH_GIZMO_PRIMARY
@ TH_GIZMO_SECONDARY
void UI_GetThemeColor4fv(int colorid, float col[4])
#define NC_GEOM
Definition WM_types.hh:393
@ KM_ALT
Definition WM_types.hh:280
@ KM_SHIFT
Definition WM_types.hh:278
#define ND_DATA
Definition WM_types.hh:509
@ KM_PRESS
Definition WM_types.hh:311
@ KM_RELEASE
Definition WM_types.hh:312
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define A
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
ChannelStorageType a
constexpr int64_t first() const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
static constexpr IndexRange from_single(const int64_t index)
constexpr IndexRange index_range() const
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
static RandomNumberGenerator from_random_seed()
Definition rand.cc:288
bool add(const Key &key)
Definition BLI_set.hh:248
void add_multiple(Span< Key > keys)
Definition BLI_set.hh:287
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
static VArray from_single(T value, const int64_t size)
bool contains(StringRef attribute_id) const
IndexRange curves_range() const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
MutableSpan< float > opacities_for_write()
MutableSpan< float > radii_for_write()
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
MutableSpan< ColorGeometry4f > fill_colors_for_write()
MutableSpan< ColorGeometry4f > vertex_colors_for_write()
void set_texture_matrices(Span< float4x2 > matrices, const IndexMask &selection)
float4x4 to_world_space(const Object &object) const
void cache_viewport_depths(Depsgraph *depsgraph, ARegion *region, View3D *view3d)
float3 project(float2 co, bool &clipped) const
nullptr float
static bool is_cyclic(const Nurb *nu)
#define rot(x, k)
uint pos
VecBase< float, D > normalize(VecOp< float, D >) RET
constexpr T sign(T) RET
#define abs
void ED_primitivetool_modal_keymap(wmKeyConfig *keyconf)
void ED_operatortypes_grease_pencil_primitives()
#define B
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
auto attribute_filter_from_skip_ref(const Span< StringRef > skip)
void fill_attribute_range_default(MutableAttributeAccessor dst_attributes, AttrDomain domain, const AttributeFilter &attribute_filter, IndexRange range)
static void grease_pencil_primitive_init_opacity(PrimitiveToolOperation &ptd)
static constexpr int control_point_center
static void grease_pencil_primitive_change_opacity(PrimitiveToolOperation &ptd, const wmEvent *event)
static int control_points_per_segment(const PrimitiveToolOperation &ptd)
static void grease_pencil_primitive_cancel_opacity(PrimitiveToolOperation &ptd)
float randomize_rotation(const BrushGpencilSettings &settings, const float stroke_factor, const float distance, const float pressure)
static constexpr float ui_tertiary_point_draw_size_px
void resize_single_curve(bke::CurvesGeometry &curves, const bool at_end, const int new_points_num)
static void grease_pencil_primitive_cancel(bContext *C, wmOperator *op)
static void grease_pencil_primitive_grab_update(PrimitiveToolOperation &ptd, const wmEvent *event)
static void grease_pencil_primitive_operator_update(PrimitiveToolOperation &ptd, const wmEvent *event)
static void grease_pencil_primitive_cancel_radius(PrimitiveToolOperation &ptd)
float randomize_opacity(const BrushGpencilSettings &settings, const float stroke_factor, const float distance, const float opacity, const float pressure)
static void GREASE_PENCIL_OT_primitive_circle(wmOperatorType *ot)
static float2 snap_diagonals_box(float2 p)
static constexpr int control_point_first
static float2 snap_8_angles(float2 p)
float opacity_from_input_sample(const float pressure, const Brush *brush, const BrushGpencilSettings *settings)
ColorGeometry4f randomize_color(const BrushGpencilSettings &settings, const std::optional< BrushColorJitterSettings > &jitter, const float stroke_hue_factor, const float stroke_saturation_factor, const float stroke_value_factor, const float distance, const ColorGeometry4f color, const float pressure)
static wmOperatorStatus grease_pencil_primitive_event_modal_map(bContext *C, wmOperator *op, PrimitiveToolOperation &ptd, const wmEvent *event)
void add_single_curve(bke::CurvesGeometry &curves, const bool at_end)
static void grease_pencil_primitive_change_radius(PrimitiveToolOperation &ptd, const wmEvent *event)
float randomize_radius(const BrushGpencilSettings &settings, const float stroke_factor, const float distance, const float radius, const float pressure)
static void grease_pencil_primitive_drag_update(PrimitiveToolOperation &ptd, const wmEvent *event)
static void grease_pencil_primitive_init_radius(PrimitiveToolOperation &ptd)
static void primitive_calulate_curve_positions(PrimitiveToolOperation &ptd, Span< float2 > control_points, MutableSpan< float2 > new_positions)
static void control_point_colors_and_sizes(const PrimitiveToolOperation &ptd, MutableSpan< ColorGeometry4f > colors, MutableSpan< float > sizes)
static void grease_pencil_primitive_extruding_update(PrimitiveToolOperation &ptd, const wmEvent *event)
static int grease_pencil_primitive_curve_points_number(PrimitiveToolOperation &ptd)
static void grease_pencil_primitive_update_view(bContext *C, PrimitiveToolOperation &ptd)
static void grease_pencil_primitive_draw(const bContext *, ARegion *, void *arg)
static void draw_control_points(PrimitiveToolOperation &ptd)
static void grease_pencil_primitive_update_curves(PrimitiveToolOperation &ptd)
static float2 primitive_center_of_mass(const PrimitiveToolOperation &ptd)
static void grease_pencil_primitive_drag_all_update(PrimitiveToolOperation &ptd, const wmEvent *event)
static constexpr float ui_secondary_point_draw_size_px
bke::CurvesGeometry curves_merge_endpoints_by_distance(const ARegion &region, const bke::CurvesGeometry &src_curves, const float4x4 &layer_to_world, const float merge_distance, const IndexMask &selection, const bke::AttributeFilter &attribute_filter)
static constexpr float ui_point_hit_size_px
static constexpr int control_point_last
static constexpr float ui_point_max_hit_size_px
static void grease_pencil_primitive_init_curves(PrimitiveToolOperation &ptd)
static void grease_pencil_primitive_undo_curves(PrimitiveToolOperation &ptd)
static wmOperatorStatus grease_pencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void GREASE_PENCIL_OT_primitive_line(wmOperatorType *ot)
static void grease_pencil_primitive_scale_all_update(PrimitiveToolOperation &ptd, const wmEvent *event)
static void primitive_calulate_curve_positions_2d(PrimitiveToolOperation &ptd, MutableSpan< float2 > new_positions)
static ControlPointType get_control_point_type(const PrimitiveToolOperation &ptd, const int point)
wmOperatorStatus grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op, const bool use_duplicate_previous_key)
static constexpr float ui_primary_point_draw_size_px
static void grease_pencil_primitive_common_props(wmOperatorType *ot, const int default_subdiv, const PrimitiveType default_type)
float radius_from_input_sample(const RegionView3D *rv3d, const ARegion *region, const Brush *brush, const float pressure, const float3 &location, const float4x4 &to_world, const BrushGpencilSettings *settings)
static void grease_pencil_primitive_rotate_all_update(PrimitiveToolOperation &ptd, const wmEvent *event)
float4x2 calculate_texture_space(const Scene *scene, const ARegion *region, const float2 &mouse, const DrawingPlacement &placement)
static void grease_pencil_primitive_exit(bContext *C, wmOperator *op, const bool cancelled)
static void grease_pencil_primitive_cursor_update(bContext *C, PrimitiveToolOperation &ptd, const wmEvent *event)
static void grease_pencil_primitive_load(PrimitiveToolOperation &ptd)
static void grease_pencil_primitive_status_indicators(bContext *C, wmOperator *op, PrimitiveToolOperation &ptd)
static void GREASE_PENCIL_OT_primitive_polyline(wmOperatorType *ot)
static void GREASE_PENCIL_OT_primitive_curve(wmOperatorType *ot)
static float2 primitive_local_to_screen(const PrimitiveToolOperation &ptd, const float3 &point)
static void grease_pencil_primitive_save(PrimitiveToolOperation &ptd)
static float2 snap_diagonals(float2 p)
static void GREASE_PENCIL_OT_primitive_box(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus grease_pencil_primitive_mouse_event(PrimitiveToolOperation &ptd, const wmEvent *event)
static void GREASE_PENCIL_OT_primitive_arc(wmOperatorType *ot)
static int primitive_check_ui_hover(const PrimitiveToolOperation &ptd, const wmEvent *event)
T cos(const AngleRadianBase< T > &a)
T distance(const T &a, const T &b)
T length(const VecBase< T, Size > &a)
T interpolate(const T &a, const T &b, const FactorT &t)
T atan2(const T &y, const T &x)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
T sin(const AngleRadianBase< T > &a)
T mod(const T &a, const T &b)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
MatBase< float, 2, 2 > float2x2
MatBase< float, 4, 4 > float4x4
VecBase< float, 2 > float2
MatBase< float, 4, 2 > float4x2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
VecBase< float, 3 > float3
const int status
#define sinf
#define cosf
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_id_pointer_create(ID *id)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
ARegionRuntimeHandle * runtime
struct CurveMapping * curve_sensitivity
struct CurveMapping * curve_strength
struct CurveMapping * curve_jitter
struct CurveMapping * curve_rand_pressure
struct CurveMapping * curve_rand_strength
struct CurveMapping * curve_rand_uv
float alpha
struct CurveMapping * curve_rand_saturation
struct CurveMapping * curve_rand_hue
struct CurveMapping * curve_rand_value
struct BrushGpencilSettings * gpencil_settings
float color[3]
struct CurveMapping * cur_primitive
struct MaterialGPencilStyle * gp_style
struct ToolSettings * toolsettings
struct RenderData r
struct GP_Sculpt_Settings gp_sculpt
RegionView3D * rv3d
Definition ED_view3d.hh:80
ARegion * region
Definition ED_view3d.hh:77
Scene * scene
Definition ED_view3d.hh:73
wmWindow * win
Definition ED_view3d.hh:79
Object * obact
Definition ED_view3d.hh:75
wmEventModifierFlag modifier
Definition WM_types.hh:774
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
int mval[2]
Definition WM_types.hh:763
const void * modal_items
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_HAND
Definition wm_cursors.hh:22
@ WM_CURSOR_NSEW_SCROLL
Definition wm_cursors.hh:52
@ WM_CURSOR_CROSS
Definition wm_cursors.hh:26
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ RIGHTMOUSE
@ EVT_MODAL_MAP
@ LEFTMOUSE
wmOperatorType * ot
Definition wm_files.cc:4237
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:932
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:959
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))