Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for nodes as functions #354

Merged
merged 60 commits into from
Aug 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
1164a6a
Added function nodes for JS and iOS
chrfalch Jul 27, 2019
695ea36
Added Android implementation of function, callfunction and param nodes
chrfalch Jul 27, 2019
a7edb0f
Added recursive call tests
chrfalch Jul 27, 2019
9b6c3af
Removed unsued node
chrfalch Jul 27, 2019
64a2d8e
Cleanup
chrfalch Jul 27, 2019
bc09442
Fixed registration of nodes
chrfalch Jul 27, 2019
3b8bcd9
Added update flag to force a subtree to be updated on evaluation
chrfalch Jul 27, 2019
43fd3e7
Updated with support for recursive forced updates
chrfalch Jul 27, 2019
8e1cf32
Removed overridden value - not necessary
chrfalch Jul 27, 2019
7a29015
Renamed updateCount to context count
chrfalch Jul 27, 2019
1bc9484
Added contextCount and implementation of overriding caching
chrfalch Jul 27, 2019
5141702
Added preliminary typings
chrfalch Jul 27, 2019
c9450f6
Added error on param/args mismatch
chrfalch Jul 27, 2019
441b983
Changed param node to support set value for REAValueNodes
chrfalch Jul 27, 2019
13334cd
Added setvalue support for ParamNode.
chrfalch Jul 27, 2019
56d0644
Added error when parameters and args has mismatch in length
chrfalch Jul 27, 2019
5f0c38b
Renamed funcdef to proc.
chrfalch Jul 28, 2019
822a0e6
Reverted some tests
chrfalch Jul 28, 2019
525325a
Reverted some tests
chrfalch Jul 28, 2019
664c2f0
Reverted some tests
chrfalch Jul 28, 2019
4eb91d6
Changed the proc API to proc((a,b) => add(a,b)).
chrfalch Jul 29, 2019
34ab46a
Changed the proc API to proc((a,b) => add(a,b)).
chrfalch Jul 29, 2019
65aebfc
Removed param node since it is no longer used after change in API
chrfalch Jul 29, 2019
68f7e76
Updated useProc to not have dependencies
chrfalch Jul 29, 2019
37cea97
Fixed typings for useProc
chrfalch Jul 29, 2019
ec1a6a8
Updated typings for proc - changed return value to function
chrfalch Jul 29, 2019
0aa24d4
Add memoization per context
osdnk Jul 30, 2019
fee0e3d
Adjusted typings for proc node
chrfalch Jul 30, 2019
3b324fa
Removed param import
chrfalch Jul 31, 2019
0e6fec3
Changed node evaluation policy
chrfalch Jul 31, 2019
91f8184
Removed tests in setValue for node type. Will throw an exception whic…
chrfalch Jul 31, 2019
3569cdf
Changed to use node.value() in evaluate function.
chrfalch Jul 31, 2019
e149f93
Make memoized value in context on Android
osdnk Jul 31, 2019
7c0d853
Merge branch 'FunctionNodes' into @osdnk/memoization-in-context
osdnk Jul 31, 2019
0e8e3cc
bunch of minor improvements
osdnk Jul 31, 2019
1729997
Make memoized value in context on Android
osdnk Jul 31, 2019
1cae9bb
semicolon
osdnk Jul 31, 2019
45ccfc0
createAnimatedParam map
osdnk Jul 31, 2019
ce4f949
revert unrelated
osdnk Jul 31, 2019
f403d81
Add spring example
osdnk Aug 2, 2019
30a599b
Store proper call ID while evaluating param's input (#357)
osdnk Aug 4, 2019
8125844
Merge pull request #1 from kmagiera/@osdnk/memoization-in-context
chrfalch Aug 5, 2019
b4157a7
removed unessecary null check for node.
chrfalch Aug 5, 2019
c538e61
Removed proc example code from test.js
chrfalch Aug 5, 2019
44a8e5f
Added documentation
chrfalch Aug 5, 2019
80a0100
Removed useProc
chrfalch Aug 5, 2019
efdc8e3
Added missing key property on spring nodes.
chrfalch Aug 5, 2019
f8e5fd4
Change callId to be concat of met contexts, not just last one
osdnk Aug 6, 2019
64bcee4
fix android
osdnk Aug 6, 2019
5731d22
Merge pull request #2 from kmagiera/@osdnk/callid-string
chrfalch Aug 7, 2019
5b41b75
Added some inline comments to the logic behind function/callfunction/…
chrfalch Aug 8, 2019
974241a
make final
osdnk Aug 8, 2019
a9d7be4
moar final
osdnk Aug 8, 2019
9e4820d
final
osdnk Aug 8, 2019
7d947e0
Update ParamNode.java
osdnk Aug 8, 2019
cf30bb9
Remove useless value
osdnk Aug 8, 2019
bf5ad92
Fix typings
osdnk Aug 8, 2019
df004cb
Remove useless value
osdnk Aug 8, 2019
8f7ce1f
Minor style improvements
osdnk Aug 8, 2019
1c38fd6
Update mock.js
osdnk Aug 8, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 72 additions & 11 deletions Example/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,64 @@ const {
Value,
Clock,
event,
proc,
} = Animated;

const betterSpring = proc(
(
finished,
velocity,
position,
time,
prevPosition,
toValue,
damping,
mass,
stiffness,
overshootClamping,
restSpeedThreshold,
restDisplacementThreshold,
clock
) =>
spring(
clock,
{
finished,
velocity,
position,
time,
prevPosition,
},
{
toValue,
damping,
mass,
stiffness,
overshootClamping,
restDisplacementThreshold,
restSpeedThreshold,
}
)
);

function springFill(clock, state, config) {
return betterSpring(
state.finished,
state.velocity,
state.position,
state.time,
new Value(0),
config.toValue,
config.damping,
config.mass,
config.stiffness,
config.overshootClamping,
config.restSpeedThreshold,
config.restDisplacementThreshold,
clock
);
}

function runSpring(clock, value, dest) {
const state = {
finished: new Value(0),
Expand All @@ -34,7 +90,7 @@ function runSpring(clock, value, dest) {
const config = {
toValue: new Value(0),
damping: 7,
mass: 1,
mass: 5,
stiffness: 121.6,
overshootClamping: false,
restSpeedThreshold: 0.001,
Expand All @@ -50,7 +106,7 @@ function runSpring(clock, value, dest) {
set(config.toValue, dest),
startClock(clock),
]),
spring(clock, state, config),
springFill(clock, state, config),
cond(state.finished, debug('stop clock', stopClock(clock))),
state.position,
]);
Expand Down Expand Up @@ -92,29 +148,34 @@ export default class Example extends Component {
// const transX = new Value(0);
const clock = new Clock();
// const twenty = new Value(20);
// const thirty = new Value(30);
// this._transX = cond(new Value(0), twenty, multiply(3, thirty));
this._transX = runTiming(clock, -120, 120);
this.t = Array.from(Array(40)).map(() =>
runSpring(new Clock(), Math.random() * -200, Math.random() * 200)
);
}

componentDidMount() {
// Animated.spring(this._transX, {
// duration: 300,
// velocity: -300,
// toValue: 150,
// }).start();
}

render() {
return (
<View style={styles.container}>
<Animated.View
style={[styles.box, { transform: [{ translateX: this._transX }] }]}
/>
<View style={styles.container}>
{Array.from(Array(40)).map((_, i) => (
<Animated.View
key={i}
style={[styles.box, { transform: [{ translateX: this.t[i] }] }]}
/>
))}
</View>
);
}
}

const BOX_SIZE = 100;
const BOX_SIZE = 10;

const styles = StyleSheet.create({
container: {
Expand All @@ -129,6 +190,6 @@ const styles = StyleSheet.create({
borderColor: '#F5FCFF',
alignSelf: 'center',
backgroundColor: 'plum',
margin: BOX_SIZE / 2,
margin: 2,
},
});
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,24 @@ Returns an accumulated value of the given node. This node stores a sum of all ev

Works the same way as with the original `Animated` library.

---
### `proc`

Returns a callable function node that can be used to define expressions that can be called from other nodes.

Example:
´´´js
// Global constant
const myProc = proc((a, b) => multiply(a,b));

// In your component
const style = { width: proc(10, 10 )};
´´´

A proc node should be declared as a global constant in your code and not recreated from inside components.

It is not possible to reference nodes that are not passed as parameters.

---
### `interpolate`
```js
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
import com.swmansion.reanimated.nodes.StyleNode;
import com.swmansion.reanimated.nodes.TransformNode;
import com.swmansion.reanimated.nodes.ValueNode;
import com.swmansion.reanimated.nodes.ParamNode;
import com.swmansion.reanimated.nodes.FunctionNode;
import com.swmansion.reanimated.nodes.CallFuncNode;

import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -264,6 +267,12 @@ public void createNode(int nodeID, ReadableMap config) {
node = new AlwaysNode(nodeID, config, this);
} else if ("concat".equals(type)) {
node = new ConcatNode(nodeID, config, this);
} else if ("param".equals(type)) {
node = new ParamNode(nodeID, config, this);
} else if ("func".equals(type)) {
node = new FunctionNode(nodeID, config, this);
} else if ("callfunc".equals(type)) {
node = new CallFuncNode(nodeID, config, this);
} else {
throw new JSApplicationIllegalArgumentException("Unsupported node type: " + type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
public class UpdateContext {

public long updateLoopID = 0;
public String callID = "";
public final ArrayList<Node> updatedNodes = new ArrayList<>();
chrfalch marked this conversation as resolved.
Show resolved Hide resolved

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.swmansion.reanimated.nodes;

import com.facebook.react.bridge.ReadableMap;
import com.swmansion.reanimated.NodesManager;
import com.swmansion.reanimated.Utils;

public class CallFuncNode extends Node {

private String mPreviousCallID;
private final int mWhatNodeID;
private final int[] mArgs;
private final int[] mParams;

public CallFuncNode(int nodeID, ReadableMap config, NodesManager nodesManager) {
super(nodeID, config, nodesManager);
mWhatNodeID = config.getInt("what");
mParams = Utils.processIntArray(config.getArray("params"));
mArgs = Utils.processIntArray(config.getArray("args"));
}

private void beginContext() {
mPreviousCallID = mNodesManager.updateContext.callID;
mNodesManager.updateContext.callID = mNodesManager.updateContext.callID + '/' + String.valueOf(mNodeID);
for (int i = 0; i < mParams.length; i++) {
int paramId = mParams[i];
ParamNode paramNode = mNodesManager.findNodeById(paramId, ParamNode.class);
chrfalch marked this conversation as resolved.
Show resolved Hide resolved
paramNode.beginContext(mArgs[i], mPreviousCallID);
}
}

private void endContext() {
for (int i = 0; i < mParams.length; i++) {
int paramId = mParams[i];
ParamNode paramNode = mNodesManager.findNodeById(paramId, ParamNode.class);
paramNode.endContext();
}
mNodesManager.updateContext.callID = mPreviousCallID;
}

@Override
protected Object evaluate() {
beginContext();
Node whatNode = mNodesManager.findNodeById(mWhatNodeID, Node.class);
Object retVal = whatNode.value();
endContext();
return retVal;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.swmansion.reanimated.nodes;

import com.facebook.react.bridge.ReadableMap;
import com.swmansion.reanimated.NodesManager;

public class FunctionNode extends Node {

private final int mWhatNodeID;

public FunctionNode(int nodeID, ReadableMap config, NodesManager nodesManager) {
super(nodeID, config, nodesManager);
mWhatNodeID = config.getInt("what");
}

@Override
protected Object evaluate() {
Node what = mNodesManager.findNodeById(mWhatNodeID, Node.class);
return what.value();
}
}
26 changes: 17 additions & 9 deletions android/src/main/java/com/swmansion/reanimated/nodes/Node.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.swmansion.reanimated.nodes;

import android.util.SparseArray;

import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;
import com.swmansion.reanimated.NodesManager;
import com.swmansion.reanimated.UpdateContext;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

Expand All @@ -21,13 +25,14 @@ public abstract class Node {
protected final int mNodeID;
protected final NodesManager mNodesManager;

private final UpdateContext mUpdateContext;
protected final UpdateContext mUpdateContext;

private long mLastLoopID = -1;
private @Nullable Object mMemoizedValue;
private final Map<String, Long> mLastLoopID = new HashMap<>();
private final Map<String, Object> mMemoizedValue = new HashMap<>();
private @Nullable List<Node> mChildren; /* lazy-initialized when a child is added */

public Node(int nodeID, @Nullable ReadableMap config, NodesManager nodesManager) {
mLastLoopID.put("", 1L);
mNodeID = nodeID;
mNodesManager = nodesManager;
mUpdateContext = nodesManager.updateContext;
Expand All @@ -36,11 +41,14 @@ public Node(int nodeID, @Nullable ReadableMap config, NodesManager nodesManager)
protected abstract @Nullable Object evaluate();

public final @Nullable Object value() {
if (mLastLoopID < mUpdateContext.updateLoopID) {
mLastLoopID = mUpdateContext.updateLoopID;
return (mMemoizedValue = evaluate());
if (!mLastLoopID.containsKey(mUpdateContext.callID) || mLastLoopID.get(mUpdateContext.callID) < mUpdateContext.updateLoopID) {
mLastLoopID.put(mUpdateContext.callID, mUpdateContext.updateLoopID);
Object result = evaluate();
mMemoizedValue.put(mUpdateContext.callID, result);

return result;
}
return mMemoizedValue;
return mMemoizedValue.get(mUpdateContext.callID);
}

/**
Expand Down Expand Up @@ -83,12 +91,12 @@ protected void markUpdated() {
}

protected final void dangerouslyRescheduleEvaluate() {
mLastLoopID = -1;
mLastLoopID.put(mUpdateContext.callID, -1L);
markUpdated();
}

protected final void forceUpdateMemoizedValue(Object value) {
mMemoizedValue = value;
mMemoizedValue.put(mUpdateContext.callID, value);
markUpdated();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.swmansion.reanimated.nodes;

import com.facebook.react.bridge.ReadableMap;
import com.swmansion.reanimated.NodesManager;

import java.util.Stack;

public class ParamNode extends ValueNode {

private final Stack<Integer> mArgsStack;
private String mPrevCallID;

public ParamNode(int nodeID, ReadableMap config, NodesManager nodesManager) {
super(nodeID, config, nodesManager);
mArgsStack = new Stack<>();
}

@Override
public void setValue(Object value) {
Node node = mNodesManager.findNodeById(mArgsStack.peek(), Node.class);
String callID = mUpdateContext.callID;
mUpdateContext.callID = mPrevCallID;
((ValueNode) node).setValue(value);
mUpdateContext.callID = callID;
}

public void beginContext(Integer ref, String prevCallID) {
mPrevCallID = prevCallID;
mArgsStack.push(ref);
}


public void endContext() {
mArgsStack.pop();
}


@Override
protected Object evaluate() {
String callID = mUpdateContext.callID;
mUpdateContext.callID = mPrevCallID;
Node node = mNodesManager.findNodeById(mArgsStack.peek(), Node.class);
Object val = node.value();
mUpdateContext.callID = callID;
return val;
}
}
7 changes: 7 additions & 0 deletions ios/Nodes/REACallFuncNode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

#import "REANode.h"

@interface REACallFuncNode : REANode

@end

Loading