From 2ae6a93f05e9efb83c943b2782b5d36b3d57f60c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 1 Nov 2017 11:56:56 +1100 Subject: [PATCH 01/11] Cleanup: style --- source/blender/alembic/intern/abc_mball.cc | 2 +- source/blender/blenkernel/intern/fcurve.c | 6 +++--- source/blender/bmesh/intern/bmesh_edgeloop.c | 4 ++-- source/blender/editors/interface/interface_layout.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/alembic/intern/abc_mball.cc b/source/blender/alembic/intern/abc_mball.cc index 56bb609ace0..1ef7b757577 100644 --- a/source/blender/alembic/intern/abc_mball.cc +++ b/source/blender/alembic/intern/abc_mball.cc @@ -54,7 +54,7 @@ AbcMBallWriter::AbcMBallWriter( m_is_animated = isAnimated(); m_mesh_ob = BKE_object_copy(bmain, ob); - m_mesh_ob->curve_cache = (CurveCache*)MEM_callocN( + m_mesh_ob->curve_cache = (CurveCache *)MEM_callocN( sizeof(CurveCache), "CurveCache for AbcMBallWriter"); diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 1a93031034b..c9f1f288182 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -896,7 +896,7 @@ bool BKE_fcurve_is_cyclic(FCurve *fcu) if (fcm->flag & (FMODIFIER_FLAG_RANGERESTRICT | FMODIFIER_FLAG_USEINFLUENCE)) return false; - FMod_Cycles *data = (FMod_Cycles*)fcm->data; + FMod_Cycles *data = (FMod_Cycles *)fcm->data; return data && data->after_cycles == 0 && data->before_cycles == 0 && ELEM(data->before_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET) && @@ -939,14 +939,14 @@ void calchandles_fcurve(FCurve *fcu) return; /* if the first modifier is Cycles, smooth the curve through the cycle */ - BezTriple *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert-1]; + BezTriple *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert - 1]; BezTriple tmp; bool cycle = BKE_fcurve_is_cyclic(fcu) && BEZT_IS_AUTOH(first) && BEZT_IS_AUTOH(last); /* get initial pointers */ bezt = fcu->bezt; - prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert-2], last, first); + prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert - 2], last, first); next = (bezt + 1); /* loop over all beztriples, adjusting handles */ diff --git a/source/blender/bmesh/intern/bmesh_edgeloop.c b/source/blender/bmesh/intern/bmesh_edgeloop.c index 9d51b59825d..54fe2801d0c 100644 --- a/source/blender/bmesh/intern/bmesh_edgeloop.c +++ b/source/blender/bmesh/intern/bmesh_edgeloop.c @@ -168,8 +168,8 @@ int BM_mesh_edgeloops_find( /* add both directions */ if (bm_loop_build(el_store, e->v1, e->v2, 1) && - bm_loop_build(el_store, e->v2, e->v1, -1) && - el_store->len > 1) + bm_loop_build(el_store, e->v2, e->v1, -1) && + el_store->len > 1) { BLI_addtail(r_eloops, el_store); count++; diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index a0840f7fb34..616fc055c03 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -875,7 +875,7 @@ void uiItemFullO( ot, opname, { if (r_opptr) { *r_opptr = PointerRNA_NULL; - }; + } return; }); From a819ef65c07131ddb203a55bd8dc4e3207130b64 Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Wed, 1 Nov 2017 13:38:51 +1300 Subject: [PATCH 02/11] Fix for T48988 - Enabling bbone easing for posemode This fix enables the usage of bbones easing parameters for edit and pose mode seperately. This allows animators to take advantage of the functionality and may eliminate confusion as the parameters now behave similar to other bbone parameters. Note that splitting the parameters between the modes effectively creates a new parameter set. Blend files of previous versions do not contain this information and will have the values set to 0 on load. As it broke backwards compatibility for pose mode values anyway, I also took the liberty to rename the easing parameters in some places for consistency (which breaks edit mode values). Reviewers: aligorith Subscribers: aligorith Tags: #animation Differential Revision: https://developer.blender.org/D2796 --- .../startup/bl_ui/properties_data_bone.py | 9 ++----- source/blender/blenkernel/intern/action.c | 5 ++++ source/blender/blenkernel/intern/armature.c | 6 +++-- .../blender/editors/armature/armature_add.c | 10 ++++--- .../editors/armature/armature_intern.h | 1 + .../blender/editors/armature/armature_utils.c | 10 ++++--- .../editors/armature/editarmature_retarget.c | 5 ++-- .../blender/editors/armature/pose_transform.c | 6 ++++- source/blender/editors/armature/pose_utils.c | 4 +++ source/blender/editors/include/ED_armature.h | 5 +++- source/blender/makesdna/DNA_action_types.h | 1 + source/blender/makesdna/DNA_armature_types.h | 2 +- source/blender/makesrna/intern/rna_armature.c | 27 ++++++++++--------- 13 files changed, 57 insertions(+), 34 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py index 132c355ed99..5e0ef5058dd 100644 --- a/release/scripts/startup/bl_ui/properties_data_bone.py +++ b/release/scripts/startup/bl_ui/properties_data_bone.py @@ -193,13 +193,8 @@ class BONE_PT_curved(BoneButtonsPanel, Panel): sub = row.column(align=True) sub.label("Easing:") - if pchan: - # XXX: have these also be an overlay? - sub.prop(bbone.bone, "bbone_in", text="Ease In") - sub.prop(bbone.bone, "bbone_out", text="Ease Out") - else: - sub.prop(bone, "bbone_in", text="Ease In") - sub.prop(bone, "bbone_out", text="Ease Out") + sub.prop(bbone, "bbone_easein", text="Ease In") + sub.prop(bbone, "bbone_easeout", text="Ease Out") if pchan: layout.separator() diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 5bd6085c8f5..5c2fe0bb040 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -870,6 +870,8 @@ static void copy_pose_channel_data(bPoseChannel *pchan, const bPoseChannel *chan pchan->curveInY = chan->curveInY; pchan->curveOutX = chan->curveOutX; pchan->curveOutY = chan->curveOutY; + pchan->ease1 = chan->ease1; + pchan->ease2 = chan->ease2; pchan->scaleIn = chan->scaleIn; pchan->scaleOut = chan->scaleOut; @@ -1361,6 +1363,7 @@ void BKE_pose_rest(bPose *pose) pchan->roll1 = pchan->roll2 = 0.0f; pchan->curveInX = pchan->curveInY = 0.0f; pchan->curveOutX = pchan->curveOutY = 0.0f; + pchan->ease1 = pchan->ease2 = 0.0f; pchan->scaleIn = pchan->scaleOut = 1.0f; pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE | POSE_BBONE_SHAPE); @@ -1404,6 +1407,8 @@ bool BKE_pose_copy_result(bPose *to, bPose *from) pchanto->curveInY = pchanfrom->curveInY; pchanto->curveOutX = pchanfrom->curveOutX; pchanto->curveOutY = pchanfrom->curveOutY; + pchanto->ease1 = pchanfrom->ease1; + pchanto->ease2 = pchanfrom->ease2; pchanto->scaleIn = pchanfrom->scaleIn; pchanto->scaleOut = pchanfrom->scaleOut; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 669344e18d7..2edbd088334 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -614,8 +614,10 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB { const float circle_factor = length * (cubic_tangent_factor_circle_v3(h1, h2) / 0.75f); - const float hlength1 = bone->ease1 * circle_factor; - const float hlength2 = bone->ease2 * circle_factor; + const float combined_ease1 = bone->ease1 + (!rest ? pchan->ease1 : 0.0f); + const float combined_ease2 = bone->ease2 + (!rest ? pchan->ease2 : 0.0f); + const float hlength1 = combined_ease1 * circle_factor; + const float hlength2 = combined_ease2 * circle_factor; /* and only now negate h2 */ mul_v3_fl(h1, hlength1); diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index cff161d9d93..e419100bd04 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -77,19 +77,20 @@ EditBone *ED_armature_edit_bone_add(bArmature *arm, const char *name) bone->dist = 0.25f; bone->xwidth = 0.1f; bone->zwidth = 0.1f; - bone->ease1 = 1.0f; - bone->ease2 = 1.0f; bone->rad_head = 0.10f; bone->rad_tail = 0.05f; bone->segments = 1; bone->layer = arm->layer; + /* Bendy-Bone parameters */ bone->roll1 = 0.0f; bone->roll2 = 0.0f; bone->curveInX = 0.0f; bone->curveInY = 0.0f; bone->curveOutX = 0.0f; bone->curveOutY = 0.0f; + bone->ease1 = 1.0f; + bone->ease2 = 1.0f; bone->scaleIn = 1.0f; bone->scaleOut = 1.0f; @@ -899,19 +900,20 @@ static int armature_extrude_exec(bContext *C, wmOperator *op) newbone->dist = ebone->dist; newbone->xwidth = ebone->xwidth; newbone->zwidth = ebone->zwidth; - newbone->ease1 = ebone->ease1; - newbone->ease2 = ebone->ease2; newbone->rad_head = ebone->rad_tail; // don't copy entire bone... newbone->rad_tail = ebone->rad_tail; newbone->segments = 1; newbone->layer = ebone->layer; + /* Bendy-Bone parameters */ newbone->roll1 = ebone->roll1; newbone->roll2 = ebone->roll2; newbone->curveInX = ebone->curveInX; newbone->curveInY = ebone->curveInY; newbone->curveOutX = ebone->curveOutX; newbone->curveOutY = ebone->curveOutY; + newbone->ease1 = ebone->ease1; + newbone->ease2 = ebone->ease2; newbone->scaleIn = ebone->scaleIn; newbone->scaleOut = ebone->scaleOut; diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 190b0610059..192bb8eea61 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -173,6 +173,7 @@ typedef struct tPChanFCurveLink { float roll1, roll2; /* old bbone values (to be restored along with the transform properties) */ float curveInX, curveInY; /* (NOTE: we haven't renamed these this time, as their names are already long enough) */ float curveOutX, curveOutY; + float ease1, ease2; float scaleIn, scaleOut; struct IDProperty *oldprops; /* copy of custom properties at start of operator (to be restored before each modal step) */ diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index a3b439536b7..a55264bd020 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -462,19 +462,20 @@ EditBone *make_boneList(ListBase *edbo, ListBase *bones, EditBone *parent, Bone eBone->weight = curBone->weight; eBone->xwidth = curBone->xwidth; eBone->zwidth = curBone->zwidth; - eBone->ease1 = curBone->ease1; - eBone->ease2 = curBone->ease2; eBone->rad_head = curBone->rad_head; eBone->rad_tail = curBone->rad_tail; eBone->segments = curBone->segments; eBone->layer = curBone->layer; + /* Bendy-Bone parameters */ eBone->roll1 = curBone->roll1; eBone->roll2 = curBone->roll2; eBone->curveInX = curBone->curveInX; eBone->curveInY = curBone->curveInY; eBone->curveOutX = curBone->curveOutX; eBone->curveOutY = curBone->curveOutY; + eBone->ease1 = curBone->ease1; + eBone->ease2 = curBone->ease2; eBone->scaleIn = curBone->scaleIn; eBone->scaleOut = curBone->scaleOut; @@ -626,19 +627,20 @@ void ED_armature_from_edit(bArmature *arm) newBone->xwidth = eBone->xwidth; newBone->zwidth = eBone->zwidth; - newBone->ease1 = eBone->ease1; - newBone->ease2 = eBone->ease2; newBone->rad_head = eBone->rad_head; newBone->rad_tail = eBone->rad_tail; newBone->segments = eBone->segments; newBone->layer = eBone->layer; + /* Bendy-Bone parameters */ newBone->roll1 = eBone->roll1; newBone->roll2 = eBone->roll2; newBone->curveInX = eBone->curveInX; newBone->curveInY = eBone->curveInY; newBone->curveOutX = eBone->curveOutX; newBone->curveOutY = eBone->curveOutY; + newBone->ease1 = eBone->ease1; + newBone->ease2 = eBone->ease2; newBone->scaleIn = eBone->scaleIn; newBone->scaleOut = eBone->scaleOut; diff --git a/source/blender/editors/armature/editarmature_retarget.c b/source/blender/editors/armature/editarmature_retarget.c index fa7bf6e7ad4..2fb216c2ef8 100644 --- a/source/blender/editors/armature/editarmature_retarget.c +++ b/source/blender/editors/armature/editarmature_retarget.c @@ -1444,19 +1444,20 @@ static EditBone *add_editbonetolist(char *name, ListBase *list) bone->dist = 0.25F; bone->xwidth = 0.1; bone->zwidth = 0.1; - bone->ease1 = 1.0; - bone->ease2 = 1.0; bone->rad_head = 0.10; bone->rad_tail = 0.05; bone->segments = 1; bone->layer = 1; //arm->layer; + /* Bendy-Bone parameters */ bone->roll1 = 0.0f; bone->roll2 = 0.0f; bone->curveInX = 0.0f; bone->curveInY = 0.0f; bone->curveOutX = 0.0f; bone->curveOutY = 0.0f; + bone->ease1 = 1.0f; + bone->ease2 = 1.0f; bone->scaleIn = 1.0f; bone->scaleOut = 1.0f; diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 063ba37f20d..2d86610e065 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -355,6 +355,8 @@ static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, const bo pchan->roll1 = chan->roll1; pchan->roll2 = chan->roll2; + pchan->ease1 = chan->ease1; + pchan->ease2 = chan->ease2; pchan->scaleIn = chan->scaleIn; pchan->scaleOut = chan->scaleOut; @@ -577,6 +579,8 @@ static void pchan_clear_scale(bPoseChannel *pchan) if ((pchan->protectflag & OB_LOCK_SCALEZ) == 0) pchan->size[2] = 1.0f; + pchan->ease1 = 0.0f; + pchan->ease2 = 0.0f; pchan->scaleIn = 1.0f; pchan->scaleOut = 1.0f; } @@ -735,7 +739,7 @@ static int pose_clear_transform_generic_exec(bContext *C, wmOperator *op, /* clear any unkeyed tags */ if (pchan->bone) pchan->bone->flag &= ~BONE_UNKEYED; - + /* tag for autokeying later */ autokey = 1; } diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c index b960bec3603..f80afdd169e 100644 --- a/source/blender/editors/armature/pose_utils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -114,6 +114,8 @@ static void fcurves_to_pchan_links_get(ListBase *pfLinks, Object *ob, bAction *a pfl->curveInY = pchan->curveInY; pfl->curveOutX = pchan->curveOutX; pfl->curveOutY = pchan->curveOutY; + pfl->ease1 = pchan->ease1; + pfl->ease2 = pchan->ease2; pfl->scaleIn = pchan->scaleIn; pfl->scaleOut = pchan->scaleOut; @@ -219,6 +221,8 @@ void poseAnim_mapping_reset(ListBase *pfLinks) pchan->curveInY = pfl->curveInY; pchan->curveOutX = pfl->curveOutX; pchan->curveOutY = pfl->curveOutY; + pchan->ease1 = pfl->ease1; + pchan->ease2 = pfl->ease2; pchan->scaleIn = pfl->scaleIn; pchan->scaleOut = pfl->scaleOut; diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 9130336228d..00081b95c51 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -72,12 +72,15 @@ typedef struct EditBone { float dist, weight; float xwidth, length, zwidth; /* put them in order! transform uses this as scale */ - float ease1, ease2; float rad_head, rad_tail; + + /* Bendy-Bone parameters */ float roll1, roll2; float curveOutX, curveOutY; float curveInX, curveInY; + float ease1, ease2; float scaleIn, scaleOut; + float oldlength; /* for envelope scaling */ short segments; diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 9d4731f8da4..a7f3d27e9d2 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -254,6 +254,7 @@ typedef struct bPoseChannel { float roll1, roll2; float curveInX, curveInY; float curveOutX, curveOutY; + float ease1, ease2; float scaleIn, scaleOut; struct bPoseChannel *bbone_prev; /* next/prev bones to use as handle references when calculating bbones (optional) */ diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index cda6441f0ae..757c0eb8394 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -66,12 +66,12 @@ typedef struct Bone { float dist, weight; /* dist, weight: for non-deformgroup deforms */ float xwidth, length, zwidth; /* width: for block bones. keep in this order, transform! */ - float ease1, ease2; /* length of bezier handles */ float rad_head, rad_tail; /* radius for head/tail sphere, defining deform as well, parent->rad_tip overrides rad_head */ float roll1, roll2; /* curved bones settings - these define the "restpose" for a curved bone */ float curveInX, curveInY; float curveOutX, curveOutY; + float ease1, ease2; /* length of bezier handles */ float scaleIn, scaleOut; float size[3]; /* patch for upward compat, UNUSED! */ diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index cbb78f42f06..b4c5783a530 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -542,7 +542,22 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone) RNA_def_property_range(prop, -5.0f, 5.0f); RNA_def_property_ui_text(prop, "Out Y", "Y-axis handle offset for end of the B-Bone's curve, adjusts curvature"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + /* Ease In/Out */ + prop = RNA_def_property(srna, "bbone_easein", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "ease1"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "B-Bone Ease In", "Length of first Bezier Handle (for B-Bones only)"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + prop = RNA_def_property(srna, "bbone_easeout", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "ease2"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "B-Bone Ease Out", "Length of second Bezier Handle (for B-Bones only)"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + /* Scale In/Out */ prop = RNA_def_property(srna, "bbone_scalein", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "scaleIn"); @@ -680,18 +695,6 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_ui_text(prop, "B-Bone Segments", "Number of subdivisions of bone (for B-Bones only)"); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); - prop = RNA_def_property(srna, "bbone_in", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "ease1"); - RNA_def_property_range(prop, 0.0f, 2.0f); - RNA_def_property_ui_text(prop, "B-Bone Ease In", "Length of first Bezier Handle (for B-Bones only)"); - RNA_def_property_update(prop, 0, "rna_Armature_update_data"); - - prop = RNA_def_property(srna, "bbone_out", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "ease2"); - RNA_def_property_range(prop, 0.0f, 2.0f); - RNA_def_property_ui_text(prop, "B-Bone Ease Out", "Length of second Bezier Handle (for B-Bones only)"); - RNA_def_property_update(prop, 0, "rna_Armature_update_data"); - prop = RNA_def_property(srna, "bbone_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "xwidth"); RNA_def_property_range(prop, 0.0f, 1000.0f); From 7f45acee80c24735cb874acb140e8aeebb3b6c93 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 1 Nov 2017 11:06:55 +0100 Subject: [PATCH 03/11] Cycles: Cleanup, delete trailing whitespace --- intern/cycles/kernel/kernel_shader.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 695d4fc380a..64e755cacd7 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -96,7 +96,7 @@ ccl_device_noinline void shader_setup_from_ray(KernelGlobals *kg, sd->P = triangle_refine(kg, sd, isect, ray); sd->Ng = Ng; sd->N = Ng; - + /* smooth normal */ if(sd->shader & SHADER_SMOOTH_NORMAL) sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); @@ -419,7 +419,7 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *s { /* vectors */ sd->P = ray->P; - sd->N = -ray->D; + sd->N = -ray->D; sd->Ng = -ray->D; sd->I = -ray->D; sd->shader = SHADER_NONE; @@ -797,7 +797,7 @@ ccl_device float3 shader_bsdf_alpha(KernelGlobals *kg, ShaderData *sd) alpha = max(alpha, make_float3(0.0f, 0.0f, 0.0f)); alpha = min(alpha, make_float3(1.0f, 1.0f, 1.0f)); - + return alpha; } @@ -919,7 +919,7 @@ ccl_device float3 shader_bssrdf_sum(ShaderData *sd, float3 *N_, float *texture_b if(texture_blur_) *texture_blur_ = safe_divide(texture_blur, weight_sum); - + return eval; } #endif @@ -1075,7 +1075,7 @@ ccl_device int shader_volume_phase_sample(KernelGlobals *kg, const ShaderData *s for(sampled = 0; sampled < sd->num_closure; sampled++) { const ShaderClosure *sc = &sd->closure[sampled]; - + if(CLOSURE_IS_PHASE(sc->type)) sum += sc->sample_weight; } @@ -1085,7 +1085,7 @@ ccl_device int shader_volume_phase_sample(KernelGlobals *kg, const ShaderData *s for(sampled = 0; sampled < sd->num_closure; sampled++) { const ShaderClosure *sc = &sd->closure[sampled]; - + if(CLOSURE_IS_PHASE(sc->type)) { float next_sum = partial_sum + sc->sample_weight; @@ -1239,4 +1239,3 @@ ccl_device bool shader_transparent_shadow(KernelGlobals *kg, Intersection *isect #endif CCL_NAMESPACE_END - From 5d7138c08a52eddb316196109e3032d2e26259d4 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 1 Nov 2017 11:10:10 +0100 Subject: [PATCH 04/11] Cycles: Cleanup, make it more obvious what preprocessor belongs to --- intern/cycles/kernel/kernel_shader.h | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 64e755cacd7..b410785d7e0 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -428,36 +428,36 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *s sd->time = ray->time; sd->ray_length = 0.0f; /* todo: can we set this to some useful value? */ -#ifdef __INSTANCING__ +# ifdef __INSTANCING__ sd->object = PRIM_NONE; /* todo: fill this for texture coordinates */ -#endif +# endif sd->prim = PRIM_NONE; sd->type = PRIMITIVE_NONE; -#ifdef __UV__ +# ifdef __UV__ sd->u = 0.0f; sd->v = 0.0f; -#endif +# endif -#ifdef __DPDU__ +# ifdef __DPDU__ /* dPdu/dPdv */ sd->dPdu = make_float3(0.0f, 0.0f, 0.0f); sd->dPdv = make_float3(0.0f, 0.0f, 0.0f); -#endif +# endif -#ifdef __RAY_DIFFERENTIALS__ +# ifdef __RAY_DIFFERENTIALS__ /* differentials */ sd->dP = ray->dD; differential_incoming(&sd->dI, sd->dP); sd->du = differential_zero(); sd->dv = differential_zero(); -#endif +# endif /* for NDC coordinates */ sd->ray_P = ray->P; sd->ray_dP = ray->dP; } -#endif +#endif /* __VOLUME__ */ /* Merging */ @@ -492,7 +492,7 @@ ccl_device_inline void shader_merge_closures(ShaderData *sd) } } } -#endif +#endif /* __BRANCHED_PATH__ || __VOLUME__ */ /* Defensive sampling. */ @@ -571,7 +571,7 @@ ccl_device_inline void _shader_bsdf_multi_eval_branched(KernelGlobals *kg, } } } -#endif +#endif /* __BRANCHED_PATH__ */ #ifndef __KERNEL_CUDA__ @@ -922,7 +922,7 @@ ccl_device float3 shader_bssrdf_sum(ShaderData *sd, float3 *N_, float *texture_b return eval; } -#endif +#endif /* __SUBSURFACE__ */ /* Emission */ @@ -1001,12 +1001,12 @@ ccl_device float3 shader_eval_background(KernelGlobals *kg, ShaderData *sd, sd->num_closure_extra = 0; #ifdef __SVM__ -#ifdef __OSL__ +# ifdef __OSL__ if(kg->osl) { OSLShader::eval_background(kg, sd, state, path_flag); } else -#endif +# endif /* __OSL__ */ { svm_eval_nodes(kg, sd, state, SHADER_TYPE_SURFACE, path_flag); } @@ -1021,9 +1021,9 @@ ccl_device float3 shader_eval_background(KernelGlobals *kg, ShaderData *sd, } return eval; -#else +#else /* __SVM__ */ return make_float3(0.8f, 0.8f, 0.8f); -#endif +#endif /* __SVM__ */ } /* Volume */ @@ -1191,7 +1191,7 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg, } } -#endif +#endif /* __VOLUME__ */ /* Displacement Evaluation */ @@ -1236,6 +1236,6 @@ ccl_device bool shader_transparent_shadow(KernelGlobals *kg, Intersection *isect return (flag & SD_HAS_TRANSPARENT_SHADOW) != 0; } -#endif +#endif /* __TRANSPARENT_SHADOWS__ */ CCL_NAMESPACE_END From 71f46bc3679039af358f905abf16b77f0d89bc57 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 1 Nov 2017 11:10:51 +0100 Subject: [PATCH 05/11] Cycles: Add utility function to distinguish between scatter and absorption volume ID --- intern/cycles/kernel/svm/svm_types.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index d859cae1708..7fb82bc502a 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -476,6 +476,8 @@ typedef enum ClosureType { #define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_BURLEY_ID) #define CLOSURE_IS_BSSRDF(type) (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_BURLEY_ID) #define CLOSURE_IS_VOLUME(type) (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) +#define CLOSURE_IS_VOLUME_SCATTER(type) (type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) +#define CLOSURE_IS_VOLUME_ABSORPTION(type) (type == CLOSURE_VOLUME_ABSORPTION_ID) #define CLOSURE_IS_EMISSION(type) (type == CLOSURE_EMISSION_ID) #define CLOSURE_IS_HOLDOUT(type) (type == CLOSURE_HOLDOUT_ID) #define CLOSURE_IS_BACKGROUND(type) (type == CLOSURE_BACKGROUND_ID) From 07dbff7dc1d5cfca9fbfb74204a2298e16d471b4 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 1 Nov 2017 17:12:36 +0100 Subject: [PATCH 06/11] Fix T53217: GLSL principled BSDF black with zero clearcoat roughness. --- source/blender/gpu/shaders/gpu_shader_material.glsl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 3dbecc58a7e..ce696706628 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -2524,7 +2524,11 @@ float schlick_fresnel(float u) float GTR1(float NdotH, float a) { - if (a >= 1.0) return M_1_PI; + if (a >= 1.0) { + return M_1_PI; + } + + a = max(a, 0.001); float a2 = a*a; float t = 1.0 + (a2 - 1.0) * NdotH*NdotH; return (a2 - 1.0) / (M_PI * log(a2) * t); From 3ec4d0b51bf3a76725e39a57b0f30bec3edc6882 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 2 Nov 2017 04:30:07 +1100 Subject: [PATCH 07/11] UI: Add UILayout.operator_menu_hold This is an operator button that opens a menu when the button is held. --- source/blender/editors/include/UI_interface.h | 10 +++ source/blender/editors/interface/interface.c | 10 +++ .../editors/interface/interface_handlers.c | 67 +++++++++++++++---- .../editors/interface/interface_intern.h | 4 ++ .../editors/interface/interface_layout.c | 46 ++++++++++++- .../editors/interface/interface_regions.c | 21 +++++- .../editors/interface/interface_widgets.c | 54 ++++++++++++++- source/blender/makesrna/intern/rna_ui_api.c | 50 +++++++++++--- 8 files changed, 234 insertions(+), 28 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 8b4f094cb8d..a609a3b51fd 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -136,6 +136,7 @@ enum { /* block->flag bits 14-17 are identical to but->drawflag bits */ +#define UI_BLOCK_POPUP_HOLD (1 << 18) #define UI_BLOCK_LIST_ITEM (1 << 19) #define UI_BLOCK_RADIAL (1 << 20) @@ -354,6 +355,7 @@ typedef struct uiSearchItems uiSearchItems; typedef void (*uiButHandleFunc)(struct bContext *C, void *arg1, void *arg2); typedef void (*uiButHandleRenameFunc)(struct bContext *C, void *arg, char *origstr); typedef void (*uiButHandleNFunc)(struct bContext *C, void *argN, void *arg2); +typedef void (*uiButHandleHoldFunc)(struct bContext *C, struct ARegion *butregion, uiBut *but); typedef int (*uiButCompleteFunc)(struct bContext *C, char *str, void *arg); typedef struct ARegion *(*uiButSearchCreateFunc)(struct bContext *C, struct ARegion *butregion, uiBut *but); typedef void (*uiButSearchFunc)(const struct bContext *C, void *arg, const char *str, uiSearchItems *items); @@ -395,6 +397,7 @@ void UI_popup_menu_reports(struct bContext *C, struct ReportList *reports) ATTR_ int UI_popup_menu_invoke(struct bContext *C, const char *idname, struct ReportList *reports) ATTR_NONNULL(1, 2); void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool enable); +void UI_popup_menu_but_set(uiPopupMenu *pup, struct ARegion *butregion, uiBut *but); /* Pie menus */ typedef struct uiPieMenu uiPieMenu; @@ -726,6 +729,8 @@ bool UI_textbutton_activate_but(const struct bContext *C, uiBut *but); void UI_but_focus_on_enter_event(struct wmWindow *win, uiBut *but); +void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN); + /* Autocomplete * * Tab complete helper functions, for use in uiButCompleteFunc callbacks. @@ -988,6 +993,11 @@ void uiItemFullO( uiLayout *layout, const char *idname, const char *name, int icon, struct IDProperty *properties, int context, int flag, PointerRNA *r_opptr); +void uiItemFullOMenuHold_ptr( + uiLayout *layout, struct wmOperatorType *ot, const char *name, int icon, + struct IDProperty *properties, int context, int flag, + const char *menu_id, /* extra menu arg. */ + PointerRNA *r_opptr); void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon); void uiItemFullR(uiLayout *layout, struct PointerRNA *ptr, struct PropertyRNA *prop, int index, int value, int flag, const char *name, int icon); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index dc4c1b71066..bc2397d8e47 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -2672,6 +2672,10 @@ static void ui_but_free(const bContext *C, uiBut *but) MEM_freeN(but->tip_argN); } + if (but->hold_argN) { + MEM_freeN(but->hold_argN); + } + if (but->active) { /* XXX solve later, buttons should be free-able without context ideally, * however they may have open tooltips or popup windows, which need to @@ -4520,6 +4524,12 @@ void UI_but_focus_on_enter_event(wmWindow *win, uiBut *but) wm_event_add(win, &event); } +void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN) +{ + but->hold_func = func; + but->hold_argN = argN; +} + void UI_but_string_info_get(bContext *C, uiBut *but, ...) { va_list args; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 0dec751526a..57f9b2ae356 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -304,6 +304,9 @@ typedef struct uiHandleButtonData { bool used_mouse; wmTimer *autoopentimer; + /* auto open (hold) */ + wmTimer *hold_action_timer; + /* text selection/editing */ /* size of 'str' (including terminator) */ int maxlen; @@ -7808,6 +7811,15 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s data->flashtimer = NULL; } + /* add hold timer if it's used */ + if (state == BUTTON_STATE_WAIT_RELEASE && (but->hold_func != NULL)) { + data->hold_action_timer = WM_event_add_timer(data->wm, data->window, TIMER, BUTTON_AUTO_OPEN_THRESH); + } + else if (data->hold_action_timer) { + WM_event_remove_timer(data->wm, data->window, data->hold_action_timer); + data->hold_action_timer = NULL; + } + /* add a blocking ui handler at the window handler for blocking, modal states * but not for popups, because we already have a window level handler*/ if (!(but->block->handle && but->block->handle->popup)) { @@ -8422,6 +8434,25 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) button_activate_state(C, but, BUTTON_STATE_EXIT); break; + case TIMER: + { + if (event->customdata == data->hold_action_timer) { + if (true) { + data->cancel = true; + button_activate_state(C, but, BUTTON_STATE_EXIT); + } + else { + /* Do this so we can still mouse-up, closing the menu and running the button. + * This is nice to support but there are times when the button gets left pressed. + * Keep disavled for now. */ + WM_event_remove_timer(data->wm, data->window, data->hold_action_timer); + data->hold_action_timer = NULL; + } + retval = WM_UI_HANDLER_CONTINUE; + but->hold_func(C, data->region, but); + } + break; + } case MOUSEMOVE: if (ELEM(but->type, UI_BTYPE_LINK, UI_BTYPE_INLINK)) { but->flag |= UI_SELECT; @@ -9348,21 +9379,29 @@ static int ui_handle_menu_event( if (inside == 0) { uiSafetyRct *saferct = block->saferct.first; - if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) && - ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) - { - if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) { - /* for root menus, allow clicking to close */ - if (block->flag & (UI_BLOCK_OUT_1)) - menu->menuretval = UI_RETURN_OK; - else - menu->menuretval = UI_RETURN_OUT; + if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) { + if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) { + if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) { + /* for root menus, allow clicking to close */ + if (block->flag & (UI_BLOCK_OUT_1)) + menu->menuretval = UI_RETURN_OK; + else + menu->menuretval = UI_RETURN_OUT; + } + else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->x, event->y)) { + if (block->flag & (UI_BLOCK_OUT_1)) + menu->menuretval = UI_RETURN_OK; + else + menu->menuretval = UI_RETURN_OUT; + } } - else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->x, event->y)) { - if (block->flag & (UI_BLOCK_OUT_1)) - menu->menuretval = UI_RETURN_OK; - else - menu->menuretval = UI_RETURN_OUT; + else if (ELEM(event->val, KM_RELEASE, KM_CLICK)) { + /* For buttons that use a hold function, exit when mouse-up outside the menu. */ + if (block->flag & UI_BLOCK_POPUP_HOLD) { + /* Note, we could check the cursor is over the parent button. */ + menu->menuretval = UI_RETURN_CANCEL; + retval = WM_UI_HANDLER_CONTINUE; + } } } } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index ab760c40451..da11c2abab2 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -264,6 +264,10 @@ struct uiBut { void *rename_arg1; void *rename_orig; + /* Run an action when holding the button down. */ + uiButHandleHoldFunc hold_func; + void *hold_argN; + uiLink *link; short linkto[2]; /* region relative coords */ diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 616fc055c03..33dc74cbe23 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -795,7 +795,7 @@ static void ui_item_disabled(uiLayout *layout, const char *name) * \param r_opptr: Optional, initialize with operator properties when not NULL. * Will always be written to even in the case of errors. */ -void uiItemFullO_ptr( +static uiBut *uiItemFullO_ptr_ex( uiLayout *layout, wmOperatorType *ot, const char *name, int icon, IDProperty *properties, int context, int flag, PointerRNA *r_opptr) @@ -862,6 +862,50 @@ void uiItemFullO_ptr( *r_opptr = *opptr; } } + + return but; +} + +static void ui_item_hold_menu(struct bContext *C, ARegion *butregion, uiBut *but) +{ + uiPopupMenu *pup = UI_popup_menu_begin(C, "", ICON_NONE); + uiLayout *layout = UI_popup_menu_layout(pup); + uiBlock *block = layout->root->block; + UI_popup_menu_but_set(pup, butregion, but); + + block->flag |= UI_BLOCK_POPUP_HOLD; + + const char *menu_id = but->hold_argN; + MenuType *mt = WM_menutype_find(menu_id, true); + if (mt) { + Menu menu = {NULL}; + menu.layout = layout; + menu.type = mt; + mt->draw(C, &menu); + } + else { + uiItemL(layout, "Menu Missing:", ICON_NONE); + uiItemL(layout, menu_id, ICON_NONE); + } + UI_popup_menu_end(C, pup); +} + +void uiItemFullO_ptr( + uiLayout *layout, wmOperatorType *ot, + const char *name, int icon, IDProperty *properties, int context, int flag, + PointerRNA *r_opptr) +{ + uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr); +} + +void uiItemFullOMenuHold_ptr( + uiLayout *layout, wmOperatorType *ot, + const char *name, int icon, IDProperty *properties, int context, int flag, + const char *menu_id, + PointerRNA *r_opptr) +{ + uiBut *but = uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr); + UI_but_func_hold_set(but, ui_item_hold_menu, BLI_strdup(menu_id)); } void uiItemFullO( diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 71124cf8eb7..06d5e5a314e 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -2627,6 +2627,7 @@ struct uiPopupMenu { uiBlock *block; uiLayout *layout; uiBut *but; + ARegion *butregion; int mx, my; bool popup, slideout; @@ -2873,17 +2874,33 @@ uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon) return UI_popup_menu_begin_ex(C, title, __func__, icon); } +/** + * Setting the button makes the popup open from the button instead of the cursor. + */ +void UI_popup_menu_but_set(uiPopupMenu *pup, struct ARegion *butregion, uiBut *but) +{ + pup->but = but; + pup->butregion = butregion; +} + /* set the whole structure to work */ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) { wmWindow *window = CTX_wm_window(C); uiPopupBlockHandle *menu; + uiBut *but = NULL; + ARegion *butregion = NULL; pup->popup = true; pup->mx = window->eventstate->x; pup->my = window->eventstate->y; - - menu = ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup); + + if (pup->but) { + but = pup->but; + butregion = pup->butregion; + } + + menu = ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup); menu->popup = true; UI_popup_handlers_add(C, &window->modalhandlers, menu, 0); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 51bf09125ba..44a9d0d91b6 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -66,6 +66,15 @@ /* icons are 80% of height of button (16 pixels inside 20 height) */ #define ICON_SIZE_FROM_BUTRECT(rect) (0.8f * BLI_rcti_size_y(rect)) +#define UI_BUT_FLAGS_PUBLIC \ + (UI_SELECT | UI_SCROLLED | UI_ACTIVE | UI_HAS_ICON | UI_TEXTINPUT | UI_HIDDEN) + +/* Bits 0..5 are from UI_SELECT .. etc */ +enum { + /* Show that holding the button opens a menu. */ + UI_STATE_HOLD_ACTION = (1 << 6), +}; + /* ************** widget base functions ************** */ /** * - in: roundbox codes for corner types and radius @@ -184,6 +193,15 @@ static const unsigned int check_tria_face[4][3] = { {3, 2, 4}, {3, 4, 5}, {1, 0, 3}, {0, 2, 3} }; +#define OY -0.2 +#define SC 0.35 +static const unsigned int hold_action_tri_face[2][3] = {{2, 0, 1}, {3, 5, 4}}; +static const float hold_action_tri_vert[6][2] = { + {-0.5 + SC, 1.0 + OY}, {0.5, 1.0 + OY}, {0.5, 0.0 + OY + SC}, +}; +#undef OY +#undef SC + /* ************************************************* */ void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3) @@ -529,6 +547,14 @@ static void widget_num_tria(uiWidgetTrias *tria, const rcti *rect, float triasiz num_tria_face, ARRAY_SIZE(num_tria_face)); } +static void widget_hold_action_tria(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) +{ + widget_draw_tria_ex( + tria, rect, triasize, where, + hold_action_tri_vert, ARRAY_SIZE(hold_action_tri_vert), + hold_action_tri_face, ARRAY_SIZE(hold_action_tri_face)); +} + static void widget_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) { widget_draw_tria_ex( @@ -3347,6 +3373,7 @@ static void widget_but(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int widgetbase_draw(&wtb, wcol); } +#if 0 static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign) { uiWidgetBase wtb; @@ -3359,6 +3386,25 @@ static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), widgetbase_draw(&wtb, wcol); } +#endif + +static void widget_roundbut_exec(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign) +{ + uiWidgetBase wtb; + const float rad = 0.25f * U.widget_unit; + + widget_init(&wtb); + + if (state & UI_STATE_HOLD_ACTION) { + /* Show that keeping pressed performs another action (typically a menu). */ + widget_hold_action_tria(&wtb.tria1, rect, 0.75f, 'r'); + } + + /* half rounded */ + round_box_edges(&wtb, roundboxalign, rect, rad); + + widgetbase_draw(&wtb, wcol); +} static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType *wt, rcti *rect) { @@ -3439,7 +3485,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) case UI_WTYPE_EXEC: wt.wcol_theme = &btheme->tui.wcol_tool; - wt.draw = widget_roundbut; + wt.draw = widget_roundbut_exec; break; case UI_WTYPE_TOOLTIP: @@ -3849,7 +3895,7 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct roundboxalign = widget_roundbox_set(but, rect); - state = but->flag; + state = but->flag & UI_BUT_FLAGS_PUBLIC; if ((but->editstr) || (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI) && ui_but_drag_multi_edit_get(but))) @@ -3857,6 +3903,10 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct state |= UI_TEXTINPUT; } + if (but->hold_func) { + state |= UI_STATE_HOLD_ACTION; + } + if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) if (but->dt != UI_EMBOSS_PULLDOWN) disabled = true; diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index c3a16c476bc..eb95602010a 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -202,6 +202,30 @@ static PointerRNA rna_uiItemO(uiLayout *layout, const char *opname, const char * return opptr; } +static PointerRNA rna_uiItemOMenuHold( + uiLayout *layout, const char *opname, const char *name, const char *text_ctxt, + int translate, int icon, int emboss, int icon_value, + const char *menu) +{ + wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */ + if (!ot || !ot->srna) { + RNA_warning("%s '%s'", ot ? "unknown operator" : "operator missing srna", opname); + return PointerRNA_NULL; + } + + /* Get translated name (label). */ + name = rna_translate_ui_text(name, text_ctxt, ot->srna, NULL, translate); + if (icon_value && !icon) { + icon = icon_value; + } + int flag = (emboss) ? 0 : UI_ITEM_R_NO_BG; + + PointerRNA opptr; + uiItemFullOMenuHold_ptr(layout, ot, name, icon, NULL, uiLayoutGetOperatorContext(layout), flag, menu, &opptr); + return opptr; +} + + static void rna_uiItemMenuEnumO(uiLayout *layout, bContext *C, const char *opname, const char *propname, const char *name, const char *text_ctxt, int translate, int icon) { @@ -553,15 +577,23 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); api_ui_item_common(func); - func = RNA_def_function(srna, "operator", "rna_uiItemO"); - api_ui_item_op_common(func); - RNA_def_boolean(func, "emboss", true, "", "Draw the button itself, just the icon/text"); - parm = RNA_def_property(func, "icon_value", PROP_INT, PROP_UNSIGNED); - RNA_def_property_ui_text(parm, "Icon Value", "Override automatic icon of the item"); - parm = RNA_def_pointer(func, "properties", "OperatorProperties", "", "Operator properties to fill in"); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED | PARM_RNAPTR); - RNA_def_function_return(func, parm); - RNA_def_function_ui_description(func, "Item. Places a button into the layout to call an Operator"); + for (int is_menu_hold = 0; is_menu_hold < 2; is_menu_hold++) { + func = (is_menu_hold) ? + RNA_def_function(srna, "operator_menu_hold", "rna_uiItemOMenuHold") : + RNA_def_function(srna, "operator", "rna_uiItemO"); + api_ui_item_op_common(func); + RNA_def_boolean(func, "emboss", true, "", "Draw the button itself, just the icon/text"); + parm = RNA_def_property(func, "icon_value", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text(parm, "Icon Value", "Override automatic icon of the item"); + if (is_menu_hold) { + parm = RNA_def_string(func, "menu", NULL, 0, "", "Identifier of the menu"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + } + parm = RNA_def_pointer(func, "properties", "OperatorProperties", "", "Operator properties to fill in"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_function_return(func, parm); + RNA_def_function_ui_description(func, "Item. Places a button into the layout to call an Operator"); + } func = RNA_def_function(srna, "operator_enum", "uiItemsEnumO"); parm = RNA_def_string(func, "operator", NULL, 0, "", "Identifier of the operator"); From a6e1f7560fa4e6308a83d42d701a1b95d7db38a8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 2 Nov 2017 05:00:09 +1100 Subject: [PATCH 08/11] UI: Option to draw button pressed Needed to show the active tool --- source/blender/editors/include/UI_interface.h | 1 + .../blender/editors/interface/interface_layout.c | 4 ++++ source/blender/makesrna/intern/rna_ui_api.c | 15 ++++++++------- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index a609a3b51fd..a0664f020bf 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -832,6 +832,7 @@ void UI_exit(void); #define UI_ITEM_R_FULL_EVENT (1 << 6) #define UI_ITEM_R_NO_BG (1 << 7) #define UI_ITEM_R_IMMEDIATE (1 << 8) +#define UI_ITEM_O_DEPRESS (1 << 9) /* uiTemplateOperatorPropertyButs flags */ #define UI_TEMPLATE_OP_PROPS_SHOW_TITLE 1 diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 33dc74cbe23..936b4fd2bba 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -845,6 +845,10 @@ static uiBut *uiItemFullO_ptr_ex( if (flag & UI_ITEM_R_NO_BG) UI_block_emboss_set(block, UI_EMBOSS); + if (flag & UI_ITEM_O_DEPRESS) { + but->flag |= UI_SELECT; + } + if (layout->redalert) UI_but_flag_enable(but, UI_BUT_REDALERT); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index eb95602010a..69636788493 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -175,11 +175,11 @@ static void rna_uiItemPointerR(uiLayout *layout, struct PointerRNA *ptr, const c uiItemPointerR(layout, ptr, propname, searchptr, searchpropname, name, icon); } -static PointerRNA rna_uiItemO(uiLayout *layout, const char *opname, const char *name, const char *text_ctxt, - int translate, int icon, int emboss, int icon_value) +static PointerRNA rna_uiItemO( + uiLayout *layout, const char *opname, const char *name, const char *text_ctxt, + int translate, int icon, int emboss, int depress, int icon_value) { wmOperatorType *ot; - int flag; ot = WM_operatortype_find(opname, 0); /* print error next */ if (!ot || !ot->srna) { @@ -193,9 +193,8 @@ static PointerRNA rna_uiItemO(uiLayout *layout, const char *opname, const char * if (icon_value && !icon) { icon = icon_value; } - - flag = 0; - flag |= (emboss) ? 0 : UI_ITEM_R_NO_BG; + int flag = (emboss) ? 0 : UI_ITEM_R_NO_BG; + flag |= (depress) ? UI_ITEM_O_DEPRESS : 0; PointerRNA opptr; uiItemFullO_ptr(layout, ot, name, icon, NULL, uiLayoutGetOperatorContext(layout), flag, &opptr); @@ -204,7 +203,7 @@ static PointerRNA rna_uiItemO(uiLayout *layout, const char *opname, const char * static PointerRNA rna_uiItemOMenuHold( uiLayout *layout, const char *opname, const char *name, const char *text_ctxt, - int translate, int icon, int emboss, int icon_value, + int translate, int icon, int emboss, int depress, int icon_value, const char *menu) { wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */ @@ -219,6 +218,7 @@ static PointerRNA rna_uiItemOMenuHold( icon = icon_value; } int flag = (emboss) ? 0 : UI_ITEM_R_NO_BG; + flag |= (depress) ? UI_ITEM_O_DEPRESS : 0; PointerRNA opptr; uiItemFullOMenuHold_ptr(layout, ot, name, icon, NULL, uiLayoutGetOperatorContext(layout), flag, menu, &opptr); @@ -583,6 +583,7 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_function(srna, "operator", "rna_uiItemO"); api_ui_item_op_common(func); RNA_def_boolean(func, "emboss", true, "", "Draw the button itself, just the icon/text"); + RNA_def_boolean(func, "depress", false, "", "Draw pressed in"); parm = RNA_def_property(func, "icon_value", PROP_INT, PROP_UNSIGNED); RNA_def_property_ui_text(parm, "Icon Value", "Override automatic icon of the item"); if (is_menu_hold) { From 88356a24422faf45d6355b449b3855e6904edf51 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 2 Nov 2017 05:48:14 +1100 Subject: [PATCH 09/11] Cleanup: use term shape-preset in widget code --- .../editors/interface/interface_widgets.c | 116 ++++++++++-------- 1 file changed, 64 insertions(+), 52 deletions(-) diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 44a9d0d91b6..79571e24a4b 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -154,54 +154,54 @@ static const float jit[WIDGET_AA_JITTER][2] = { {-0.272855, 0.269918}, { 0.095909, 0.388710} }; -static const float num_tria_vert[3][2] = { +/* -------------------------------------------------------------------- */ +/** \name Shape Preset Data + * \{ */ + +static const float g_shape_preset_number_arrow_vert[3][2] = { {-0.352077, 0.532607}, {-0.352077, -0.549313}, {0.330000, -0.008353} }; - -static const unsigned int num_tria_face[1][3] = { +static const uint g_shape_preset_number_arrow_face[1][3] = { {0, 1, 2} }; -static const float scroll_circle_vert[16][2] = { +static const float g_shape_preset_scroll_circle_vert[16][2] = { {0.382684, 0.923879}, {0.000001, 1.000000}, {-0.382683, 0.923880}, {-0.707107, 0.707107}, {-0.923879, 0.382684}, {-1.000000, 0.000000}, {-0.923880, -0.382684}, {-0.707107, -0.707107}, {-0.382683, -0.923880}, {0.000000, -1.000000}, {0.382684, -0.923880}, {0.707107, -0.707107}, {0.923880, -0.382684}, {1.000000, -0.000000}, {0.923880, 0.382683}, {0.707107, 0.707107} }; - -static const unsigned int scroll_circle_face[14][3] = { +static const uint g_shape_preset_scroll_circle_face[14][3] = { {0, 1, 2}, {2, 0, 3}, {3, 0, 15}, {3, 15, 4}, {4, 15, 14}, {4, 14, 5}, {5, 14, 13}, {5, 13, 6}, {6, 13, 12}, {6, 12, 7}, {7, 12, 11}, {7, 11, 8}, {8, 11, 10}, {8, 10, 9} }; - -static const float menu_tria_vert[6][2] = { +static const float g_shape_preset_menu_arrow_vert[6][2] = { {-0.33, 0.16}, {0.33, 0.16}, {0, 0.82}, {0, -0.82}, {-0.33, -0.16}, {0.33, -0.16} }; +static const uint g_shape_preset_menu_arrow_face[2][3] = {{2, 0, 1}, {3, 5, 4}}; - - -static const unsigned int menu_tria_face[2][3] = {{2, 0, 1}, {3, 5, 4}}; - -static const float check_tria_vert[6][2] = { +static const float g_shape_preset_checkmark_vert[6][2] = { {-0.578579, 0.253369}, {-0.392773, 0.412794}, {-0.004241, -0.328551}, {-0.003001, 0.034320}, {1.055313, 0.864744}, {0.866408, 1.026895} }; -static const unsigned int check_tria_face[4][3] = { +static const uint g_shape_preset_checkmark_face[4][3] = { {3, 2, 4}, {3, 4, 5}, {1, 0, 3}, {0, 2, 3} }; #define OY -0.2 #define SC 0.35 -static const unsigned int hold_action_tri_face[2][3] = {{2, 0, 1}, {3, 5, 4}}; -static const float hold_action_tri_vert[6][2] = { +static const float g_shape_preset_hold_action_vert[6][2] = { {-0.5 + SC, 1.0 + OY}, {0.5, 1.0 + OY}, {0.5, 0.0 + OY + SC}, }; +static const uint g_shape_preset_hold_action_face[2][3] = {{2, 0, 1}, {3, 5, 4}}; #undef OY #undef SC +/** \} */ + /* ************************************************* */ void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3) @@ -498,9 +498,12 @@ static void round_box_edges(uiWidgetBase *wt, int roundboxalign, const rcti *rec round_box__edges(wt, roundboxalign, rect, rad, rad - U.pixelsize); } +/* -------------------------------------------------------------------- */ +/** \name Shape Preset Mini API + * \{ */ /* based on button rect, return scaled array of triangles */ -static void widget_draw_tria_ex( +static void shape_preset_init_trias_ex( uiWidgetTrias *tria, const rcti *rect, float triasize, char where, /* input data */ const float verts[][2], const int verts_tot, @@ -539,31 +542,31 @@ static void widget_draw_tria_ex( tria->index = tris; } -static void widget_num_tria(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) +static void shape_preset_init_number_arrows(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) { - widget_draw_tria_ex( + shape_preset_init_trias_ex( tria, rect, triasize, where, - num_tria_vert, ARRAY_SIZE(num_tria_vert), - num_tria_face, ARRAY_SIZE(num_tria_face)); + g_shape_preset_number_arrow_vert, ARRAY_SIZE(g_shape_preset_number_arrow_vert), + g_shape_preset_number_arrow_face, ARRAY_SIZE(g_shape_preset_number_arrow_face)); } -static void widget_hold_action_tria(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) +static void shape_preset_init_hold_action(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) { - widget_draw_tria_ex( + shape_preset_init_trias_ex( tria, rect, triasize, where, - hold_action_tri_vert, ARRAY_SIZE(hold_action_tri_vert), - hold_action_tri_face, ARRAY_SIZE(hold_action_tri_face)); + g_shape_preset_hold_action_vert, ARRAY_SIZE(g_shape_preset_hold_action_vert), + g_shape_preset_hold_action_face, ARRAY_SIZE(g_shape_preset_hold_action_face)); } -static void widget_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) +static void shape_preset_init_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) { - widget_draw_tria_ex( + shape_preset_init_trias_ex( tria, rect, triasize, where, - scroll_circle_vert, ARRAY_SIZE(scroll_circle_vert), - scroll_circle_face, ARRAY_SIZE(scroll_circle_face)); + g_shape_preset_scroll_circle_vert, ARRAY_SIZE(g_shape_preset_scroll_circle_vert), + g_shape_preset_scroll_circle_face, ARRAY_SIZE(g_shape_preset_scroll_circle_face)); } -static void widget_trias_draw(uiWidgetTrias *tria) +static void shape_preset_draw_trias(uiWidgetTrias *tria) { glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, tria->vec); @@ -571,7 +574,7 @@ static void widget_trias_draw(uiWidgetTrias *tria) glDisableClientState(GL_VERTEX_ARRAY); } -static void widget_menu_trias(uiWidgetTrias *tria, const rcti *rect) +static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *rect) { float centx, centy, size; int a; @@ -582,15 +585,15 @@ static void widget_menu_trias(uiWidgetTrias *tria, const rcti *rect) size = 0.4f * BLI_rcti_size_y(rect); for (a = 0; a < 6; a++) { - tria->vec[a][0] = size * menu_tria_vert[a][0] + centx; - tria->vec[a][1] = size * menu_tria_vert[a][1] + centy; + tria->vec[a][0] = size * g_shape_preset_menu_arrow_vert[a][0] + centx; + tria->vec[a][1] = size * g_shape_preset_menu_arrow_vert[a][1] + centy; } tria->tot = 2; - tria->index = menu_tria_face; + tria->index = g_shape_preset_menu_arrow_face; } -static void widget_check_trias(uiWidgetTrias *tria, const rcti *rect) +static void shape_preset_trias_from_rect_checkmark(uiWidgetTrias *tria, const rcti *rect) { float centx, centy, size; int a; @@ -601,14 +604,16 @@ static void widget_check_trias(uiWidgetTrias *tria, const rcti *rect) size = 0.5f * BLI_rcti_size_y(rect); for (a = 0; a < 6; a++) { - tria->vec[a][0] = size * check_tria_vert[a][0] + centx; - tria->vec[a][1] = size * check_tria_vert[a][1] + centy; + tria->vec[a][0] = size * g_shape_preset_checkmark_vert[a][0] + centx; + tria->vec[a][1] = size * g_shape_preset_checkmark_vert[a][1] + centy; } tria->tot = 4; - tria->index = check_tria_face; + tria->index = g_shape_preset_checkmark_face; } +/** \} */ + /* prepares shade colors */ static void shadecolors4(char coltop[4], char coldown[4], const char *color, short shadetop, short shadedown) @@ -809,12 +814,13 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) for (j = 0; j < WIDGET_AA_JITTER; j++) { glTranslate2fv(jit[j]); - if (wtb->tria1.tot) - widget_trias_draw(&wtb->tria1); + if (wtb->tria1.tot) { + shape_preset_draw_trias(&wtb->tria1); + } + if (wtb->tria2.tot) { + shape_preset_draw_trias(&wtb->tria2); + } - if (wtb->tria2.tot) - widget_trias_draw(&wtb->tria2); - glTranslatef(-jit[j][0], -jit[j][1], 0.0f); } } @@ -2673,8 +2679,8 @@ static void widget_numbut_draw(uiWidgetColors *wcol, rcti *rect, int state, int /* decoration */ if (!(state & UI_TEXTINPUT)) { - widget_num_tria(&wtb.tria1, rect, 0.6f, 'l'); - widget_num_tria(&wtb.tria2, rect, 0.6f, 'r'); + shape_preset_init_number_arrows(&wtb.tria1, rect, 0.6f, 'l'); + shape_preset_init_number_arrows(&wtb.tria2, rect, 0.6f, 'r'); } widgetbase_draw(&wtb, wcol); @@ -2809,12 +2815,12 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s wcol->item[3] = 255; if (horizontal) { - widget_scroll_circle(&wtb.tria1, slider, 0.6f, 'l'); - widget_scroll_circle(&wtb.tria2, slider, 0.6f, 'r'); + shape_preset_init_scroll_circle(&wtb.tria1, slider, 0.6f, 'l'); + shape_preset_init_scroll_circle(&wtb.tria2, slider, 0.6f, 'r'); } else { - widget_scroll_circle(&wtb.tria1, slider, 0.6f, 'b'); - widget_scroll_circle(&wtb.tria2, slider, 0.6f, 't'); + shape_preset_init_scroll_circle(&wtb.tria1, slider, 0.6f, 'b'); + shape_preset_init_scroll_circle(&wtb.tria2, slider, 0.6f, 't'); } } widgetbase_draw(&wtb, wcol); @@ -3151,7 +3157,7 @@ static void widget_menubut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), round_box_edges(&wtb, roundboxalign, rect, rad); /* decoration */ - widget_menu_trias(&wtb.tria1, rect); + shape_preset_trias_from_rect_menu(&wtb.tria1, rect); widgetbase_draw(&wtb, wcol); @@ -3290,7 +3296,7 @@ static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, int state, int UN /* decoration */ if (state & UI_SELECT) { - widget_check_trias(&wtb.tria1, &recttemp); + shape_preset_trias_from_rect_checkmark(&wtb.tria1, &recttemp); } widgetbase_draw(&wtb, wcol); @@ -3397,7 +3403,7 @@ static void widget_roundbut_exec(uiWidgetColors *wcol, rcti *rect, int state, in if (state & UI_STATE_HOLD_ACTION) { /* Show that keeping pressed performs another action (typically a menu). */ - widget_hold_action_tria(&wtb.tria1, rect, 0.75f, 'r'); + shape_preset_init_hold_action(&wtb.tria1, rect, 0.75f, 'r'); } /* half rounded */ @@ -3660,6 +3666,10 @@ static int widget_roundbox_set(uiBut *but, rcti *rect) return roundbox; } +/* -------------------------------------------------------------------- */ +/** \name Public API + * \{ */ + /* conversion from old to new buttons, so still messy */ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rcti *rect) { @@ -4233,3 +4243,5 @@ void ui_draw_preview_item(uiFontStyle *fstyle, rcti *rect, const char *name, int UI_fontstyle_draw(fstyle, &trect, drawstr); } } + +/** \} */ From 8bdc391c5488228dfe9c9e995277d67558293f08 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 1 Nov 2017 21:34:30 +0300 Subject: [PATCH 10/11] Implement a new automatic handle algorithm to produce smooth F-Curves. The legacy algorithm only considers two adjacent points when computing the bezier handles, which cannot produce satisfactory results. Animators are often forced to manually adjust all curves. The new approach instead solves a system of equations to trace a cubic spline with continuous second derivative through the whole segment of auto points, delimited at ends by keyframes with handles set by other requirements. This algorithm also adjusts Vector handles that face ordinary bezier keyframes to achieve zero acceleration at the Vector keyframe, instead of simply pointing it at the adjacent point. Original idea and implementation by Benoit Bolsee ; code mostly rewritten to improve code clarity and extensibility. Reviewers: aligorith Differential Revision: https://developer.blender.org/D2884 --- source/blender/blenkernel/BKE_curve.h | 4 +- source/blender/blenkernel/intern/curve.c | 569 +++++++++++++++++- source/blender/blenkernel/intern/fcurve.c | 21 +- source/blender/blenkernel/intern/mask.c | 6 +- source/blender/blenkernel/intern/nla.c | 2 + source/blender/blenlib/BLI_math_solvers.h | 5 + source/blender/blenlib/intern/math_solvers.c | 108 ++++ source/blender/editors/animation/drivers.c | 1 + source/blender/editors/animation/keyframing.c | 1 + source/blender/editors/curve/editcurve.c | 2 +- .../editors/space_graph/graph_buttons.c | 7 +- source/blender/makesdna/DNA_anim_types.h | 9 + source/blender/makesdna/DNA_curve_types.h | 9 +- source/blender/makesrna/intern/rna_fcurve.c | 10 + 14 files changed, 736 insertions(+), 18 deletions(-) diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index 30db8f45059..175fa1dad79 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -202,10 +202,12 @@ void BKE_nurb_bpoint_calc_normal(struct Nurb *nu, struct BPoint *bp, float r_nor void BKE_nurb_bpoint_calc_plane(struct Nurb *nu, struct BPoint *bp, float r_plane[3]); void BKE_nurb_handle_calc(struct BezTriple *bezt, struct BezTriple *prev, struct BezTriple *next, - const bool is_fcurve); + const bool is_fcurve, const char smoothing); void BKE_nurb_handle_calc_simple(struct Nurb *nu, struct BezTriple *bezt); void BKE_nurb_handle_calc_simple_auto(struct Nurb *nu, struct BezTriple *bezt); +void BKE_nurb_handle_smooth_fcurve(struct BezTriple *bezt, int total, bool cyclic); + void BKE_nurb_handles_calc(struct Nurb *nu); void BKE_nurb_handles_autocalc(struct Nurb *nu, int flag); void BKE_nurb_bezt_handle_test(struct BezTriple *bezt, const bool use_handle); diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 6c6019748d6..98e4cb85981 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -41,6 +41,7 @@ #include "BLI_utildefines.h" #include "BLI_ghash.h" +#include "DNA_anim_types.h" #include "DNA_curve_types.h" #include "DNA_material_types.h" @@ -3134,7 +3135,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render) static void calchandleNurb_intern( BezTriple *bezt, const BezTriple *prev, const BezTriple *next, - bool is_fcurve, bool skip_align) + bool is_fcurve, bool skip_align, char fcurve_smoothing) { /* defines to avoid confusion */ #define p2_h1 ((p2) - 3) @@ -3148,6 +3149,9 @@ static void calchandleNurb_intern( float len_ratio; const float eps = 1e-5; + /* assume normal handle until we check */ + bezt->f5 = HD_AUTOTYPE_NORMAL; + if (bezt->h1 == 0 && bezt->h2 == 0) { return; } @@ -3199,7 +3203,14 @@ static void calchandleNurb_intern( tvec[2] = dvec_b[2] / len_b + dvec_a[2] / len_a; if (is_fcurve) { - len = tvec[0]; + if (fcurve_smoothing != FCURVE_SMOOTH_NONE) { + /* force the horizontal handle size to be 1/3 of the key interval so that + * the X component of the parametric bezier curve is a linear spline */ + len = 6.0f/2.5614f; + } + else { + len = tvec[0]; + } } else { len = len_v3(tvec); @@ -3210,10 +3221,12 @@ static void calchandleNurb_intern( /* only for fcurves */ bool leftviolate = false, rightviolate = false; - if (len_a > 5.0f * len_b) - len_a = 5.0f * len_b; - if (len_b > 5.0f * len_a) - len_b = 5.0f * len_a; + if (!is_fcurve || fcurve_smoothing == FCURVE_SMOOTH_NONE) { + if (len_a > 5.0f * len_b) + len_a = 5.0f * len_b; + if (len_b > 5.0f * len_a) + len_b = 5.0f * len_a; + } if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM)) { len_a /= len; @@ -3224,6 +3237,7 @@ static void calchandleNurb_intern( float ydiff2 = next->vec[1][1] - bezt->vec[1][1]; if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) { bezt->vec[0][1] = bezt->vec[1][1]; + bezt->f5 = HD_AUTOTYPE_SPECIAL; } else { /* handles should not be beyond y coord of two others */ if (ydiff1 <= 0.0f) { @@ -3250,6 +3264,7 @@ static void calchandleNurb_intern( float ydiff2 = next->vec[1][1] - bezt->vec[1][1]; if ( (ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f) ) { bezt->vec[2][1] = bezt->vec[1][1]; + bezt->f5 = HD_AUTOTYPE_SPECIAL; } else { /* handles should not be beyond y coord of two others */ if (ydiff1 <= 0.0f) { @@ -3397,7 +3412,7 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align) next = bezt + 1; while (a--) { - calchandleNurb_intern(bezt, prev, next, 0, skip_align); + calchandleNurb_intern(bezt, prev, next, 0, skip_align, 0); prev = bezt; if (a == 1) { if (nu->flagu & CU_NURB_CYCLIC) @@ -3412,9 +3427,543 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align) } } -void BKE_nurb_handle_calc(BezTriple *bezt, BezTriple *prev, BezTriple *next, const bool is_fcurve) +/* A utility function for allocating a number of arrays of the same length + * with easy error checking and deallocation, and an easy way to add or remove + * arrays that are processed in this way when changing code. + * + * floats, chars: NULL-terminated arrays of pointers to array pointers that need to be allocated. + * + * Returns: pointer to the buffer that contains all of the arrays. + */ +static void *allocate_arrays(int count, float ***floats, char ***chars, const char *name) { - calchandleNurb_intern(bezt, prev, next, is_fcurve, false); + int num_floats = 0, num_chars = 0; + + while (floats && floats[num_floats]) { + num_floats++; + } + + while (chars && chars[num_chars]) { + num_chars++; + } + + void *buffer = (float*)MEM_mallocN(count * (sizeof(float)*num_floats + num_chars), name); + + if (!buffer) + return NULL; + + float *fptr = buffer; + + for (int i = 0; i < num_floats; i++, fptr += count) { + *floats[i] = fptr; + } + + char *cptr = (char*)fptr; + + for (int i = 0; i < num_chars; i++, cptr += count) { + *chars[i] = cptr; + } + + return buffer; +} + +static void free_arrays(void *buffer) +{ + MEM_freeN(buffer); +} + +/* computes in which direction to change h[i] to satisfy conditions better */ +static float bezier_relax_direction(float *a, float *b, float *c, float *d, float *h, int i, int count) +{ + /* current deviation between sides of the equation */ + float state = a[i] * h[(i+count-1) % count] + + b[i] * h[i] + + c[i] * h[(i+1) % count] + - d[i]; + + /* only the sign is meaningful */ + return -state * b[i]; +} + +static void bezier_lock_unknown(float *a, float *b, float *c, float *d, int i, float value) +{ + a[i] = c[i] = 0.0f; + b[i] = 1.0f; + d[i] = value; +} + +static void bezier_restore_equation(float *a, float *b, float *c, float *d, float *a0, float *b0, float *c0, float *d0, int i) +{ + a[i] = a0[i]; + b[i] = b0[i]; + c[i] = c0[i]; + d[i] = d0[i]; +} + +static bool tridiagonal_solve_with_limits(float *a, float *b, float *c, float *d, float *h, float *hmin, float *hmax, int solve_count) +{ + float *a0, *b0, *c0, *d0; + float **arrays[] = { &a0, &b0, &c0, &d0, NULL }; + char *is_locked, *num_unlocks; + char **flagarrays[] = { &is_locked, &num_unlocks, NULL }; + + void *tmps = allocate_arrays(solve_count, arrays, flagarrays, "tridiagonal_solve_with_limits"); + if (!tmps) + return false; + + memcpy(a0, a, sizeof(float)*solve_count); + memcpy(b0, b, sizeof(float)*solve_count); + memcpy(c0, c, sizeof(float)*solve_count); + memcpy(d0, d, sizeof(float)*solve_count); + + memset(is_locked, 0, solve_count); + memset(num_unlocks, 0, solve_count); + + bool overshoot, unlocked; + + do + { + if (!BLI_tridiagonal_solve_cyclic(a, b, c, d, h, solve_count)) { + free_arrays(tmps); + return false; + } + + /* first check if any handles overshoot the limits, and lock them */ + bool all = false, locked = false; + + overshoot = unlocked = false; + + do + { + for (int i = 0; i < solve_count; i++) { + if (h[i] >= hmin[i] && h[i] <= hmax[i]) + continue; + + overshoot = true; + + float target = h[i] > hmax[i] ? hmax[i] : hmin[i]; + + /* heuristically only lock handles that go in the right direction if there are such ones */ + if (target != 0.0f || all) { + /* mark item locked */ + is_locked[i] = 1; + + bezier_lock_unknown(a, b, c, d, i, target); + locked = true; + } + } + + all = true; + } + while (overshoot && !locked); + + /* if no handles overshot and were locked, see if it may be a good idea to unlock some handles */ + if (!locked) { + for (int i = 0; i < solve_count; i++) { + // to definitely avoid infinite loops limit this to 2 times + if (!is_locked[i] || num_unlocks[i] >= 2) + continue; + + /* if the handle wants to move in allowable direction, release it */ + float relax = bezier_relax_direction(a0, b0, c0, d0, h, i, solve_count); + + if ((relax > 0 && h[i] < hmax[i]) || (relax < 0 && h[i] > hmin[i])) { + bezier_restore_equation(a, b, c, d, a0, b0, c0, d0, i); + + is_locked[i] = 0; + num_unlocks[i]++; + unlocked = true; + } + } + } + } + while (overshoot || unlocked); + + free_arrays(tmps); + return true; +} + +/* + * This function computes the handles of a series of auto bezier points + * on the basis of 'no acceleration discontinuities' at the points. + * The first and last bezier points are considered 'fixed' (their handles are not touched) + * The result is the smoothest possible trajectory going through intemediate points. + * The difficulty is that the handles depends on their neighbours. + * + * The exact solution is found by solving a tridiagonal matrix equation formed + * by the continuity and boundary conditions. Although theoretically handle position + * is affected by all other points of the curve segment, in practice the influence + * decreases exponentially with distance. + * + * Note: this algorithm assumes that the handle horizontal size if always 1/3 of the + * of the interval to the next point. This rule ensures linear interpolation of time. + * + * ^ height (co 1) + * | yN + * | yN-1 | + * | y2 | | + * | y1 | | | + * | y0 | | | | + * | | | | | | + * | | | | | | + * | | | | | | + * |-------t1---------t2--------- ~ --------tN-------------------> time (co 0) + * + * + * Mathematical basis: + * + * 1. Handle lengths on either side of each point are connected by a factor + * ensuring continuity of the first derivative: + * + * l[i] = t[i+1]/t[i] + * + * 2. The tridiagonal system is formed by the following equation, which is derived + * by differentiating the bezier curve and specifies second derivative continuity + * at every point: + * + * l[i]^2 * h[i-1] + (2*l[i]+2) * h[i] + 1/l[i+1] * h[i+1] = (y[i]-y[i-1])*l[i]^2 + y[i+1]-y[i] + * + * 3. If this point is adjacent to a manually set handle with X size not equal to 1/3 + * of the horizontal interval, this equation becomes slightly more complex: + * + * l[i]^2 * h[i-1] + (3*(1-R[i-1])*l[i] + 3*(1-L[i+1])) * h[i] + 1/l[i+1] * h[i+1] = (y[i]-y[i-1])*l[i]^2 + y[i+1]-y[i] + * + * The difference between equations amounts to this, and it's obvious that when R[i-1] + * and L[i+1] are both 1/3, it becomes zero: + * + * ( (1-3*R[i-1])*l[i] + (1-3*L[i+1]) ) * h[i] + * + * 4. The equations for zero acceleration border conditions are basically the above + * equation with parts omitted, so the handle size correction also applies. + */ + + +static void bezier_eq_continuous(float *a, float *b, float *c, float *d, float *dy, float *l, int i) +{ + a[i] = l[i] * l[i]; + b[i] = 2.0f * (l[i] + 1); + c[i] = 1.0f / l[i+1]; + d[i] = dy[i]*l[i]*l[i] + dy[i+1]; +} + +static void bezier_eq_noaccel_right(float *a, float *b, float *c, float *d, float *dy, float *l, int i) +{ + a[i] = 0.0f; + b[i] = 2.0f; + c[i] = 1.0f / l[i+1]; + d[i] = dy[i+1]; +} + +static void bezier_eq_noaccel_left(float *a, float *b, float *c, float *d, float *dy, float *l, int i) +{ + a[i] = l[i] * l[i]; + b[i] = 2.0f * l[i]; + c[i] = 0.0f; + d[i] = dy[i] * l[i] * l[i]; +} + +/* auto clamp prevents its own point going the wrong way, and adjacent handles overshooting */ +static void bezier_clamp(float *hmax, float *hmin, int i, float dy, bool no_reverse, bool no_overshoot) +{ + if (dy > 0) { + if (no_overshoot) + hmax[i] = min_ff(hmax[i], dy); + if (no_reverse) + hmin[i] = 0.0f; + } + else if (dy < 0) { + if (no_reverse) + hmax[i] = 0.0f; + if (no_overshoot) + hmin[i] = max_ff(hmin[i], dy); + } + else if (no_reverse || no_overshoot) { + hmax[i] = hmin[i] = 0.0f; + } +} + +/* write changes to a bezier handle */ +static void bezier_output_handle_inner(BezTriple *bezt, bool right, float newval[3], bool endpoint) +{ + float tmp[3]; + + int idx = right ? 2 : 0; + char hr = right ? bezt->h2 : bezt->h1; + char hm = right ? bezt->h1 : bezt->h2; + + /* only assign Auto/Vector handles */ + if (!ELEM(hr, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) + return; + + copy_v3_v3(bezt->vec[idx], newval); + + /* fix up the Align handle if any */ + if (ELEM(hm, HD_ALIGN, HD_ALIGN_DOUBLESIDE)) { + float hlen = len_v3v3(bezt->vec[1], bezt->vec[2-idx]); + float h2len = len_v3v3(bezt->vec[1], bezt->vec[idx]); + + sub_v3_v3v3(tmp, bezt->vec[1], bezt->vec[idx]); + madd_v3_v3v3fl(bezt->vec[2-idx], bezt->vec[1], tmp, hlen / h2len); + } + /* at end points of the curve, mirror handle to the other side */ + else if (endpoint && ELEM(hm, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) { + sub_v3_v3v3(tmp, bezt->vec[1], bezt->vec[idx]); + add_v3_v3v3(bezt->vec[2-idx], bezt->vec[1], tmp); + } +} + +static void bezier_output_handle(BezTriple *bezt, bool right, float dy, bool endpoint) +{ + float tmp[3]; + + copy_v3_v3(tmp, bezt->vec[right ? 2 : 0]); + + tmp[1] = bezt->vec[1][1] + dy; + + bezier_output_handle_inner(bezt, right, tmp, endpoint); +} + +static bool bezier_check_solve_end_handle(BezTriple *bezt, char htype, bool end) +{ + return (htype == HD_VECT) || (end && ELEM(htype, HD_AUTO, HD_AUTO_ANIM) && bezt->f5 == HD_AUTOTYPE_NORMAL); +} + +static float bezier_calc_handle_adj(float hsize[2], float dx) +{ + /* if handles intersect in x direction, they are scaled to fit */ + float fac = dx/(hsize[0] + dx/3.0f); + if (fac < 1.0f) + mul_v2_fl(hsize, fac); + + return 1.0f - 3.0f*hsize[0]/dx; +} + +static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int start, int count, bool cycle) +{ + float *dx, *dy, *l, *a, *b, *c, *d, *h, *hmax, *hmin; + float **arrays[] = { &dx, &dy, &l, &a, &b, &c, &d, &h, &hmax, &hmin, NULL }; + + int solve_count = count; + + /* verify index ranges */ + + if (count < 2) + return; + + BLI_assert(start < total-1 && count <= total); + BLI_assert(start + count <= total || cycle); + + bool full_cycle = (start == 0 && count == total && cycle); + + BezTriple *bezt_first = &bezt[start]; + BezTriple *bezt_last = &bezt[(start+count > total) ? start+count-total : start+count-1]; + + bool solve_first = bezier_check_solve_end_handle(bezt_first, bezt_first->h2, start==0); + bool solve_last = bezier_check_solve_end_handle(bezt_last, bezt_last->h1, start+count==total); + + if (count == 2 && !full_cycle && solve_first == solve_last) + return; + + /* allocate all */ + + void *tmp_buffer = allocate_arrays(count, arrays, NULL, "bezier_calc_smooth_tmp"); + if (!tmp_buffer) + return; + + /* point locations */ + + dx[0] = dy[0] = NAN_FLT; + + for (int i = 1, j = start+1; i < count; i++, j++) { + dx[i] = bezt[j].vec[1][0] - bezt[j-1].vec[1][0]; + dy[i] = bezt[j].vec[1][1] - bezt[j-1].vec[1][1]; + + /* when cyclic, jump from last point to first */ + if (cycle && j == total-1) + j = 0; + } + + /* ratio of x intervals */ + + l[0] = l[count-1] = 1.0f; + + for (int i = 1; i < count-1; i++) { + l[i] = dx[i+1] / dx[i]; + } + + /* compute handle clamp ranges */ + + bool clamped_prev = false, clamped_cur = ELEM(HD_AUTO_ANIM, bezt_first->h1, bezt_first->h2); + + for (int i = 0; i < count; i++) { + hmax[i] = FLT_MAX; + hmin[i] = -FLT_MAX; + } + + for (int i = 1, j = start+1; i < count; i++, j++) { + clamped_prev = clamped_cur; + clamped_cur = ELEM(HD_AUTO_ANIM, bezt[j].h1, bezt[j].h2); + + if (cycle && j == total-1) { + j = 0; + clamped_cur = clamped_cur || ELEM(HD_AUTO_ANIM, bezt[j].h1, bezt[j].h2); + } + + bezier_clamp(hmax, hmin, i-1, dy[i], clamped_prev, clamped_prev); + bezier_clamp(hmax, hmin, i, dy[i] * l[i], clamped_cur, clamped_cur); + } + + /* full cycle merges first and last points into continuous loop */ + + float first_handle_adj = 0.0f, last_handle_adj = 0.0f; + + if (full_cycle) { + /* reduce the number of uknowns by one */ + int i = solve_count = count-1; + + dx[0] = dx[i]; + dy[0] = dy[i]; + + l[0] = l[i] = dx[1] / dx[0]; + + hmin[0] = max_ff(hmin[0], hmin[i]); + hmax[0] = min_ff(hmax[0], hmax[i]); + + solve_first = solve_last = true; + + bezier_eq_continuous(a, b, c, d, dy, l, 0); + } + else { + float tmp[2]; + + /* boundary condition: fixed handles or zero curvature */ + if (!solve_first) { + sub_v2_v2v2(tmp, bezt_first->vec[2], bezt_first->vec[1]); + first_handle_adj = bezier_calc_handle_adj(tmp, dx[1]); + + bezier_lock_unknown(a, b, c, d, 0, tmp[1]); + } + else { + bezier_eq_noaccel_right(a, b, c, d, dy, l, 0); + } + + if (!solve_last) { + sub_v2_v2v2(tmp, bezt_last->vec[1], bezt_last->vec[0]); + last_handle_adj = bezier_calc_handle_adj(tmp, dx[count-1]); + + bezier_lock_unknown(a, b, c, d, count-1, tmp[1]); + } + else { + bezier_eq_noaccel_left(a, b, c, d, dy, l, count-1); + } + } + + /* main tridiagonal system of equations */ + + for (int i = 1; i < count-1; i++) { + bezier_eq_continuous(a, b, c, d, dy, l, i); + } + + /* apply correction for user-defined handles with nonstandard x positions */ + + if (!full_cycle) { + if (count > 2 || solve_last) { + b[1] += l[1]*first_handle_adj; + } + + if (count > 2 || solve_first) { + b[count-2] += last_handle_adj; + } + } + + /* solve and output results */ + + if (tridiagonal_solve_with_limits(a, b, c, d, h, hmin, hmax, solve_count)) { + if (full_cycle) { + h[count-1] = h[0]; + } + + for (int i = 1, j = start+1; i < count-1; i++, j++) { + bool end = (j == total-1); + + bezier_output_handle(&bezt[j], false, - h[i] / l[i], end); + + if (end) + j = 0; + + bezier_output_handle(&bezt[j], true, h[i], end); + } + + if (solve_first) { + bezier_output_handle(bezt_first, true, h[0], start == 0); + } + + if (solve_last) { + bezier_output_handle(bezt_last, false, - h[count-1] / l[count-1], start+count == total); + } + } + + /* free all */ + + free_arrays(tmp_buffer); +} + +static bool is_free_auto_point(BezTriple *bezt) +{ + return BEZT_IS_AUTOH(bezt) && bezt->f5 == HD_AUTOTYPE_NORMAL; +} + +void BKE_nurb_handle_smooth_fcurve(BezTriple *bezt, int total, bool cycle) +{ + /* ignore cyclic extrapolation if end points are locked */ + cycle = cycle && is_free_auto_point(&bezt[0]) && is_free_auto_point(&bezt[total-1]); + + /* if cyclic, try to find a sequence break point */ + int search_base = 0; + + if (cycle) { + for (int i = 1; i < total-1; i++) { + if (!is_free_auto_point(&bezt[i])) { + search_base = i; + break; + } + } + + /* all points of the curve are freely changeable auto handles - solve as full cycle */ + if (search_base == 0) { + bezier_handle_calc_smooth_fcurve(bezt, total, 0, total, cycle); + return; + } + } + + /* Find continuous subsequences of free auto handles and smooth them, starting at + * search_base. In cyclic mode these subsequences can span the cycle boundary. */ + int start = search_base, count = 1; + + for (int i = 1, j = start+1; i < total; i++, j++) { + /* in cyclic mode: jump from last to first point when necessary */ + if (j == total-1 && cycle) + j = 0; + + /* non auto handle closes the list (we come here at least for the last handle, see above) */ + if (!is_free_auto_point(&bezt[j])) { + bezier_handle_calc_smooth_fcurve(bezt, total, start, count+1, cycle); + start = j; + count = 1; + } + else { + count++; + } + } + + if (count > 1) { + bezier_handle_calc_smooth_fcurve(bezt, total, start, count, cycle); + } +} + +void BKE_nurb_handle_calc(BezTriple *bezt, BezTriple *prev, BezTriple *next, const bool is_fcurve, const char smoothing) +{ + calchandleNurb_intern(bezt, prev, next, is_fcurve, false, smoothing); } void BKE_nurb_handles_calc(Nurb *nu) /* first, if needed, set handle flags */ @@ -3454,7 +4003,7 @@ void BKE_nurb_handle_calc_simple(Nurb *nu, BezTriple *bezt) if (nu->pntsu > 1) { BezTriple *prev = BKE_nurb_bezt_get_prev(nu, bezt); BezTriple *next = BKE_nurb_bezt_get_next(nu, bezt); - BKE_nurb_handle_calc(bezt, prev, next, 0); + BKE_nurb_handle_calc(bezt, prev, next, 0, 0); } } diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index c9f1f288182..382b26abbc6 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -956,7 +956,7 @@ void calchandles_fcurve(FCurve *fcu) if (bezt->vec[2][0] < bezt->vec[1][0]) bezt->vec[2][0] = bezt->vec[1][0]; /* calculate auto-handles */ - BKE_nurb_handle_calc(bezt, prev, next, true); + BKE_nurb_handle_calc(bezt, prev, next, true, fcu->auto_smoothing); /* for automatic ease in and out */ if (BEZT_IS_AUTOH(bezt) && !cycle) { @@ -965,9 +965,16 @@ void calchandles_fcurve(FCurve *fcu) /* set both handles to have same horizontal value as keyframe */ if (fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) { bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1]; + /* remember that these keyframes are special, they don't need to be adjusted */ + bezt->f5 = HD_AUTOTYPE_SPECIAL; } } } + + /* avoid total smoothing failure on duplicate keyframes (can happen during grab) */ + if (prev && prev->vec[1][0] >= bezt->vec[1][0]) { + prev->f5 = bezt->f5 = HD_AUTOTYPE_SPECIAL; + } /* advance pointers for next iteration */ prev = bezt; @@ -981,6 +988,18 @@ void calchandles_fcurve(FCurve *fcu) bezt++; } + + /* if cyclic extrapolation and Auto Clamp has triggered, ensure it is symmetric */ + if (cycle && (first->f5 != HD_AUTOTYPE_NORMAL || last->f5 != HD_AUTOTYPE_NORMAL)) { + first->vec[0][1] = first->vec[2][1] = first->vec[1][1]; + last->vec[0][1] = last->vec[2][1] = last->vec[1][1]; + first->f5 = last->f5 = HD_AUTOTYPE_SPECIAL; + } + + /* do a second pass for auto handle: compute the handle to have 0 accelaration step */ + if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) { + BKE_nurb_handle_smooth_fcurve(fcu->bezt, fcu->totvert, cycle); + } } void testhandles_fcurve(FCurve *fcu, const bool use_handle) diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 8b8b48db279..b8a241ed956 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -1196,14 +1196,14 @@ static void mask_calc_point_handle(MaskSplinePoint *point, MaskSplinePoint *poin #if 1 if (bezt_prev || bezt_next) { - BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0); + BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0, 0); } #else if (handle_type == HD_VECT) { - BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0); + BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0, 0); } else if (handle_type == HD_AUTO) { - BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0); + BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0, 0); } else if (handle_type == HD_ALIGN || handle_type == HD_ALIGN_DOUBLESIDE) { float v1[3], v2[3]; diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 9a923b643ac..2363eb525d5 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -1387,6 +1387,7 @@ void BKE_nlastrip_validate_fcurves(NlaStrip *strip) /* set default flags */ fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); + fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL; /* store path - make copy, and store that */ fcu->rna_path = BLI_strdupn("influence", 9); @@ -1408,6 +1409,7 @@ void BKE_nlastrip_validate_fcurves(NlaStrip *strip) /* set default flags */ fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); + fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL; /* store path - make copy, and store that */ fcu->rna_path = BLI_strdupn("strip_time", 10); diff --git a/source/blender/blenlib/BLI_math_solvers.h b/source/blender/blenlib/BLI_math_solvers.h index 810c84cc830..b0193022837 100644 --- a/source/blender/blenlib/BLI_math_solvers.h +++ b/source/blender/blenlib/BLI_math_solvers.h @@ -48,6 +48,11 @@ bool BLI_eigen_solve_selfadjoint_m3(const float m3[3][3], float r_eigen_values[3 void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[], float r_V[3][3]); +/***************************** Simple Solvers ************************************/ + +bool BLI_tridiagonal_solve(const float *a, const float *b, const float *c, const float *d, float *r_x, const int count); +bool BLI_tridiagonal_solve_cyclic(const float *a, const float *b, const float *c, const float *d, float *r_x, const int count); + /**************************** Inline Definitions ******************************/ #if 0 /* None so far. */ # if BLI_MATH_DO_INLINE diff --git a/source/blender/blenlib/intern/math_solvers.c b/source/blender/blenlib/intern/math_solvers.c index 641e50c9bde..5ee2f10928f 100644 --- a/source/blender/blenlib/intern/math_solvers.c +++ b/source/blender/blenlib/intern/math_solvers.c @@ -72,3 +72,111 @@ void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[3], float r_V[3 { EIG_svd_square_matrix(3, (const float *)m3, (float *)r_U, (float *)r_S, (float *)r_V); } + +/***************************** Simple Solvers ************************************/ + +/** + * \brief Solve a tridiagonal system of equations: + * + * a[i] * r_x[i-1] + b[i] * r_x[i] + c[i] * r_x[i+1] = d[i] + * + * Ignores a[0] and c[count-1]. Uses the Thomas algorithm, e.g. see wiki. + * + * \param r_x output vector, may be shared with any of the input ones + * \return true if success + */ +bool BLI_tridiagonal_solve(const float *a, const float *b, const float *c, const float *d, float *r_x, const int count) +{ + if (count < 1) + return false; + + size_t bytes = sizeof(double)*(unsigned)count; + double *c1 = (double *)MEM_mallocN(bytes*2, "tridiagonal_c1d1"); + double *d1 = c1 + count; + + if (!c1) + return false; + + int i; + double c_prev, d_prev, x_prev; + + /* forward pass */ + + c1[0] = c_prev = ((double)c[0]) / b[0]; + d1[0] = d_prev = ((double)d[0]) / b[0]; + + for (i = 1; i < count; i++) { + double denum = b[i] - a[i]*c_prev; + + c1[i] = c_prev = c[i] / denum; + d1[i] = d_prev = (d[i] - a[i]*d_prev) / denum; + } + + /* back pass */ + + x_prev = d_prev; + r_x[--i] = ((float)x_prev); + + while (--i >= 0) { + x_prev = d1[i] - c1[i] * x_prev; + r_x[i] = ((float)x_prev); + } + + MEM_freeN(c1); + + return isfinite(x_prev); +} + +/** + * \brief Solve a possibly cyclic tridiagonal system using the Sherman-Morrison formula. + * + * \param r_x output vector, may be shared with any of the input ones + * \return true if success + */ +bool BLI_tridiagonal_solve_cyclic(const float *a, const float *b, const float *c, const float *d, float *r_x, const int count) +{ + if (count < 1) + return false; + + float a0 = a[0], cN = c[count-1]; + + /* if not really cyclic, fall back to the simple solver */ + if (a0 == 0.0f && cN == 0.0f) { + return BLI_tridiagonal_solve(a, b, c, d, r_x, count); + } + + size_t bytes = sizeof(float)*(unsigned)count; + float *tmp = (float*)MEM_mallocN(bytes*2, "tridiagonal_ex"); + float *b2 = tmp + count; + + if (!tmp) + return false; + + /* prepare the noncyclic system; relies on tridiagonal_solve ignoring values */ + memcpy(b2, b, bytes); + b2[0] -= a0; + b2[count-1] -= cN; + + memset(tmp, 0, bytes); + tmp[0] = a0; + tmp[count-1] = cN; + + /* solve for partial solution and adjustment vector */ + bool success = + BLI_tridiagonal_solve(a, b2, c, tmp, tmp, count) && + BLI_tridiagonal_solve(a, b2, c, d, r_x, count); + + /* apply adjustment */ + if (success) { + float coeff = (r_x[0] + r_x[count-1]) / (1.0f + tmp[0] + tmp[count-1]); + + for (int i = 0; i < count; i++) { + r_x[i] -= coeff * tmp[i]; + } + } + + MEM_freeN(tmp); + + return success; +} + diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 958c2f2c8d1..24eb6451c5d 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -102,6 +102,7 @@ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_inde fcu = MEM_callocN(sizeof(FCurve), "FCurve"); fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); + fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL; /* store path - make copy, and store that */ fcu->rna_path = BLI_strdup(rna_path); diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 344c6cc1621..de0d19f7b05 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -186,6 +186,7 @@ FCurve *verify_fcurve(bAction *act, const char group[], PointerRNA *ptr, fcu = MEM_callocN(sizeof(FCurve), "FCurve"); fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); + fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL; if (BLI_listbase_is_empty(&act->curves)) fcu->flag |= FCURVE_ACTIVE; /* first one added active */ diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index ee83f212463..4916c206a85 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -592,7 +592,7 @@ static void calc_keyHandles(ListBase *nurb, float *key) if (nextp) key_to_bezt(nextfp, nextp, &next); if (prevp) key_to_bezt(prevfp, prevp, &prev); - BKE_nurb_handle_calc(&cur, prevp ? &prev : NULL, nextp ? &next : NULL, 0); + BKE_nurb_handle_calc(&cur, prevp ? &prev : NULL, nextp ? &next : NULL, 0, 0); bezt_to_key(&cur, fp); prevp = bezt; diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index cbb8e98e7e0..985cca7c4af 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -208,7 +208,12 @@ static void graph_panel_properties(const bContext *C, Panel *pa) sub = uiLayoutRow(row, true); uiLayoutSetEnabled(sub, (fcu->color_mode == FCURVE_COLOR_CUSTOM)); uiItemR(sub, &fcu_ptr, "color", 0, "", ICON_NONE); - + + /* smoothing setting */ + col = uiLayoutColumn(layout, true); + uiItemL(col, IFACE_("Auto Handle Smoothing:"), ICON_NONE); + uiItemR(col, &fcu_ptr, "auto_smoothing", 0, "", ICON_NONE); + MEM_freeN(ale); } diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 977cd2347ad..099d5da7c49 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -490,7 +490,10 @@ typedef struct FCurve { float curval; /* value stored from last time curve was evaluated (not threadsafe, debug display only!) */ short flag; /* user-editable settings for this curve */ short extend; /* value-extending mode for this curve (does not cover */ + char auto_smoothing; /* auto-handle smoothing mode */ + char pad[7]; + /* RNA - data link */ int array_index; /* if applicable, the index of the RNA-array item to get */ char *rna_path; /* RNA-path to resolve data-access */ @@ -545,6 +548,12 @@ typedef enum eFCurve_Coloring { FCURVE_COLOR_CUSTOM = 2, /* custom color */ } eFCurve_Coloring; +/* curve smoothing modes */ +typedef enum eFCurve_Smoothing { + FCURVE_SMOOTH_NONE = 0, /* legacy mode: auto handles only consider adjacent points */ + FCURVE_SMOOTH_CONT_ACCEL = 1, /* maintain continuity of the acceleration */ +} eFCurve_Smoothing; + /* ************************************************ */ /* 'Action' Datatypes */ diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index a25c00915d2..2207b0ec0b1 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -124,7 +124,8 @@ typedef struct BezTriple { float back; /* BEZT_IPO_BACK */ float amplitude, period; /* BEZT_IPO_ELASTIC */ - char pad[4]; + char f5; /* f5: used for auto handle to distinguish between normal handle and exception (extrema) */ + char pad[3]; } BezTriple; /* note; alfa location in struct is abused by Key system */ @@ -395,6 +396,12 @@ typedef enum eBezTriple_Handle { HD_ALIGN_DOUBLESIDE = 5, /* align handles, displayed both of them. used for masks */ } eBezTriple_Handle; +/* f5 (beztriple) */ +typedef enum eBezTriple_Auto_Type { + HD_AUTOTYPE_NORMAL = 0, + HD_AUTOTYPE_SPECIAL = 1 +} eBezTriple_Auto_Type; + /* interpolation modes (used only for BezTriple->ipo) */ typedef enum eBezTriple_Interpolation { /* traditional interpolation */ diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index b23b229a27d..c8ee6e15689 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -1895,6 +1895,11 @@ static void rna_def_fcurve(BlenderRNA *brna) "Use custom hand-picked color for F-Curve"}, {0, NULL, 0, NULL, NULL} }; + static EnumPropertyItem prop_mode_smoothing_items[] = { + {FCURVE_SMOOTH_NONE, "NONE", 0, "None", "Auto handles only take adjacent keys into account (legacy mode)"}, + {FCURVE_SMOOTH_CONT_ACCEL, "CONT_ACCEL", 0, "Continuous Acceleration", "Auto handles are placed to avoid jumps in acceleration"}, + {0, NULL, 0, NULL, NULL} + }; srna = RNA_def_struct(brna, "FCurve", NULL); RNA_def_struct_ui_text(srna, "F-Curve", "F-Curve defining values of a period of time"); @@ -1968,6 +1973,11 @@ static void rna_def_fcurve(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Hide", "F-Curve and its keyframes are hidden in the Graph Editor graphs"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL); + prop = RNA_def_property(srna, "auto_smoothing", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_mode_smoothing_items); + RNA_def_property_ui_text(prop, "Auto Handle Smoothing", "Algorithm used to compute automatic handles"); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FCurve_update_data"); + /* State Info (for Debugging) */ prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FCURVE_DISABLED); From 765e28948e706d8fce97c23d4d050a607971488b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 2 Nov 2017 15:09:11 +1100 Subject: [PATCH 11/11] Cleanup: code style --- source/blender/blenkernel/intern/curve.c | 164 +++++++++---------- source/blender/blenlib/intern/math_solvers.c | 20 +-- 2 files changed, 92 insertions(+), 92 deletions(-) diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 98e4cb85981..d0382b941ed 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -1238,8 +1238,9 @@ void BKE_nurb_makeFaces(Nurb *nu, float *coord_array, int rowstride, int resolu, basis += KNOTSV(nu); } u += ustep; - if (rowstride != 0) - in = (float *) (((unsigned char *) in) + (rowstride - 3 * totv * sizeof(*in))); + if (rowstride != 0) { + in = (float *)(((unsigned char *)in) + (rowstride - 3 * totv * sizeof(*in))); + } } /* free */ @@ -1682,7 +1683,7 @@ float *BKE_curve_make_orco(Scene *scene, Object *ob, int *r_numVerts) if (dl->flag & DL_CYCL_V) sizev++; } - else if (dl->flag & DL_CYCL_V) { + else if (dl->flag & DL_CYCL_V) { sizev++; } @@ -1798,7 +1799,7 @@ void BKE_curve_bevel_make(Scene *scene, Object *ob, ListBase *disp, fp[3] = fp[4] = 0.0; fp[5] = cu->ext1; } - else if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0 && cu->ext1 == 0.0f) { // we make a full round bevel in that case + else if ((cu->flag & (CU_FRONT | CU_BACK)) == 0 && cu->ext1 == 0.0f) { /* we make a full round bevel in that case */ nr = 4 + 2 * cu->bevresol; dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1"); @@ -1830,9 +1831,9 @@ void BKE_curve_bevel_make(Scene *scene, Object *ob, ListBase *disp, if ((cu->flag & CU_BACK) || !(cu->flag & CU_FRONT)) { dnr = nr = 2 + cu->bevresol; - if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0) + if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { nr = 3 + 2 * cu->bevresol; - + } dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1"); dl->verts = MEM_mallocN(nr * sizeof(float[3]), "makebevelcurve p1"); BLI_addtail(disp, dl); @@ -1872,7 +1873,7 @@ void BKE_curve_bevel_make(Scene *scene, Object *ob, ListBase *disp, fp[4] = cu->ext2; fp[5] = cu->ext1; - if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0) { + if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { dl = MEM_dupallocN(dl); dl->verts = MEM_dupallocN(dl->verts); BLI_addtail(disp, dl); @@ -1888,9 +1889,9 @@ void BKE_curve_bevel_make(Scene *scene, Object *ob, ListBase *disp, /* part 3, front */ if ((cu->flag & CU_FRONT) || !(cu->flag & CU_BACK)) { dnr = nr = 2 + cu->bevresol; - if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0) + if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { nr = 3 + 2 * cu->bevresol; - + } dl = MEM_callocN(sizeof(DispList), "makebevelcurve p3"); dl->verts = MEM_mallocN(nr * sizeof(float[3]), "makebevelcurve p3"); BLI_addtail(disp, dl); @@ -2108,7 +2109,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * if (tilt_array) { if (nu->tilt_interp == KEY_CU_EASE) { /* May as well support for tilt also 2.47 ease interp */ *tilt_array = prevbezt->alfa + - (bezt->alfa - prevbezt->alfa) * (3.0f * fac * fac - 2.0f * fac * fac * fac); + (bezt->alfa - prevbezt->alfa) * (3.0f * fac * fac - 2.0f * fac * fac * fac); } else { key_curve_position_weights(fac, t, nu->tilt_interp); @@ -2124,7 +2125,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * * Note! - this only takes the 2 points into account, * giving much more localized results to changes in radius, sometimes you want that */ *radius_array = prevbezt->radius + - (bezt->radius - prevbezt->radius) * (3.0f * fac * fac - 2.0f * fac * fac * fac); + (bezt->radius - prevbezt->radius) * (3.0f * fac * fac - 2.0f * fac * fac * fac); } else { @@ -2132,8 +2133,9 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * if (tilt_array == NULL || nu->tilt_interp != nu->radius_interp) { key_curve_position_weights(fac, t, nu->radius_interp); } - *radius_array = t[0] * pprev->radius + t[1] * prevbezt->radius + - t[2] * bezt->radius + t[3] * next->radius; + *radius_array = + t[0] * pprev->radius + t[1] * prevbezt->radius + + t[2] * bezt->radius + t[3] * next->radius; } radius_array = POINTER_OFFSET(radius_array, stride); @@ -2141,8 +2143,9 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * if (weight_array) { /* basic interpolation for now, could copy tilt interp too */ - *weight_array = prevbezt->weight + - (bezt->weight - prevbezt->weight) * (3.0f * fac * fac - 2.0f * fac * fac * fac); + *weight_array = + prevbezt->weight + + (bezt->weight - prevbezt->weight) * (3.0f * fac * fac - 2.0f * fac * fac * fac); weight_array = POINTER_OFFSET(weight_array, stride); } @@ -3206,7 +3209,7 @@ static void calchandleNurb_intern( if (fcurve_smoothing != FCURVE_SMOOTH_NONE) { /* force the horizontal handle size to be 1/3 of the key interval so that * the X component of the parametric bezier curve is a linear spline */ - len = 6.0f/2.5614f; + len = 6.0f / 2.5614f; } else { len = tvec[0]; @@ -3262,7 +3265,7 @@ static void calchandleNurb_intern( if ((bezt->h2 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */ float ydiff1 = prev->vec[1][1] - bezt->vec[1][1]; float ydiff2 = next->vec[1][1] - bezt->vec[1][1]; - if ( (ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f) ) { + if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) { bezt->vec[2][1] = bezt->vec[1][1]; bezt->f5 = HD_AUTOTYPE_SPECIAL; } @@ -3447,7 +3450,7 @@ static void *allocate_arrays(int count, float ***floats, char ***chars, const ch num_chars++; } - void *buffer = (float*)MEM_mallocN(count * (sizeof(float)*num_floats + num_chars), name); + void *buffer = (float *)MEM_mallocN(count * (sizeof(float) * num_floats + num_chars), name); if (!buffer) return NULL; @@ -3458,7 +3461,7 @@ static void *allocate_arrays(int count, float ***floats, char ***chars, const ch *floats[i] = fptr; } - char *cptr = (char*)fptr; + char *cptr = (char *)fptr; for (int i = 0; i < num_chars; i++, cptr += count) { *chars[i] = cptr; @@ -3476,10 +3479,10 @@ static void free_arrays(void *buffer) static float bezier_relax_direction(float *a, float *b, float *c, float *d, float *h, int i, int count) { /* current deviation between sides of the equation */ - float state = a[i] * h[(i+count-1) % count] - + b[i] * h[i] - + c[i] * h[(i+1) % count] - - d[i]; + float state = a[i] * h[(i + count - 1) % count] + + b[i] * h[i] + + c[i] * h[(i + 1) % count] - + d[i]; /* only the sign is meaningful */ return -state * b[i]; @@ -3511,18 +3514,17 @@ static bool tridiagonal_solve_with_limits(float *a, float *b, float *c, float *d if (!tmps) return false; - memcpy(a0, a, sizeof(float)*solve_count); - memcpy(b0, b, sizeof(float)*solve_count); - memcpy(c0, c, sizeof(float)*solve_count); - memcpy(d0, d, sizeof(float)*solve_count); + memcpy(a0, a, sizeof(float) * solve_count); + memcpy(b0, b, sizeof(float) * solve_count); + memcpy(c0, c, sizeof(float) * solve_count); + memcpy(d0, d, sizeof(float) * solve_count); memset(is_locked, 0, solve_count); memset(num_unlocks, 0, solve_count); bool overshoot, unlocked; - do - { + do { if (!BLI_tridiagonal_solve_cyclic(a, b, c, d, h, solve_count)) { free_arrays(tmps); return false; @@ -3533,8 +3535,7 @@ static bool tridiagonal_solve_with_limits(float *a, float *b, float *c, float *d overshoot = unlocked = false; - do - { + do { for (int i = 0; i < solve_count; i++) { if (h[i] >= hmin[i] && h[i] <= hmax[i]) continue; @@ -3554,8 +3555,7 @@ static bool tridiagonal_solve_with_limits(float *a, float *b, float *c, float *d } all = true; - } - while (overshoot && !locked); + } while (overshoot && !locked); /* if no handles overshot and were locked, see if it may be a good idea to unlock some handles */ if (!locked) { @@ -3576,8 +3576,7 @@ static bool tridiagonal_solve_with_limits(float *a, float *b, float *c, float *d } } } - } - while (overshoot || unlocked); + } while (overshoot || unlocked); free_arrays(tmps); return true; @@ -3642,16 +3641,16 @@ static void bezier_eq_continuous(float *a, float *b, float *c, float *d, float * { a[i] = l[i] * l[i]; b[i] = 2.0f * (l[i] + 1); - c[i] = 1.0f / l[i+1]; - d[i] = dy[i]*l[i]*l[i] + dy[i+1]; + c[i] = 1.0f / l[i + 1]; + d[i] = dy[i] * l[i] * l[i] + dy[i + 1]; } static void bezier_eq_noaccel_right(float *a, float *b, float *c, float *d, float *dy, float *l, int i) { a[i] = 0.0f; b[i] = 2.0f; - c[i] = 1.0f / l[i+1]; - d[i] = dy[i+1]; + c[i] = 1.0f / l[i + 1]; + d[i] = dy[i + 1]; } static void bezier_eq_noaccel_left(float *a, float *b, float *c, float *d, float *dy, float *l, int i) @@ -3699,16 +3698,16 @@ static void bezier_output_handle_inner(BezTriple *bezt, bool right, float newval /* fix up the Align handle if any */ if (ELEM(hm, HD_ALIGN, HD_ALIGN_DOUBLESIDE)) { - float hlen = len_v3v3(bezt->vec[1], bezt->vec[2-idx]); + float hlen = len_v3v3(bezt->vec[1], bezt->vec[2 - idx]); float h2len = len_v3v3(bezt->vec[1], bezt->vec[idx]); sub_v3_v3v3(tmp, bezt->vec[1], bezt->vec[idx]); - madd_v3_v3v3fl(bezt->vec[2-idx], bezt->vec[1], tmp, hlen / h2len); + madd_v3_v3v3fl(bezt->vec[2 - idx], bezt->vec[1], tmp, hlen / h2len); } /* at end points of the curve, mirror handle to the other side */ else if (endpoint && ELEM(hm, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) { sub_v3_v3v3(tmp, bezt->vec[1], bezt->vec[idx]); - add_v3_v3v3(bezt->vec[2-idx], bezt->vec[1], tmp); + add_v3_v3v3(bezt->vec[2 - idx], bezt->vec[1], tmp); } } @@ -3731,11 +3730,11 @@ static bool bezier_check_solve_end_handle(BezTriple *bezt, char htype, bool end) static float bezier_calc_handle_adj(float hsize[2], float dx) { /* if handles intersect in x direction, they are scaled to fit */ - float fac = dx/(hsize[0] + dx/3.0f); - if (fac < 1.0f) + float fac = dx / (hsize[0] + dx / 3.0f); + if (fac < 1.0f) { mul_v2_fl(hsize, fac); - - return 1.0f - 3.0f*hsize[0]/dx; + } + return 1.0f - 3.0f * hsize[0] / dx; } static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int start, int count, bool cycle) @@ -3750,19 +3749,20 @@ static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int sta if (count < 2) return; - BLI_assert(start < total-1 && count <= total); + BLI_assert(start < total - 1 && count <= total); BLI_assert(start + count <= total || cycle); bool full_cycle = (start == 0 && count == total && cycle); BezTriple *bezt_first = &bezt[start]; - BezTriple *bezt_last = &bezt[(start+count > total) ? start+count-total : start+count-1]; + BezTriple *bezt_last = &bezt[(start + count > total) ? start + count - total : start + count - 1]; - bool solve_first = bezier_check_solve_end_handle(bezt_first, bezt_first->h2, start==0); - bool solve_last = bezier_check_solve_end_handle(bezt_last, bezt_last->h1, start+count==total); + bool solve_first = bezier_check_solve_end_handle(bezt_first, bezt_first->h2, start == 0); + bool solve_last = bezier_check_solve_end_handle(bezt_last, bezt_last->h1, start + count == total); - if (count == 2 && !full_cycle && solve_first == solve_last) - return; + if (count == 2 && !full_cycle && solve_first == solve_last) { + return; + } /* allocate all */ @@ -3774,21 +3774,21 @@ static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int sta dx[0] = dy[0] = NAN_FLT; - for (int i = 1, j = start+1; i < count; i++, j++) { - dx[i] = bezt[j].vec[1][0] - bezt[j-1].vec[1][0]; - dy[i] = bezt[j].vec[1][1] - bezt[j-1].vec[1][1]; + for (int i = 1, j = start + 1; i < count; i++, j++) { + dx[i] = bezt[j].vec[1][0] - bezt[j - 1].vec[1][0]; + dy[i] = bezt[j].vec[1][1] - bezt[j - 1].vec[1][1]; /* when cyclic, jump from last point to first */ - if (cycle && j == total-1) + if (cycle && j == total - 1) j = 0; } /* ratio of x intervals */ - l[0] = l[count-1] = 1.0f; + l[0] = l[count - 1] = 1.0f; - for (int i = 1; i < count-1; i++) { - l[i] = dx[i+1] / dx[i]; + for (int i = 1; i < count - 1; i++) { + l[i] = dx[i + 1] / dx[i]; } /* compute handle clamp ranges */ @@ -3800,16 +3800,16 @@ static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int sta hmin[i] = -FLT_MAX; } - for (int i = 1, j = start+1; i < count; i++, j++) { + for (int i = 1, j = start + 1; i < count; i++, j++) { clamped_prev = clamped_cur; clamped_cur = ELEM(HD_AUTO_ANIM, bezt[j].h1, bezt[j].h2); - if (cycle && j == total-1) { + if (cycle && j == total - 1) { j = 0; clamped_cur = clamped_cur || ELEM(HD_AUTO_ANIM, bezt[j].h1, bezt[j].h2); } - bezier_clamp(hmax, hmin, i-1, dy[i], clamped_prev, clamped_prev); + bezier_clamp(hmax, hmin, i - 1, dy[i], clamped_prev, clamped_prev); bezier_clamp(hmax, hmin, i, dy[i] * l[i], clamped_cur, clamped_cur); } @@ -3817,9 +3817,9 @@ static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int sta float first_handle_adj = 0.0f, last_handle_adj = 0.0f; - if (full_cycle) { + if (full_cycle) { /* reduce the number of uknowns by one */ - int i = solve_count = count-1; + int i = solve_count = count - 1; dx[0] = dx[i]; dy[0] = dy[i]; @@ -3849,18 +3849,18 @@ static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int sta if (!solve_last) { sub_v2_v2v2(tmp, bezt_last->vec[1], bezt_last->vec[0]); - last_handle_adj = bezier_calc_handle_adj(tmp, dx[count-1]); + last_handle_adj = bezier_calc_handle_adj(tmp, dx[count - 1]); - bezier_lock_unknown(a, b, c, d, count-1, tmp[1]); + bezier_lock_unknown(a, b, c, d, count - 1, tmp[1]); } else { - bezier_eq_noaccel_left(a, b, c, d, dy, l, count-1); + bezier_eq_noaccel_left(a, b, c, d, dy, l, count - 1); } } /* main tridiagonal system of equations */ - for (int i = 1; i < count-1; i++) { + for (int i = 1; i < count - 1; i++) { bezier_eq_continuous(a, b, c, d, dy, l, i); } @@ -3868,11 +3868,11 @@ static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int sta if (!full_cycle) { if (count > 2 || solve_last) { - b[1] += l[1]*first_handle_adj; + b[1] += l[1] * first_handle_adj; } if (count > 2 || solve_first) { - b[count-2] += last_handle_adj; + b[count - 2] += last_handle_adj; } } @@ -3880,13 +3880,13 @@ static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int sta if (tridiagonal_solve_with_limits(a, b, c, d, h, hmin, hmax, solve_count)) { if (full_cycle) { - h[count-1] = h[0]; + h[count - 1] = h[0]; } - for (int i = 1, j = start+1; i < count-1; i++, j++) { - bool end = (j == total-1); + for (int i = 1, j = start + 1; i < count - 1; i++, j++) { + bool end = (j == total - 1); - bezier_output_handle(&bezt[j], false, - h[i] / l[i], end); + bezier_output_handle(&bezt[j], false, -h[i] / l[i], end); if (end) j = 0; @@ -3899,7 +3899,7 @@ static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int sta } if (solve_last) { - bezier_output_handle(bezt_last, false, - h[count-1] / l[count-1], start+count == total); + bezier_output_handle(bezt_last, false, -h[count - 1] / l[count - 1], start + count == total); } } @@ -3916,13 +3916,13 @@ static bool is_free_auto_point(BezTriple *bezt) void BKE_nurb_handle_smooth_fcurve(BezTriple *bezt, int total, bool cycle) { /* ignore cyclic extrapolation if end points are locked */ - cycle = cycle && is_free_auto_point(&bezt[0]) && is_free_auto_point(&bezt[total-1]); + cycle = cycle && is_free_auto_point(&bezt[0]) && is_free_auto_point(&bezt[total - 1]); /* if cyclic, try to find a sequence break point */ int search_base = 0; if (cycle) { - for (int i = 1; i < total-1; i++) { + for (int i = 1; i < total - 1; i++) { if (!is_free_auto_point(&bezt[i])) { search_base = i; break; @@ -3940,14 +3940,14 @@ void BKE_nurb_handle_smooth_fcurve(BezTriple *bezt, int total, bool cycle) * search_base. In cyclic mode these subsequences can span the cycle boundary. */ int start = search_base, count = 1; - for (int i = 1, j = start+1; i < total; i++, j++) { + for (int i = 1, j = start + 1; i < total; i++, j++) { /* in cyclic mode: jump from last to first point when necessary */ - if (j == total-1 && cycle) + if (j == total - 1 && cycle) j = 0; /* non auto handle closes the list (we come here at least for the last handle, see above) */ if (!is_free_auto_point(&bezt[j])) { - bezier_handle_calc_smooth_fcurve(bezt, total, start, count+1, cycle); + bezier_handle_calc_smooth_fcurve(bezt, total, start, count + 1, cycle); start = j; count = 1; } @@ -4110,7 +4110,7 @@ void BKE_nurb_handles_autocalc(Nurb *nu, int flag) bool align = false, leftsmall = false, rightsmall = false; /* left handle: */ - if (flag == 0 || (bezt1->f1 & flag) ) { + if (flag == 0 || (bezt1->f1 & flag)) { bezt1->h1 = HD_FREE; /* distance too short: vectorhandle */ if (len_squared_v3v3(bezt1->vec[1], bezt0->vec[1]) < eps_sq) { @@ -4129,7 +4129,7 @@ void BKE_nurb_handles_autocalc(Nurb *nu, int flag) } } /* right handle: */ - if (flag == 0 || (bezt1->f3 & flag) ) { + if (flag == 0 || (bezt1->f3 & flag)) { bezt1->h2 = HD_FREE; /* distance too short: vectorhandle */ if (len_squared_v3v3(bezt1->vec[1], bezt2->vec[1]) < eps_sq) { diff --git a/source/blender/blenlib/intern/math_solvers.c b/source/blender/blenlib/intern/math_solvers.c index 5ee2f10928f..b8a22900ba1 100644 --- a/source/blender/blenlib/intern/math_solvers.c +++ b/source/blender/blenlib/intern/math_solvers.c @@ -90,8 +90,8 @@ bool BLI_tridiagonal_solve(const float *a, const float *b, const float *c, const if (count < 1) return false; - size_t bytes = sizeof(double)*(unsigned)count; - double *c1 = (double *)MEM_mallocN(bytes*2, "tridiagonal_c1d1"); + size_t bytes = sizeof(double) * (unsigned)count; + double *c1 = (double *)MEM_mallocN(bytes * 2, "tridiagonal_c1d1"); double *d1 = c1 + count; if (!c1) @@ -106,10 +106,10 @@ bool BLI_tridiagonal_solve(const float *a, const float *b, const float *c, const d1[0] = d_prev = ((double)d[0]) / b[0]; for (i = 1; i < count; i++) { - double denum = b[i] - a[i]*c_prev; + double denum = b[i] - a[i] * c_prev; c1[i] = c_prev = c[i] / denum; - d1[i] = d_prev = (d[i] - a[i]*d_prev) / denum; + d1[i] = d_prev = (d[i] - a[i] * d_prev) / denum; } /* back pass */ @@ -138,15 +138,15 @@ bool BLI_tridiagonal_solve_cyclic(const float *a, const float *b, const float *c if (count < 1) return false; - float a0 = a[0], cN = c[count-1]; + float a0 = a[0], cN = c[count - 1]; /* if not really cyclic, fall back to the simple solver */ if (a0 == 0.0f && cN == 0.0f) { return BLI_tridiagonal_solve(a, b, c, d, r_x, count); } - size_t bytes = sizeof(float)*(unsigned)count; - float *tmp = (float*)MEM_mallocN(bytes*2, "tridiagonal_ex"); + size_t bytes = sizeof(float) * (unsigned)count; + float *tmp = (float *)MEM_mallocN(bytes * 2, "tridiagonal_ex"); float *b2 = tmp + count; if (!tmp) @@ -155,11 +155,11 @@ bool BLI_tridiagonal_solve_cyclic(const float *a, const float *b, const float *c /* prepare the noncyclic system; relies on tridiagonal_solve ignoring values */ memcpy(b2, b, bytes); b2[0] -= a0; - b2[count-1] -= cN; + b2[count - 1] -= cN; memset(tmp, 0, bytes); tmp[0] = a0; - tmp[count-1] = cN; + tmp[count - 1] = cN; /* solve for partial solution and adjustment vector */ bool success = @@ -168,7 +168,7 @@ bool BLI_tridiagonal_solve_cyclic(const float *a, const float *b, const float *c /* apply adjustment */ if (success) { - float coeff = (r_x[0] + r_x[count-1]) / (1.0f + tmp[0] + tmp[count-1]); + float coeff = (r_x[0] + r_x[count - 1]) / (1.0f + tmp[0] + tmp[count - 1]); for (int i = 0; i < count; i++) { r_x[i] -= coeff * tmp[i];