Page Menu
Home
Search
Configure Global Search
Log In
Paste
P2602
(An Untitled Masterwork)
Active
Public
Actions
Authored by
Charlie Jolly (charlie)
on Nov 18 2021, 6:25 PM.
Edit Paste
Archive Paste
View Raw File
Subscribe
Mute Notifications
Award Token
Tags
None
Subscribers
None
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include
"BLI_easing.h"
#include
"UI_interface.h"
#include
"UI_resources.h"
#include
"node_function_util.hh"
/* Easing Variable Naming.
* To generalise the use of Easing functions, the following socket names are used in place of the
* original parameter names.
* Time > Value
* Begin > Start
* Change > End
* Duration > Range
*/
namespace
blender
::
nodes
{
static
void
fn_node_easing_declare
(
NodeDeclarationBuilder
&
b
)
{
b
.
is_function_node
();
b
.
add_input
<
decl
::
Float
>
(
N_
(
"Value"
)).
min
(
-
FLT_MAX
).
max
(
FLT_MAX
);
b
.
add_input
<
decl
::
Float
>
(
N_
(
"Start"
))
.
min
(
-
FLT_MAX
)
.
max
(
FLT_MAX
)
.
description
(
N_
(
"Result at the start of easing when Value is 0.0"
));
b
.
add_input
<
decl
::
Float
>
(
N_
(
"End"
)).
default_value
(
1.0f
).
min
(
-
FLT_MAX
).
max
(
FLT_MAX
).
description
(
N_
(
"Result at the end of easing when Value is equal to Range"
));
b
.
add_input
<
decl
::
Float
>
(
N_
(
"Range"
))
.
default_value
(
1.0f
)
.
min
(
-
FLT_MAX
)
.
max
(
FLT_MAX
)
.
description
(
N_
(
"Range or duration of easing"
));
b
.
add_input
<
decl
::
Float
>
(
N_
(
"Overshoot"
)).
default_value
(
1.0f
).
min
(
-
FLT_MAX
).
max
(
FLT_MAX
);
b
.
add_input
<
decl
::
Float
>
(
N_
(
"Amplitude"
)).
default_value
(
1.0f
).
min
(
-
FLT_MAX
).
max
(
FLT_MAX
);
b
.
add_input
<
decl
::
Float
>
(
N_
(
"Period"
)).
default_value
(
1.0f
).
min
(
-
FLT_MAX
).
max
(
FLT_MAX
);
b
.
add_input
<
decl
::
Bool
>
(
N_
(
"Clamp"
))
.
default_value
(
true
)
.
description
(
N_
(
"Clamp Value between 0.0 and Range"
));
b
.
add_output
<
decl
::
Float
>
(
N_
(
"Result"
));
};
}
// namespace blender::nodes
static
void
fn_node_easing_layout
(
uiLayout
*
layout
,
bContext
*
UNUSED
(
C
),
PointerRNA
*
ptr
)
{
bNode
*
node
=
(
bNode
*
)
ptr
->
data
;
NodeEasing
*
node_storage
=
(
NodeEasing
*
)
node
->
storage
;
NodeEasingOperation
operation
=
(
NodeEasingOperation
)
node_storage
->
operation
;
uiLayoutSetPropSep
(
layout
,
true
);
uiLayoutSetPropDecorate
(
layout
,
false
);
uiLayout
*
column
=
uiLayoutColumn
(
layout
,
false
);
uiItemR
(
column
,
ptr
,
"operation"
,
0
,
""
,
ICON_NONE
);
if
(
operation
!=
NODE_EASING_LINEAR
)
{
uiItemR
(
column
,
ptr
,
"direction"
,
0
,
""
,
ICON_NONE
);
}
}
static
void
fn_node_easing_init
(
bNodeTree
*
UNUSED
(
tree
),
bNode
*
node
)
{
NodeEasing
*
data
=
(
NodeEasing
*
)
MEM_callocN
(
sizeof
(
NodeEasing
),
__func__
);
data
->
operation
=
NODE_EASING_BOUNCE
;
data
->
direction
=
NODE_EASING_DIRECTION_IN
;
node
->
storage
=
data
;
}
namespace
blender
::
nodes
{
static
void
fn_node_easing_update
(
bNodeTree
*
ntree
,
bNode
*
node
)
{
const
NodeEasing
*
node_storage
=
(
NodeEasing
*
)
node
->
storage
;
const
NodeEasingOperation
operation
=
(
const
NodeEasingOperation
)
node_storage
->
operation
;
bNodeSocket
*
sockOvershoot
=
(
bNodeSocket
*
)
BLI_findlink
(
&
node
->
inputs
,
4
);
bNodeSocket
*
sockAmplitude
=
(
bNodeSocket
*
)
BLI_findlink
(
&
node
->
inputs
,
5
);
bNodeSocket
*
sockPeriod
=
(
bNodeSocket
*
)
BLI_findlink
(
&
node
->
inputs
,
6
);
nodeSetSocketAvailability
(
ntree
,
sockOvershoot
,
operation
==
NODE_EASING_BACK
);
nodeSetSocketAvailability
(
ntree
,
sockAmplitude
,
operation
==
NODE_EASING_ELASTIC
);
nodeSetSocketAvailability
(
ntree
,
sockPeriod
,
operation
==
NODE_EASING_ELASTIC
);
}
static
float
clamp_range
(
const
float
value
,
const
float
a
,
const
float
b
)
{
return
(
a
<
b
)
?
clamp_f
(
value
,
a
,
b
)
:
clamp_f
(
value
,
b
,
a
);
}
class
EasingElasticFunction
:
public
fn
::
MultiFunction
{
private
:
int
direction_
;
public
:
EasingElasticFunction
(
int
direction
)
:
direction_
(
direction
)
{
static
fn
::
MFSignature
signature
=
create_signature
();
this
->
set_signature
(
&
signature
);
}
static
fn
::
MFSignature
create_signature
()
{
fn
::
MFSignatureBuilder
signature
{
"Easing Elastic"
};
signature
.
single_input
<
float
>
(
"Value"
);
signature
.
single_input
<
float
>
(
"Start"
);
signature
.
single_input
<
float
>
(
"End"
);
signature
.
single_input
<
float
>
(
"Range"
);
signature
.
single_input
<
float
>
(
"Amplitude"
);
signature
.
single_input
<
float
>
(
"Period"
);
signature
.
single_input
<
bool
>
(
"Clamp"
);
signature
.
single_output
<
float
>
(
"Result"
);
return
signature
.
build
();
}
using
EaseElasticFunc
=
float
(
*
)(
float
,
float
,
float
,
float
,
float
,
float
);
static
EaseElasticFunc
get_ease_elastic_func
(
const
int
direction
)
{
switch
(
direction
)
{
case
NODE_EASING_DIRECTION_IN
:
{
return
BLI_easing_elastic_ease_in
;
}
case
NODE_EASING_DIRECTION_OUT
:
{
return
BLI_easing_elastic_ease_out
;
}
case
NODE_EASING_DIRECTION_IN_OUT
:
{
return
BLI_easing_elastic_ease_in_out
;
}
}
return
nullptr
;
}
void
call
(
IndexMask
mask
,
fn
::
MFParams
params
,
fn
::
MFContext
UNUSED
(
context
))
const
override
{
const
VArray
<
float
>
&
value
=
params
.
readonly_single_input
<
float
>
(
0
,
"Value"
);
const
VArray
<
float
>
&
start
=
params
.
readonly_single_input
<
float
>
(
1
,
"Start"
);
const
VArray
<
float
>
&
end
=
params
.
readonly_single_input
<
float
>
(
2
,
"End"
);
const
VArray
<
float
>
&
range
=
params
.
readonly_single_input
<
float
>
(
3
,
"Range"
);
const
VArray
<
float
>
&
amplitude
=
params
.
readonly_single_input
<
float
>
(
4
,
"Amplitude"
);
const
VArray
<
float
>
&
period
=
params
.
readonly_single_input
<
float
>
(
5
,
"Period"
);
const
VArray
<
bool
>
&
clamp
=
params
.
readonly_single_input
<
bool
>
(
6
,
"Clamp"
);
MutableSpan
<
float
>
results
=
params
.
uninitialized_single_output
<
float
>
(
7
,
"Result"
);
const
EaseElasticFunc
easefunc
=
get_ease_elastic_func
(
direction_
);
if
(
easefunc
!=
nullptr
)
{
for
(
int64_t
i
:
mask
)
{
const
float
val
=
clamp
[
i
]
?
clamp_range
(
value
[
i
],
0.0
,
range
[
i
])
:
value
[
i
];
const
float
change
=
end
[
i
]
-
start
[
i
];
results
[
i
]
=
(
range
[
i
]
!=
0.0f
)
?
easefunc
(
val
,
start
[
i
],
change
,
range
[
i
],
amplitude
[
i
],
period
[
i
])
:
start
[
i
];
}
}
}
};
class
EasingBackFunction
:
public
fn
::
MultiFunction
{
private
:
int
direction_
;
public
:
EasingBackFunction
(
int
direction
)
:
direction_
(
direction
)
{
static
fn
::
MFSignature
signature
=
create_signature
();
this
->
set_signature
(
&
signature
);
}
static
fn
::
MFSignature
create_signature
()
{
fn
::
MFSignatureBuilder
signature
{
"Easing Back"
};
signature
.
single_input
<
float
>
(
"Value"
);
signature
.
single_input
<
float
>
(
"Start"
);
signature
.
single_input
<
float
>
(
"End"
);
signature
.
single_input
<
float
>
(
"Range"
);
signature
.
single_input
<
float
>
(
"Overshoot"
);
signature
.
single_input
<
bool
>
(
"Clamp"
);
signature
.
single_output
<
float
>
(
"Result"
);
return
signature
.
build
();
}
using
EaseBackFunc
=
float
(
*
)(
float
,
float
,
float
,
float
,
float
);
static
EaseBackFunc
get_ease_back_func
(
const
int
direction
)
{
switch
(
direction
)
{
case
NODE_EASING_DIRECTION_IN
:
{
return
BLI_easing_back_ease_in
;
}
case
NODE_EASING_DIRECTION_OUT
:
{
return
BLI_easing_back_ease_out
;
}
case
NODE_EASING_DIRECTION_IN_OUT
:
{
return
BLI_easing_back_ease_in_out
;
}
}
return
nullptr
;
}
void
call
(
IndexMask
mask
,
fn
::
MFParams
params
,
fn
::
MFContext
UNUSED
(
context
))
const
override
{
const
VArray
<
float
>
&
value
=
params
.
readonly_single_input
<
float
>
(
0
,
"Value"
);
const
VArray
<
float
>
&
start
=
params
.
readonly_single_input
<
float
>
(
1
,
"Start"
);
const
VArray
<
float
>
&
end
=
params
.
readonly_single_input
<
float
>
(
2
,
"End"
);
const
VArray
<
float
>
&
range
=
params
.
readonly_single_input
<
float
>
(
3
,
"Range"
);
const
VArray
<
float
>
&
overshoot
=
params
.
readonly_single_input
<
float
>
(
4
,
"Overshoot"
);
const
VArray
<
bool
>
&
clamp
=
params
.
readonly_single_input
<
bool
>
(
5
,
"Clamp"
);
MutableSpan
<
float
>
results
=
params
.
uninitialized_single_output
<
float
>
(
6
,
"Result"
);
const
EaseBackFunc
easefunc
=
get_ease_back_func
(
direction_
);
if
(
easefunc
!=
nullptr
)
{
for
(
int64_t
i
:
mask
)
{
const
float
val
=
clamp
[
i
]
?
clamp_range
(
value
[
i
],
0.0
,
range
[
i
])
:
value
[
i
];
const
float
change
=
end
[
i
]
-
start
[
i
];
results
[
i
]
=
(
range
[
i
]
!=
0.0f
)
?
easefunc
(
val
,
start
[
i
],
change
,
range
[
i
],
overshoot
[
i
])
:
start
[
i
];
}
}
}
};
class
EasingFunction
:
public
fn
::
MultiFunction
{
private
:
int
direction_
;
int
operation_
;
public
:
EasingFunction
(
int
direction
,
int
operation
)
:
direction_
(
direction
),
operation_
(
operation
)
{
static
fn
::
MFSignature
signature
=
create_signature
();
this
->
set_signature
(
&
signature
);
}
static
fn
::
MFSignature
create_signature
()
{
fn
::
MFSignatureBuilder
signature
{
"Easing Back"
};
signature
.
single_input
<
float
>
(
"Value"
);
signature
.
single_input
<
float
>
(
"Start"
);
signature
.
single_input
<
float
>
(
"End"
);
signature
.
single_input
<
float
>
(
"Range"
);
signature
.
single_input
<
bool
>
(
"Clamp"
);
signature
.
single_output
<
float
>
(
"Result"
);
return
signature
.
build
();
}
using
EaseFunc
=
float
(
*
)(
float
,
float
,
float
,
float
);
static
EaseFunc
get_ease_func
(
const
int
direction
,
const
int
operation
)
{
switch
(
operation
)
{
case
NODE_EASING_LINEAR
:
{
return
BLI_easing_linear_ease
;
}
case
NODE_EASING_BOUNCE
:
{
switch
(
direction
)
{
case
NODE_EASING_DIRECTION_IN
:
{
return
BLI_easing_bounce_ease_in
;
}
case
NODE_EASING_DIRECTION_OUT
:
{
return
BLI_easing_bounce_ease_out
;
}
case
NODE_EASING_DIRECTION_IN_OUT
:
{
return
BLI_easing_bounce_ease_in_out
;
}
}
break
;
}
case
NODE_EASING_CIRC
:
{
switch
(
direction
)
{
case
NODE_EASING_DIRECTION_IN
:
{
return
BLI_easing_circ_ease_in
;
}
case
NODE_EASING_DIRECTION_OUT
:
{
return
BLI_easing_circ_ease_out
;
}
case
NODE_EASING_DIRECTION_IN_OUT
:
{
return
BLI_easing_circ_ease_in_out
;
}
}
break
;
}
case
NODE_EASING_CUBIC
:
{
switch
(
direction
)
{
case
NODE_EASING_DIRECTION_IN
:
{
return
BLI_easing_cubic_ease_in
;
}
case
NODE_EASING_DIRECTION_OUT
:
{
return
BLI_easing_cubic_ease_out
;
}
case
NODE_EASING_DIRECTION_IN_OUT
:
{
return
BLI_easing_cubic_ease_in_out
;
}
}
break
;
}
case
NODE_EASING_EXPO
:
{
switch
(
direction
)
{
case
NODE_EASING_DIRECTION_IN
:
{
return
BLI_easing_expo_ease_in
;
}
case
NODE_EASING_DIRECTION_OUT
:
{
return
BLI_easing_expo_ease_out
;
}
case
NODE_EASING_DIRECTION_IN_OUT
:
{
return
BLI_easing_expo_ease_in_out
;
}
}
break
;
}
case
NODE_EASING_QUAD
:
{
switch
(
direction
)
{
case
NODE_EASING_DIRECTION_IN
:
{
return
BLI_easing_quad_ease_in
;
}
case
NODE_EASING_DIRECTION_OUT
:
{
return
BLI_easing_quad_ease_out
;
}
case
NODE_EASING_DIRECTION_IN_OUT
:
{
return
BLI_easing_quad_ease_in_out
;
}
}
break
;
}
case
NODE_EASING_QUART
:
{
switch
(
direction
)
{
case
NODE_EASING_DIRECTION_IN
:
{
return
BLI_easing_quart_ease_in
;
}
case
NODE_EASING_DIRECTION_OUT
:
{
return
BLI_easing_quart_ease_out
;
}
case
NODE_EASING_DIRECTION_IN_OUT
:
{
return
BLI_easing_quart_ease_in_out
;
}
}
break
;
}
case
NODE_EASING_QUINT
:
{
switch
(
direction
)
{
case
NODE_EASING_DIRECTION_IN
:
{
return
BLI_easing_quint_ease_in
;
}
case
NODE_EASING_DIRECTION_OUT
:
{
return
BLI_easing_quint_ease_out
;
}
case
NODE_EASING_DIRECTION_IN_OUT
:
{
return
BLI_easing_quint_ease_in_out
;
}
}
break
;
}
case
NODE_EASING_SINE
:
{
switch
(
direction
)
{
case
NODE_EASING_DIRECTION_IN
:
{
return
BLI_easing_sine_ease_in
;
}
case
NODE_EASING_DIRECTION_OUT
:
{
return
BLI_easing_sine_ease_out
;
}
case
NODE_EASING_DIRECTION_IN_OUT
:
{
return
BLI_easing_sine_ease_in_out
;
}
}
break
;
}
}
return
nullptr
;
}
void
call
(
IndexMask
mask
,
fn
::
MFParams
params
,
fn
::
MFContext
UNUSED
(
context
))
const
override
{
const
VArray
<
float
>
&
value
=
params
.
readonly_single_input
<
float
>
(
0
,
"Value"
);
const
VArray
<
float
>
&
start
=
params
.
readonly_single_input
<
float
>
(
1
,
"Start"
);
const
VArray
<
float
>
&
end
=
params
.
readonly_single_input
<
float
>
(
2
,
"End"
);
const
VArray
<
float
>
&
range
=
params
.
readonly_single_input
<
float
>
(
3
,
"Range"
);
const
VArray
<
bool
>
&
clamp
=
params
.
readonly_single_input
<
bool
>
(
4
,
"Clamp"
);
MutableSpan
<
float
>
results
=
params
.
uninitialized_single_output
<
float
>
(
5
,
"Result"
);
BLI_assert
(
operation_
!=
NODE_EASING_BACK
&&
operation_
!=
NODE_EASING_ELASTIC
);
const
EaseFunc
easefunc
=
get_ease_func
(
direction_
,
operation_
);
if
(
easefunc
!=
nullptr
)
{
for
(
int64_t
i
:
mask
)
{
const
float
val
=
clamp
[
i
]
?
clamp_range
(
value
[
i
],
0.0
,
range
[
i
])
:
value
[
i
];
const
float
change
=
end
[
i
]
-
start
[
i
];
results
[
i
]
=
(
range
[
i
]
!=
0.0f
)
?
easefunc
(
val
,
start
[
i
],
change
,
range
[
i
])
:
start
[
i
];
}
}
}
};
static
void
fn_node_easing_build_multi_function
(
blender
::
nodes
::
NodeMultiFunctionBuilder
&
builder
)
{
bNode
&
node
=
builder
.
node
();
NodeEasing
*
data
=
(
NodeEasing
*
)
node
.
storage
;
if
(
data
->
operation
==
NODE_EASING_BACK
)
{
builder
.
construct_and_set_matching_fn
<
EasingBackFunction
>
(
data
->
direction
);
}
else
if
(
data
->
operation
==
NODE_EASING_ELASTIC
)
{
builder
.
construct_and_set_matching_fn
<
EasingElasticFunction
>
(
data
->
direction
);
}
else
{
builder
.
construct_and_set_matching_fn
<
EasingFunction
>
(
data
->
direction
,
data
->
operation
);
}
}
}
// namespace blender::nodes
void
register_node_type_fn_easing
()
{
static
bNodeType
ntype
;
fn_node_type_base
(
&
ntype
,
FN_NODE_EASING
,
"Easing"
,
NODE_CLASS_CONVERTER
,
0
);
ntype
.
declare
=
blender
::
nodes
::
fn_node_easing_declare
;
node_type_update
(
&
ntype
,
blender
::
nodes
::
fn_node_easing_update
);
ntype
.
build_multi_function
=
blender
::
nodes
::
fn_node_easing_build_multi_function
;
ntype
.
draw_buttons
=
fn_node_easing_layout
;
node_type_init
(
&
ntype
,
fn_node_easing_init
);
node_type_storage
(
&
ntype
,
"NodeEasing"
,
node_free_standard_storage
,
node_copy_standard_storage
);
nodeRegisterType
(
&
ntype
);
}
Event Timeline
Charlie Jolly (charlie)
created this paste.
Nov 18 2021, 6:25 PM
Charlie Jolly (charlie)
mentioned this in
D12035: Geometry Nodes: Add Easing Function Node
.
Nov 18 2021, 7:02 PM
Charlie Jolly (charlie)
edited the content of this paste.
(Show Details)
Nov 19 2021, 8:07 PM
Log In to Comment