Blender V4.5
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_interface.hh"
52#include "UI_resources.hh"
53
55
56enum class PrimitiveType : int8_t {
57 Line = 0,
59 Arc = 2,
60 Curve = 3,
61 Box = 4,
62 Circle = 5,
63};
64
65enum class OperatorMode : int8_t {
66 Idle = 0,
68 /* Set the active control point to the mouse. */
69 Grab = 2,
70 /* Move the active control point. */
71 Drag = 3,
72 /* Move all control points. */
74 /* Rotate all control points. */
76 /* Scale all control points. */
78 /* Change brush radius. */
80 /* Change brush opacity. */
82};
83
84enum class ControlPointType : int8_t {
85 /* The points that are at the end of segments. */
87 /* The points inside of the segments not including the end points. */
89};
90
104
105static constexpr float ui_primary_point_draw_size_px = 8.0f;
106static constexpr float ui_secondary_point_draw_size_px = 5.0f;
107static constexpr float ui_tertiary_point_draw_size_px = 3.0f;
108static constexpr float ui_point_hit_size_px = 20.0f;
109static constexpr float ui_point_max_hit_size_px = 600.0f;
110
111/* These three points are only used for `Box` and `Circle` type. */
112static constexpr int control_point_first = 0;
113static constexpr int control_point_center = 1;
114static constexpr int control_point_last = 2;
115
118 /* For drawing preview loop. */
121
123 /* Stored in layer space. */
125 /* Store the control points temporally. */
128
132 /* Helper class to project screen space coordinates to 3D. */
134
138 std::optional<ColorGeometry4f> vertex_color;
139 std::optional<ColorGeometry4f> fill_color;
143 float softness;
147
155
159
160 /* Reference mouse position for initial radial control value. */
162 /* Initial value of radius or opacity. */
163 std::variant<int, float> initial_value;
164
166};
167
169{
170 switch (ptd.type) {
172 case PrimitiveType::Line: {
173 return 1;
174 }
177 case PrimitiveType::Arc: {
178 return 2;
179 }
181 return 3;
182 }
183 }
184
186 return 0;
187}
188
190{
191 BLI_assert(point != -1);
194 }
195
196 const int num_shared_points = control_points_per_segment(ptd);
197 if (math::mod(point, num_shared_points) == 0) {
199 }
201}
202
205 MutableSpan<float> sizes)
206{
207 ColorGeometry4f color_gizmo_primary;
208 ColorGeometry4f color_gizmo_secondary;
209 ColorGeometry4f color_gizmo_a;
210 ColorGeometry4f color_gizmo_b;
211 UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color_gizmo_primary);
212 UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color_gizmo_secondary);
213 UI_GetThemeColor4fv(TH_GIZMO_A, color_gizmo_a);
214 UI_GetThemeColor4fv(TH_GIZMO_B, color_gizmo_b);
215
216 const float size_primary = ui_primary_point_draw_size_px;
217 const float size_secondary = ui_secondary_point_draw_size_px;
218 const float size_tertiary = ui_tertiary_point_draw_size_px;
219
220 if (ptd.segments == 0) {
221 colors.fill(color_gizmo_primary);
222 sizes.fill(size_primary);
223 return;
224 }
225
227 colors.fill(color_gizmo_primary);
228 sizes.fill(size_primary);
229
230 /* Set the center point's color. */
231 colors[control_point_center] = color_gizmo_b;
232 sizes[control_point_center] = size_secondary;
233 }
234 else {
235 colors.fill(color_gizmo_secondary);
236 sizes.fill(size_secondary);
237
238 for (const int i : colors.index_range()) {
239 const ControlPointType control_point_type = get_control_point_type(ptd, i);
240
241 if (control_point_type == ControlPointType::JoinPoint) {
242 colors[i] = color_gizmo_b;
243 sizes[i] = size_tertiary;
244 }
245 }
246
247 colors.last() = color_gizmo_primary;
248 sizes.last() = size_tertiary;
249
251 colors.last(1) = color_gizmo_secondary;
252 sizes.last(1) = size_primary;
253 }
254 }
255
256 const int active_index = ptd.active_control_point_index;
257 if (active_index != -1) {
258 sizes[active_index] *= 1.5;
259 colors[active_index] = math::interpolate(colors[active_index], color_gizmo_a, 0.5f);
260 }
261}
262
264{
265 GPUVertFormat *format3d = immVertexFormat();
266 const uint pos3d = GPU_vertformat_attr_add(format3d, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
267 const uint col3d = GPU_vertformat_attr_add(format3d, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
268 const uint siz3d = GPU_vertformat_attr_add(format3d, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
270
273
274 Array<ColorGeometry4f> colors(ptd.control_points.size());
275 Array<float> sizes(ptd.control_points.size());
276 control_point_colors_and_sizes(ptd, colors, sizes);
277
278 for (const int point : ptd.control_points.index_range()) {
280 ptd.control_points[point]);
281 const ColorGeometry4f color = colors[point];
282 const float size = sizes[point];
283
284 immAttr4f(col3d, color[0], color[1], color[2], color[3]);
285 immAttr1f(siz3d, size * 2.0f);
286 immVertex3fv(pos3d, world_pos);
287 }
288
289 immEnd();
292}
293
294static void grease_pencil_primitive_draw(const bContext * /*C*/, ARegion * /*region*/, void *arg)
295{
296 PrimitiveToolOperation &ptd = *reinterpret_cast<PrimitiveToolOperation *>(arg);
298}
299
301{
302 ptd.temp_segments = ptd.segments;
303 ptd.temp_control_points.resize(ptd.control_points.size());
304 array_utils::copy(ptd.control_points.as_span(), ptd.temp_control_points.as_mutable_span());
305}
306
308{
309 ptd.segments = ptd.temp_segments;
310 ptd.control_points.resize(ptd.temp_control_points.size());
311 array_utils::copy(ptd.temp_control_points.as_span(), ptd.control_points.as_mutable_span());
312}
313
315 Span<float2> control_points,
316 MutableSpan<float2> new_positions)
317{
318 const int subdivision = ptd.subdivision;
319 const int new_points_num = new_positions.size();
320
321 if (ptd.segments == 0) {
322 new_positions.fill(control_points.last());
323 return;
324 }
325
326 switch (ptd.type) {
329 for (const int i : new_positions.index_range().drop_back(1)) {
330 const float t = math::mod(i / float(subdivision + 1), 1.0f);
331 const int point = int(i / (subdivision + 1));
332 const int point_next = point + 1;
333 new_positions[i] = math::interpolate(control_points[point], control_points[point_next], t);
334 }
335 new_positions.last() = control_points.last();
336 return;
337 }
338 case PrimitiveType::Arc: {
339 const int num_shared_points = control_points_per_segment(ptd);
340 const int num_segments = ptd.segments;
341 for (const int segment_i : IndexRange(num_segments)) {
342 const float2 A = control_points[num_shared_points * segment_i + 0];
343 const float2 B = control_points[num_shared_points * segment_i + 1];
344 const float2 C = control_points[num_shared_points * segment_i + 2];
345 for (const int i : IndexRange(subdivision + 1)) {
346 const float t = i / float(subdivision + 1);
347 const float2 AB = math::interpolate(A, B, t);
348 const float2 BC = math::interpolate(B, C, t);
349 new_positions[i + segment_i * (subdivision + 1)] = math::interpolate(AB, BC, t);
350 }
351 }
352 new_positions.last() = control_points.last();
353 return;
354 }
356 const int num_shared_points = control_points_per_segment(ptd);
357 const int num_segments = ptd.segments;
358
359 for (const int segment_i : IndexRange(num_segments)) {
360 const float2 A = control_points[num_shared_points * segment_i + 0];
361 const float2 B = control_points[num_shared_points * segment_i + 1];
362 const float2 C = control_points[num_shared_points * segment_i + 2];
363 const float2 D = control_points[num_shared_points * segment_i + 3];
364 for (const int i : IndexRange(subdivision + 1)) {
365 const float t = i / float(subdivision + 1);
366 const float2 AB = math::interpolate(A, B, t);
367 const float2 BC = math::interpolate(B, C, t);
368 const float2 CD = math::interpolate(C, D, t);
369 const float2 ABBC = math::interpolate(AB, BC, t);
370 const float2 BCCD = math::interpolate(BC, CD, t);
371 new_positions[i + segment_i * (subdivision + 1)] = math::interpolate(ABBC, BCCD, t);
372 }
373 }
374 new_positions.last() = control_points.last();
375 return;
376 }
378 const float2 center = control_points[control_point_center];
379 const float2 offset = control_points[control_point_first] - center;
380 for (const int i : new_positions.index_range()) {
381 const float t = i / float(new_points_num);
382 const float a = t * math::numbers::pi * 2.0f;
383 new_positions[i] = offset * float2(sinf(a), cosf(a)) + center;
384 }
385 return;
386 }
387 case PrimitiveType::Box: {
388 const float2 center = control_points[control_point_center];
389 const float2 offset = control_points[control_point_first] - center;
390 /*
391 * Calculate the 4 corners of the box.
392 * Here's a diagram.
393 *
394 * +-----------+
395 * |A B|
396 * | |
397 * | center |
398 * | |
399 * |D C|
400 * +-----------+
401 *
402 */
403 const float2 A = center + offset * float2(1.0f, 1.0f);
404 const float2 B = center + offset * float2(-1.0f, 1.0f);
405 const float2 C = center + offset * float2(-1.0f, -1.0f);
406 const float2 D = center + offset * float2(1.0f, -1.0f);
407 const float2 corners[4] = {A, B, C, D};
408 for (const int i : new_positions.index_range()) {
409 const float t = math::mod(i / float(subdivision + 1), 1.0f);
410 const int point = int(i / (subdivision + 1));
411 const int point_next = math::mod(point + 1, 4);
412 new_positions[i] = math::interpolate(corners[point], corners[point_next], t);
413 }
414 return;
415 }
416 }
417}
418
424
426 MutableSpan<float2> new_positions)
427{
428 Array<float2> control_points_2d(ptd.control_points.size());
429 for (const int i : ptd.control_points.index_range()) {
430 control_points_2d[i] = primitive_local_to_screen(ptd, ptd.control_points[i]);
431 }
432
433 primitive_calulate_curve_positions(ptd, control_points_2d, new_positions);
434}
435
437{
438 const int subdivision = ptd.subdivision;
439
440 switch (ptd.type) {
445 case PrimitiveType::Arc: {
446 const int join_points = ptd.segments + 1;
447 return join_points + subdivision * ptd.segments;
448 break;
449 }
450 case PrimitiveType::Box: {
451 return 4 + subdivision * 4;
452 break;
453 }
454 }
455
457 return 0;
458}
459
461{
462 const bool on_back = ptd.on_back;
463 const int new_points_num = grease_pencil_primitive_curve_points_number(ptd);
464 const bool use_random = (ptd.settings->flag & GP_BRUSH_GROUP_RANDOM) != 0;
465
467 const int target_curve_index = on_back ? 0 : curves.curves_range().last();
468 ed::greasepencil::resize_single_curve(curves, on_back == false, new_points_num);
469
470 const IndexRange curve_points = curves.points_by_curve()[target_curve_index];
471
472 MutableSpan<float3> positions_3d = curves.positions_for_write().slice(curve_points);
473 Array<float2> positions_2d(new_points_num);
474
475 primitive_calulate_curve_positions_2d(ptd, positions_2d);
476 ptd.placement.project(positions_2d, positions_3d);
477
478 Set<std::string> point_attributes_to_skip;
479
480 MutableSpan<float> new_radii = ptd.drawing->radii_for_write().slice(curve_points);
481 MutableSpan<float> new_opacities = ptd.drawing->opacities_for_write().slice(curve_points);
482 MutableSpan<ColorGeometry4f> new_vertex_colors = ptd.drawing->vertex_colors_for_write().slice(
483 curve_points);
485 MutableSpan<float> new_rotations;
486 if (use_random && ptd.settings->uv_random > 0.0f) {
487 rotations = curves.attributes_for_write().lookup_or_add_for_write_span<float>(
488 "rotation", bke::AttrDomain::Point);
489 new_rotations = rotations.span.slice(curve_points);
490 }
491
492 const ToolSettings *ts = ptd.vc.scene->toolsettings;
493 const GP_Sculpt_Settings *gset = &ts->gp_sculpt;
494
495 /* Screen-space length along curve used as randomization parameter. */
496 Array<float> lengths(new_points_num);
497
498 for (const int point : curve_points.index_range()) {
499 float pressure = 1.0f;
500 /* Apply pressure curve. */
502 const float t = point / float(new_points_num - 1);
503 pressure = BKE_curvemapping_evaluateF(gset->cur_primitive, 0, t);
504 }
505
506 const float radius = ed::greasepencil::radius_from_input_sample(ptd.vc.rv3d,
507 ptd.region,
508 ptd.brush,
509 pressure,
510 positions_3d[point],
512 ptd.settings);
514 pressure, ptd.brush, ptd.settings);
515
516 if (point == 0) {
517 lengths[point] = 0.0f;
518 }
519 else {
520 const float distance_2d = math::distance(positions_2d[point - 1], positions_2d[point]);
521 lengths[point] = lengths[point - 1] + distance_2d;
522 }
523
524 new_radii[point] = ed::greasepencil::randomize_radius(
525 *ptd.settings, ptd.stroke_random_radius_factor, lengths[point], radius, pressure);
526 new_opacities[point] = ed::greasepencil::randomize_opacity(
527 *ptd.settings, ptd.stroke_random_opacity_factor, lengths[point], opacity, pressure);
528 if (ptd.vertex_color) {
529 std::optional<BrushColorJitterSettings> jitter_settings =
531 ptd.vc.scene, &ptd.vc.scene->toolsettings->gp_paint->paint, ptd.brush);
532 new_vertex_colors[point] = ed::greasepencil::randomize_color(*ptd.settings,
533 jitter_settings,
537 lengths[point],
538 *ptd.vertex_color,
539 pressure);
540 }
541 if (rotations) {
542 new_rotations[point] = ed::greasepencil::randomize_rotation(
543 *ptd.settings, ptd.stroke_random_rotation_factor, lengths[point], pressure);
544 }
545 }
546
547 point_attributes_to_skip.add_multiple({"position", "radius", "opacity"});
548 if (ptd.vertex_color) {
549 point_attributes_to_skip.add("vertex_color");
550 }
551 if (rotations) {
552 point_attributes_to_skip.add("rotation");
553 rotations.finish();
554 }
555
556 /* Initialize the rest of the attributes with default values. */
557 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
560 bke::attribute_filter_from_skip_ref(point_attributes_to_skip),
561 curve_points);
562
565 IndexRange::from_single(target_curve_index));
566}
567
569{
570 /* Resize the curves geometry so there is one more curve with a single point. */
572
573 const bool on_back = ptd.on_back;
575
576 const int target_curve_index = on_back ? 0 : (curves.curves_num() - 1);
577
578 Set<std::string> curve_attributes_to_skip;
579
580 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
582 "material_index", bke::AttrDomain::Curve);
583 materials.span[target_curve_index] = ptd.material_index;
584 materials.finish();
585 curve_attributes_to_skip.add("material_index");
586
588 "cyclic", bke::AttrDomain::Curve);
590 cyclic.span[target_curve_index] = is_cyclic;
591 cyclic.finish();
592 curve_attributes_to_skip.add("cyclic");
593
594 if (bke::SpanAttributeWriter<float> softness = attributes.lookup_or_add_for_write_span<float>(
595 "softness", bke::AttrDomain::Curve))
596 {
597 softness.span[target_curve_index] = ptd.softness;
598 softness.finish();
599 curve_attributes_to_skip.add("softness");
600 }
601
602 /* Only set the attribute if the type is not the default or if it already exists. */
603 if (ptd.settings->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("start_cap")) {
604 if (bke::SpanAttributeWriter<int8_t> start_caps =
605 attributes.lookup_or_add_for_write_span<int8_t>("start_cap", bke::AttrDomain::Curve))
606 {
607 start_caps.span[target_curve_index] = ptd.settings->caps_type;
608 start_caps.finish();
609 curve_attributes_to_skip.add("start_cap");
610 }
611 }
612
613 if (ptd.settings->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("end_cap")) {
615 attributes.lookup_or_add_for_write_span<int8_t>("end_cap", bke::AttrDomain::Curve))
616 {
617 end_caps.span[target_curve_index] = ptd.settings->caps_type;
618 end_caps.finish();
619 curve_attributes_to_skip.add("end_cap");
620 }
621 }
622
623 if (ptd.use_fill && (ptd.fill_opacity < 1.0f || attributes.contains("fill_opacity"))) {
624 if (bke::SpanAttributeWriter<float> fill_opacities =
625 attributes.lookup_or_add_for_write_span<float>(
626 "fill_opacity",
629 {
630 fill_opacities.span[target_curve_index] = ptd.fill_opacity;
631 fill_opacities.finish();
632 curve_attributes_to_skip.add("fill_opacity");
633 }
634 }
635
636 if (ptd.fill_color) {
637 ptd.drawing->fill_colors_for_write()[target_curve_index] = *ptd.fill_color;
638 curve_attributes_to_skip.add("fill_color");
639 }
640
642 "u_scale", bke::AttrDomain::Curve))
643 {
644 u_scale.span[target_curve_index] = 1.0f;
645 u_scale.finish();
646 curve_attributes_to_skip.add("u_scale");
647 }
648
649 curves.curve_types_for_write()[target_curve_index] = CURVE_TYPE_POLY;
650 curves.update_curve_types();
651 curve_attributes_to_skip.add("curve_type");
652
653 /* Initialize the rest of the attributes with default values. */
656 bke::attribute_filter_from_skip_ref(curve_attributes_to_skip),
657 on_back ? curves.curves_range().take_front(1) :
658 curves.curves_range().take_back(1));
660}
661
663{
665 const int target_curve_index = ptd.on_back ? 0 : (curves.curves_num() - 1);
666 curves.remove_curves(IndexRange::from_single(target_curve_index), {});
668}
669
670/* Helper: Draw status message while the user is running the operator. */
672 wmOperator *op,
674{
675 WorkspaceStatus status(C);
676 status.opmodal(IFACE_("Confirm"), op->type, int(ModalKeyMode::Confirm));
677 status.opmodal(IFACE_("Cancel"), op->type, int(ModalKeyMode::Cancel));
678 status.opmodal(IFACE_("Panning"), op->type, int(ModalKeyMode::Panning));
679 status.item(IFACE_("Align"), ICON_EVENT_SHIFT);
680 status.opmodal("", op->type, int(ModalKeyMode::IncreaseSubdivision));
681 status.opmodal("", op->type, int(ModalKeyMode::DecreaseSubdivision));
682 status.item(fmt::format("{} ({})", IFACE_("Subdivisions"), ptd.subdivision), ICON_NONE);
683
684 if (ptd.segments == 1) {
685 status.item(IFACE_("Center"), ICON_EVENT_ALT);
686 }
687
688 if (ELEM(ptd.type,
693 {
694 status.opmodal(IFACE_("Extrude"), op->type, int(ModalKeyMode::Extrude));
695 }
696
697 status.opmodal(IFACE_("Grab"), op->type, int(ModalKeyMode::Grab));
698 status.opmodal(IFACE_("Rotate"), op->type, int(ModalKeyMode::Rotate));
699 status.opmodal(IFACE_("Scale"), op->type, int(ModalKeyMode::Scale));
700}
701
703{
704 GreasePencil *grease_pencil = static_cast<GreasePencil *>(ptd.vc.obact->data);
705
706 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
707 WM_event_add_notifier(C, NC_GEOM | ND_DATA, grease_pencil);
708
710}
711
712/* Invoke handler: Initialize the operator. */
714 wmOperator *op,
715 const wmEvent *event)
716{
718 C, op, false);
719 if (retval != OPERATOR_RUNNING_MODAL) {
720 return retval;
721 }
722
723 /* If in tools region, wait till we get to the main (3D-space)
724 * region before allowing drawing to take place. */
726
727 wmWindow *win = CTX_wm_window(C);
728
729 /* Set cursor to indicate modal. */
731
733
734 /* Allocate new data. */
735 PrimitiveToolOperation *ptd_pointer = MEM_new<PrimitiveToolOperation>(__func__);
736 op->customdata = ptd_pointer;
737
738 PrimitiveToolOperation &ptd = *ptd_pointer;
739
740 ptd.vc = vc;
741 ptd.region = vc.region;
743 const float2 start_coords = float2(event->mval);
744
745 GreasePencil *grease_pencil = static_cast<GreasePencil *>(vc.obact->data);
746
747 /* Initialize helper class for projecting screen space coordinates. */
749 *vc.scene, *vc.region, *view3d, *vc.obact, grease_pencil->get_active_layer());
750 if (placement.use_project_to_surface()) {
752 }
753 else if (placement.use_project_to_stroke()) {
755 }
756
757 ptd.placement = placement;
758
759 ptd.vod = ED_view3d_navigation_init(C, nullptr);
760
761 ptd.start_position_2d = start_coords;
762 ptd.subdivision = RNA_int_get(op->ptr, "subdivision");
763 ptd.type = PrimitiveType(RNA_enum_get(op->ptr, "type"));
765 ptd.segments = 0;
767
769
771 ptd.segments++;
772 ptd.control_points.append_n_times(pos, control_points_per_segment(ptd));
775
776 Paint *paint = &vc.scene->toolsettings->gp_paint->paint;
777 ptd.brush = BKE_paint_brush(paint);
778 if (ptd.brush->gpencil_settings == nullptr) {
780 }
783
793
795 GP_Sculpt_Settings *gset = &ts->gp_sculpt;
796 /* Initialize pressure curve. */
799 }
800
802 CTX_data_main(C), vc.obact, ptd.brush);
804 ptd.use_fill = (material->gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
805
806 const bool use_vertex_color = (vc.scene->toolsettings->gp_paint->mode ==
808 if (use_vertex_color) {
809 ColorGeometry4f color_base;
810 srgb_to_linearrgb_v3_v3(color_base, ptd.brush->rgb);
811 color_base.a = ptd.settings->vertex_factor;
813 std::make_optional(color_base) :
814 std::nullopt;
816 std::make_optional(color_base) :
817 std::nullopt;
818 }
819 else {
820 ptd.vertex_color = std::nullopt;
821 ptd.fill_color = std::nullopt;
822 }
823
824 ptd.fill_opacity = ptd.brush->alpha;
825 ptd.softness = 1.0 - ptd.settings->hardness;
826
828 vc.scene, ptd.region, ptd.start_position_2d, ptd.placement);
829
830 const bool use_random = (ptd.settings->flag & GP_BRUSH_GROUP_RANDOM) != 0;
831 if (use_random) {
833 ptd.stroke_random_radius_factor = ptd.rng.get_float() * 2.0f - 1.0f;
834 ptd.stroke_random_opacity_factor = ptd.rng.get_float() * 2.0f - 1.0f;
835 ptd.stroke_random_rotation_factor = ptd.rng.get_float() * 2.0f - 1.0f;
836 ptd.stroke_random_hue_factor = ptd.rng.get_float() * 2.0f - 1.0f;
837 ptd.stroke_random_sat_factor = ptd.rng.get_float() * 2.0f - 1.0f;
838 ptd.stroke_random_val_factor = ptd.rng.get_float() * 2.0f - 1.0f;
839 }
840
841 BLI_assert(grease_pencil->has_active_layer());
842 ptd.local_transform = grease_pencil->get_active_layer()->local_transform();
843 ptd.drawing = grease_pencil->get_editable_drawing_at(*grease_pencil->get_active_layer(),
844 vc.scene->r.cfra);
845
848
851
852 /* Updates indicator in header. */
854
855 /* Add a modal handler for this operator. */
857
859}
860
861/* Exit and free memory. */
862static void grease_pencil_primitive_exit(bContext *C, wmOperator *op, const bool cancelled)
863{
865
866 const Scene &scene = *CTX_data_scene(C);
867 const bool do_automerge_endpoints = (scene.toolsettings->gpencil_flags &
869 const bool on_back = (scene.toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
870
871 if (do_automerge_endpoints && !cancelled) {
872 const Object &ob = *ptd->vc.obact;
873 const GreasePencil *grease_pencil = static_cast<GreasePencil *>(ob.data);
874 const bke::greasepencil::Layer &active_layer = *grease_pencil->get_active_layer();
875
876 constexpr float merge_distance = 30.0f;
877 const float4x4 layer_to_world = active_layer.to_world_space(ob);
878 bke::greasepencil::Drawing &drawing = *ptd->drawing;
879 const bke::CurvesGeometry &src_curves = drawing.strokes();
880 const int active_curve = on_back ? src_curves.curves_range().first() :
881 src_curves.curves_range().last();
882 const IndexMask selection = IndexRange::from_single(active_curve);
883
885 *CTX_wm_region(C), src_curves, layer_to_world, merge_distance, selection, {});
886 drawing.tag_topology_changed();
887 }
888
889 /* Clear status message area. */
890 ED_workspace_status_text(C, nullptr);
891
893
894 /* Deactivate the extra drawing stuff in 3D-View. */
896
898
900
901 MEM_delete<PrimitiveToolOperation>(ptd);
902 /* Clear pointer. */
903 op->customdata = nullptr;
904}
905
907{
908 using namespace math;
909 return sign(p) * float2(1.0f / numbers::sqrt2) * length(p);
910}
911
912/* Using Chebyshev distance instead of Euclidean. */
914{
915 using namespace math;
916 return sign(p) * float2(std::max(abs(p[0]), abs(p[1])));
917}
918
919/* Snaps to the closest diagonal, horizontal or vertical. */
921{
922 using namespace math;
923 /* sin(pi/8) or sin of 22.5 degrees. */
924 const float sin225 = 0.3826834323650897717284599840304f;
925 return sign(p) * length(p) * normalize(sign(normalize(abs(p)) - sin225) + 1.0f);
926}
927
929 const wmEvent *event)
930{
931 using namespace math;
932 const float2 start = ptd.start_position_2d;
933 const float2 end = float2(event->mval);
934
935 const float2 dif = end - start;
936 float2 offset = dif;
937
938 if (event->modifier & KM_SHIFT) {
939 if (ptd.type == PrimitiveType::Box) {
940 offset = snap_diagonals_box(dif);
941 }
942 else if (ptd.type == PrimitiveType::Circle) {
943 offset = snap_diagonals(dif);
944 }
945 else { /* Line, Polyline, Arc and Curve. */
946 offset = snap_8_angles(dif);
947 }
948 }
949 offset *= 0.5f;
950
951 float2 center = start + offset;
952
953 if (event->modifier & KM_ALT && ptd.segments == 1) {
954 center = start;
955 offset *= 2.0f;
956 }
957
958 const float3 start_pos = ptd.placement.project(center - offset);
959 const float3 end_pos = ptd.placement.project(center + offset);
960
961 const int number_control_points = control_points_per_segment(ptd);
962 for (const int i : IndexRange(number_control_points + 1)) {
963 ptd.control_points.last(i) = interpolate(
964 end_pos, start_pos, (i / float(number_control_points)));
965 }
966}
967
969 const wmEvent *event)
970{
971 const float2 start = ptd.start_position_2d;
972 const float2 end = float2(event->mval);
973 const float2 dif = end - start;
974
975 for (const int point_index : ptd.control_points.index_range()) {
976 const float2 start_pos2 = primitive_local_to_screen(ptd, ptd.temp_control_points[point_index]);
977
978 float3 pos = ptd.placement.project(start_pos2 + dif);
979 ptd.control_points[point_index] = pos;
980 }
981}
982
984{
986 const float3 pos = ptd.placement.project(float2(event->mval));
988
990 return;
991 }
992
993 /* If the center point is been grabbed, move all points. */
996 return;
997 }
998
999 const int other_point = ptd.active_control_point_index == control_point_first ?
1002
1003 /* Get the location of the other control point. */
1004 const float2 other_point_2d = primitive_local_to_screen(ptd,
1005 ptd.temp_control_points[other_point]);
1006
1007 /* Set the center point to between the first and last point. */
1009 (other_point_2d + float2(event->mval)) / 2.0f);
1010}
1011
1013{
1015 const float2 start = ptd.start_position_2d;
1016 const float2 end = float2(event->mval);
1017 const float2 dif = end - start;
1018
1019 const float2 start_pos2 = primitive_local_to_screen(
1021
1022 const float3 pos = ptd.placement.project(start_pos2 + dif);
1024}
1025
1027{
1030 }
1031 float2 center_of_mass = float2(0.0f, 0.0f);
1032
1033 for (const int point_index : ptd.control_points.index_range()) {
1034 center_of_mass += primitive_local_to_screen(ptd, ptd.temp_control_points[point_index]);
1035 }
1036 center_of_mass /= ptd.control_points.size();
1037 return center_of_mass;
1038}
1039
1041 const wmEvent *event)
1042{
1043 const float2 start = ptd.start_position_2d;
1044 const float2 end = float2(event->mval);
1045
1046 const float2 center_of_mass = primitive_center_of_mass(ptd);
1047
1048 const float2 end_ = end - center_of_mass;
1049 const float2 start_ = start - center_of_mass;
1050 const float rotation = math::atan2(start_[0], start_[1]) - math::atan2(end_[0], end_[1]);
1051
1052 for (const int point_index : ptd.control_points.index_range()) {
1053 const float2 start_pos2 = primitive_local_to_screen(ptd, ptd.temp_control_points[point_index]);
1054
1055 const float2 dif = start_pos2 - center_of_mass;
1056 const float c = math::cos(rotation);
1057 const float s = math::sin(rotation);
1058 const float2x2 rot = float2x2(float2(c, s), float2(-s, c));
1059 const float2 pos2 = rot * dif + center_of_mass;
1060 const float3 pos = ptd.placement.project(pos2);
1061 ptd.control_points[point_index] = pos;
1062 }
1063}
1064
1066 const wmEvent *event)
1067{
1068 const float2 start = ptd.start_position_2d;
1069 const float2 end = float2(event->mval);
1070
1071 const float2 center_of_mass = primitive_center_of_mass(ptd);
1072
1073 const float scale = math::length(end - center_of_mass) / math::length(start - center_of_mass);
1074
1075 for (const int point_index : ptd.control_points.index_range()) {
1076 const float2 start_pos2 = primitive_local_to_screen(ptd, ptd.temp_control_points[point_index]);
1077
1078 const float2 pos2 = (start_pos2 - center_of_mass) * scale + center_of_mass;
1079 const float3 pos = ptd.placement.project(pos2);
1080 ptd.control_points[point_index] = pos;
1081 }
1082}
1083
1085{
1086 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1087 const int value = RNA_int_get(&brush_ptr, "size");
1088
1089 ptd.initial_value.emplace<int>(value);
1090 ptd.reference_position_2d = ptd.start_position_2d - float2(value, 0.0f);
1091}
1092
1094{
1095 const float display_size = 200.0f * UI_SCALE_FAC;
1096
1097 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1098 const float value = RNA_float_get(&brush_ptr, "strength");
1099
1100 ptd.initial_value.emplace<float>(value);
1101 ptd.reference_position_2d = ptd.start_position_2d - float2(value * display_size, 0.0f);
1102}
1103
1105{
1106 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1107 RNA_int_set(&brush_ptr, "size", std::get<int>(ptd.initial_value));
1108}
1109
1111{
1112 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1113 RNA_float_set(&brush_ptr, "strength", std::get<float>(ptd.initial_value));
1114}
1115
1117 const wmEvent *event)
1118{
1119 /* Clamp reference position if mouse moves past the limits. */
1120 const float2 mouse_co = float2(event->mval);
1121 ptd.reference_position_2d.x = std::min(ptd.reference_position_2d.x, mouse_co.x);
1122 const float2 delta = mouse_co - ptd.reference_position_2d;
1123 /* Clamp to work around brush property getting "stuck" on zero. */
1124 const int new_value = std::max(int(delta.x), 1);
1125
1126 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1127 RNA_int_set(&brush_ptr, "size", new_value);
1128}
1129
1131 const wmEvent *event)
1132{
1133 const float display_size = 200.0f * UI_SCALE_FAC;
1134
1135 /* Clamp reference position if mouse moves past the limits. */
1136 const float2 mouse_co = float2(event->mval);
1137 ptd.reference_position_2d.x = std::max(std::min(ptd.reference_position_2d.x, mouse_co.x),
1138 mouse_co.x - display_size);
1139 const float2 delta = mouse_co - ptd.reference_position_2d;
1140 const float new_value = delta.x / display_size;
1141
1142 PointerRNA brush_ptr = RNA_id_pointer_create(&ptd.brush->id);
1143 RNA_float_set(&brush_ptr, "strength", new_value);
1144}
1145
1146static int primitive_check_ui_hover(const PrimitiveToolOperation &ptd, const wmEvent *event)
1147{
1148 float closest_distance_squared = std::numeric_limits<float>::max();
1149 int closest_point = -1;
1150
1151 for (const int i : ptd.control_points.index_range()) {
1152 const int point = (ptd.control_points.size() - 1) - i;
1153 const float2 pos_proj = primitive_local_to_screen(ptd, ptd.control_points[point]);
1154 const float radius_sq = ui_point_hit_size_px * ui_point_hit_size_px;
1155 const float distance_squared = math::distance_squared(pos_proj, float2(event->mval));
1156 /* If the mouse is over a control point. */
1157 if (distance_squared <= radius_sq) {
1158 return point;
1159 }
1160
1161 const ControlPointType control_point_type = get_control_point_type(ptd, point);
1162
1163 /* Save the closest handle point. */
1164 if (distance_squared < closest_distance_squared &&
1165 control_point_type == ControlPointType::HandlePoint &&
1167 {
1168 closest_point = point;
1169 closest_distance_squared = distance_squared;
1170 }
1171 }
1172
1173 if (closest_point != -1) {
1174 return closest_point;
1175 }
1176
1177 return -1;
1178}
1179
1182 const wmEvent *event)
1183{
1184 wmWindow *win = CTX_wm_window(C);
1185
1186 if (ptd.mode != OperatorMode::Idle) {
1188 return;
1189 }
1190
1191 const int ui_id = primitive_check_ui_hover(ptd, event);
1192 ptd.active_control_point_index = ui_id;
1193 if (ui_id == -1) {
1194 if (ptd.type == PrimitiveType::Polyline) {
1196 return;
1197 }
1198
1200 return;
1201 }
1202
1204}
1205
1207 wmOperator *op,
1209 const wmEvent *event)
1210{
1211 switch (event->val) {
1212 case int(ModalKeyMode::Cancel): {
1215
1216 return OPERATOR_CANCELLED;
1217 }
1218 case int(ModalKeyMode::Confirm): {
1219 grease_pencil_primitive_exit(C, op, false);
1220
1221 return OPERATOR_FINISHED;
1222 }
1223 case int(ModalKeyMode::Extrude): {
1224 if (ptd.mode == OperatorMode::Idle &&
1226 {
1229
1231 const float3 pos = ptd.placement.project(ptd.start_position_2d);
1232
1233 const int number_control_points = control_points_per_segment(ptd);
1234 ptd.control_points.append_n_times(pos, number_control_points);
1236 ptd.segments++;
1237
1239 }
1240
1241 if (ptd.type == PrimitiveType::Polyline &&
1243 {
1246
1249 const float3 pos = ptd.placement.project(float2(event->mval));
1250
1251 /* If we have only two points and they're the same then don't extrude new a point. */
1252 if (ptd.segments == 1 &&
1253 math::distance_squared(ptd.control_points.first(), ptd.control_points.last()) == 0.0f)
1254 {
1255 ptd.control_points.last() = pos;
1256 }
1257 else {
1258 ptd.control_points.append(pos);
1259 ptd.segments++;
1260 }
1261
1263 }
1264
1266 }
1267 case int(ModalKeyMode::Grab): {
1268 if (ptd.mode == OperatorMode::Idle) {
1269 ptd.start_position_2d = float2(event->mval);
1271
1273 }
1275 }
1276 case int(ModalKeyMode::Rotate): {
1277 if (ptd.mode == OperatorMode::Idle) {
1278 ptd.start_position_2d = float2(event->mval);
1280
1282 }
1284 }
1285 case int(ModalKeyMode::Scale): {
1286 if (ptd.mode == OperatorMode::Idle) {
1287 ptd.start_position_2d = float2(event->mval);
1289
1291 }
1293 }
1295 if (event->val != KM_RELEASE) {
1296 ptd.subdivision++;
1297 RNA_int_set(op->ptr, "subdivision", ptd.subdivision);
1298 }
1300 }
1302 if (event->val != KM_RELEASE) {
1303 ptd.subdivision--;
1304 ptd.subdivision = std::max(ptd.subdivision, 0);
1305 RNA_int_set(op->ptr, "subdivision", ptd.subdivision);
1306 }
1308 }
1309 case int(ModalKeyMode::ChangeRadius): {
1310 if (ptd.mode == OperatorMode::Idle) {
1311 ptd.start_position_2d = float2(event->mval);
1314
1316 }
1318 }
1319 case int(ModalKeyMode::ChangeOpacity): {
1320 if (ptd.mode == OperatorMode::Idle) {
1321 ptd.start_position_2d = float2(event->mval);
1324
1326 }
1328 }
1329 }
1330
1332}
1333
1335 const wmEvent *event)
1336{
1337 if (event->val == KM_RELEASE && ELEM(ptd.mode,
1346 {
1349 }
1350
1351 if (ptd.mode == OperatorMode::Idle && event->val == KM_PRESS) {
1352 const int ui_id = primitive_check_ui_hover(ptd, event);
1353 ptd.active_control_point_index = ui_id;
1354 if (ui_id == -1) {
1355 if (ptd.type != PrimitiveType::Polyline) {
1356 ptd.start_position_2d = float2(event->mval);
1358
1360
1362 }
1363 }
1364 else {
1365 const ControlPointType control_point_type = get_control_point_type(ptd, ui_id);
1366
1367 if (control_point_type == ControlPointType::JoinPoint) {
1371
1373 }
1374 else if (control_point_type == ControlPointType::HandlePoint) {
1375 ptd.start_position_2d = float2(event->mval);
1377
1379 }
1380
1382 }
1383 }
1384
1385 if (ptd.type == PrimitiveType::Polyline && ptd.mode == OperatorMode::Idle &&
1386 event->val == KM_PRESS)
1387 {
1390
1392 const float3 pos = ptd.placement.project(float2(event->mval));
1393
1394 /* If we have only two points and they're the same then don't extrude new a point. */
1395 if (ptd.segments == 1 &&
1396 math::distance_squared(ptd.control_points.first(), ptd.control_points.last()) == 0.0f)
1397 {
1398 ptd.control_points.last() = pos;
1399 }
1400 else {
1401 ptd.control_points.append(pos);
1402 ptd.segments++;
1403 }
1404 }
1405
1407}
1408
1410 const wmEvent *event)
1411{
1412 switch (ptd.mode) {
1415 break;
1416 }
1417 case OperatorMode::Grab: {
1419 break;
1420 }
1421 case OperatorMode::Drag: {
1423 break;
1424 }
1425 case OperatorMode::DragAll: {
1427 break;
1428 }
1431 break;
1432 }
1435 break;
1436 }
1439 break;
1440 }
1443 break;
1444 }
1445 case OperatorMode::Idle: {
1446 /* Do nothing. */
1447 break;
1448 }
1449 }
1450}
1451
1452/* Modal handler: Events handling during interactive part. */
1454 wmOperator *op,
1455 const wmEvent *event)
1456{
1457 PrimitiveToolOperation &ptd = *reinterpret_cast<PrimitiveToolOperation *>(op->customdata);
1458
1459 /* Check for confirm before navigation. */
1460 if (event->type == EVT_MODAL_MAP) {
1461 if (event->val == int(ModalKeyMode::Confirm)) {
1462 grease_pencil_primitive_exit(C, op, false);
1463
1464 return OPERATOR_FINISHED;
1465 }
1466 }
1467
1468 const float3 pos = ptd.control_points.first();
1469 if (ED_view3d_navigation_do(C, ptd.vod, event, pos)) {
1470 if (ptd.vc.rv3d->rflag & RV3D_NAVIGATING) {
1472
1475
1477 }
1478 }
1479
1482
1483 if (event->type == EVT_MODAL_MAP) {
1484 const wmOperatorStatus return_val = grease_pencil_primitive_event_modal_map(C, op, ptd, event);
1485 if (return_val != OPERATOR_RUNNING_MODAL) {
1486 return return_val;
1487 }
1488 }
1489
1490 switch (event->type) {
1491 case LEFTMOUSE: {
1492 const wmOperatorStatus return_val = grease_pencil_primitive_mouse_event(ptd, event);
1493 if (return_val != OPERATOR_RUNNING_MODAL) {
1494 return return_val;
1495 }
1496
1497 break;
1498 }
1499 case RIGHTMOUSE: {
1500 if (event->val != KM_PRESS) {
1501 break;
1502 }
1503
1504 if (ptd.mode == OperatorMode::Idle) {
1507
1508 return OPERATOR_CANCELLED;
1509 }
1510
1511 if (ptd.mode == OperatorMode::ChangeRadius) {
1513 }
1514 if (ptd.mode == OperatorMode::ChangeOpacity) {
1516 }
1517
1520 break;
1521 }
1522 default: {
1523 break;
1524 }
1525 }
1526
1527 /* Updating is done every event not just `MOUSEMOVE`. */
1530
1531 /* Updates indicator in header. */
1534
1535 /* Still running... */
1537}
1538
1539/* Cancel handler. */
1541{
1542 /* This is just a wrapper around exit() */
1544}
1545
1547 const int default_subdiv,
1548 const PrimitiveType default_type)
1549{
1550 static const EnumPropertyItem grease_pencil_primitive_type[] = {
1551 {int(PrimitiveType::Box), "BOX", 0, "Box", ""},
1552 {int(PrimitiveType::Line), "LINE", 0, "Line", ""},
1553 {int(PrimitiveType::Polyline), "POLYLINE", 0, "Polyline", ""},
1554 {int(PrimitiveType::Circle), "CIRCLE", 0, "Circle", ""},
1555 {int(PrimitiveType::Arc), "ARC", 0, "Arc", ""},
1556 {int(PrimitiveType::Curve), "CURVE", 0, "Curve", ""},
1557 {0, nullptr, 0, nullptr, nullptr},
1558 };
1559
1560 PropertyRNA *prop;
1561
1562 prop = RNA_def_int(ot->srna,
1563 "subdivision",
1564 default_subdiv,
1565 0,
1566 INT_MAX,
1567 "Subdivisions",
1568 "Number of subdivisions per segment",
1569 0,
1570 INT_MAX);
1572
1574 ot->srna, "type", grease_pencil_primitive_type, int(default_type), "Type", "Type of shape");
1575}
1576
1578{
1579 /* Identifiers. */
1580 ot->name = "Grease Pencil Line Shape";
1581 ot->idname = "GREASE_PENCIL_OT_primitive_line";
1582 ot->description = "Create predefined Grease Pencil stroke lines";
1583
1584 /* Callbacks. */
1588
1589 /* Flags. */
1591
1592 /* Properties and Flags. */
1594}
1595
1597{
1598 /* Identifiers. */
1599 ot->name = "Grease Pencil Polyline Shape";
1600 ot->idname = "GREASE_PENCIL_OT_primitive_polyline";
1601 ot->description = "Create predefined Grease Pencil stroke polylines";
1602
1603 /* Callbacks. */
1607
1608 /* Flags. */
1610
1611 /* Properties. */
1613}
1614
1616{
1617 /* Identifiers. */
1618 ot->name = "Grease Pencil Arc Shape";
1619 ot->idname = "GREASE_PENCIL_OT_primitive_arc";
1620 ot->description = "Create predefined Grease Pencil stroke arcs";
1621
1622 /* Callbacks. */
1626
1627 /* Flags. */
1629
1630 /* Properties. */
1632}
1633
1635{
1636 /* Identifiers. */
1637 ot->name = "Grease Pencil Curve Shape";
1638 ot->idname = "GREASE_PENCIL_OT_primitive_curve";
1639 ot->description = "Create predefined Grease Pencil stroke curve shapes";
1640
1641 /* Callbacks. */
1645
1646 /* Flags. */
1648
1649 /* Properties. */
1651}
1652
1654{
1655 /* Identifiers. */
1656 ot->name = "Grease Pencil Box Shape";
1657 ot->idname = "GREASE_PENCIL_OT_primitive_box";
1658 ot->description = "Create predefined Grease Pencil stroke boxes";
1659
1660 /* Callbacks. */
1664
1665 /* Flags. */
1667
1668 /* Properties. */
1670}
1671
1673{
1674 /* Identifiers. */
1675 ot->name = "Grease Pencil Circle Shape";
1676 ot->idname = "GREASE_PENCIL_OT_primitive_circle";
1677 ot->description = "Create predefined Grease Pencil stroke circles";
1678
1679 /* Callbacks. */
1683
1684 /* Flags. */
1686
1687 /* Properties. */
1689}
1690
1691} // namespace blender::ed::greasepencil
1692
1703
1705{
1706 using namespace blender::ed::greasepencil;
1707 static const EnumPropertyItem modal_items[] = {
1708 {int(ModalKeyMode::Cancel), "CANCEL", 0, "Cancel", ""},
1709 {int(ModalKeyMode::Confirm), "CONFIRM", 0, "Confirm", ""},
1710 {int(ModalKeyMode::Panning), "PANNING", 0, "Panning", ""},
1711 {int(ModalKeyMode::Extrude), "EXTRUDE", 0, "Extrude", ""},
1712 {int(ModalKeyMode::Grab), "GRAB", 0, "Grab", ""},
1713 {int(ModalKeyMode::Rotate), "ROTATE", 0, "Rotate", ""},
1714 {int(ModalKeyMode::Scale), "SCALE", 0, "Scale", ""},
1716 "INCREASE_SUBDIVISION",
1717 0,
1718 "Increase Subdivision",
1719 ""},
1721 "DECREASE_SUBDIVISION",
1722 0,
1723 "Decrease Subdivision",
1724 ""},
1725 {int(ModalKeyMode::ChangeRadius), "CHANGE_RADIUS", 0, "Change Radius", ""},
1726 {int(ModalKeyMode::ChangeOpacity), "CHANGE_OPACITY", 0, "Change Opacity", ""},
1727 {0, nullptr, 0, nullptr, nullptr},
1728 };
1729
1730 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Primitive Tool Modal Map");
1731
1732 /* This function is called for each space-type, only needs to add map once. */
1733 if (keymap && keymap->modal_items) {
1734 return;
1735 }
1736
1737 keymap = WM_modalkeymap_ensure(keyconf, "Primitive Tool Modal Map", modal_items);
1738
1739 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_line");
1740 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_polyline");
1741 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_arc");
1742 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_curve");
1743 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_box");
1744 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_circle");
1745}
const std::optional< BrushColorJitterSettings > BKE_brush_color_jitter_get_settings(const Scene *scene, const Paint *paint, const Brush *brush)
Definition brush.cc:1137
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:604
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:636
#define D
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
unsigned int uint
#define ELEM(...)
#define IFACE_(msgid)
float[3] Vector
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ GP_BRUSH_GROUP_RANDOM
@ GPPAINT_MODE_STROKE
@ GPPAINT_MODE_FILL
@ GPPAINT_MODE_BOTH
@ CURVE_TYPE_POLY
@ GP_STROKE_CAP_TYPE_ROUND
@ GP_MATERIAL_FILL_SHOW
@ GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE
@ GPPAINT_FLAG_USE_VERTEXCOLOR
@ 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:1040
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
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 immAttr1f(uint attr_id, float x)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
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
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
#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:390
@ KM_ALT
Definition WM_types.hh:277
@ KM_SHIFT
Definition WM_types.hh:275
#define ND_DATA
Definition WM_types.hh:506
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
@ KM_PRESS
Definition WM_types.hh:308
@ KM_RELEASE
Definition WM_types.hh:309
#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
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:979
void opmodal(std::string text, const wmOperatorType *ot, int propvalue, bool inverted=false)
Definition area.cc:1005
ChannelStorageType a
Definition BLI_color.hh:93
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 ForSingle(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, eCustomDataType 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
#define sinf(x)
#define cosf(x)
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)
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_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)
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
Definition BLI_color.hh:342
VecBase< float, 3 > float3
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
float rgb[3]
struct BrushGpencilSettings * gpencil_settings
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:771
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int mval[2]
Definition WM_types.hh:760
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:4225
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:929
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:956
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))