react-native-smooth-clip-view 0.2.9 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -349
- package/android/build.gradle +8 -4
- package/android/proguard-rules.pro +3 -3
- package/android/src/main/cpp/SmoothClipAndroid.h +13 -6
- package/android/src/main/cpp/SmoothClipBindings.cpp +605 -123
- package/android/src/main/cpp/SmoothClipRegistry.cpp +975 -209
- package/android/src/main/java/com/smoothclipview/ClipGeometryNormalizer.kt +192 -118
- package/android/src/main/java/com/smoothclipview/SmoothClipBindings.kt +22 -4
- package/android/src/main/java/com/smoothclipview/SmoothClipModule.kt +81 -56
- package/android/src/main/java/com/smoothclipview/SmoothClipView.kt +449 -240
- package/android/src/main/java/com/smoothclipview/SmoothClipViewManager.kt +150 -38
- package/cpp/SmoothClipAnimationCurve.h +317 -397
- package/cpp/SmoothClipRegistry.h +84 -20
- package/cpp/SmoothClipSharedGeometry.h +131 -22
- package/cpp/SmoothClipVelocityTracker.h +16 -12
- package/ios/SmoothClipGeometry.h +188 -21
- package/ios/SmoothClipRegistry.mm +869 -368
- package/ios/SmoothClipRegistrySnapshot.h +13 -0
- package/ios/SmoothClipTurboModule.cpp +513 -156
- package/ios/SmoothClipTurboModule.h +123 -56
- package/ios/SmoothClipView.mm +1074 -449
- package/ios/SmoothClipViewRegistry.h +3 -7
- package/ios/reactnativesmoothclipview.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/reactnativesmoothclipview.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/lib/module/NativeSmoothClipModule.js.map +1 -1
- package/lib/module/SmoothClipView.ios.js +94 -11
- package/lib/module/SmoothClipView.ios.js.map +1 -1
- package/lib/module/SmoothClipView.js +1 -46
- package/lib/module/SmoothClipView.js.map +1 -1
- package/lib/module/SmoothClipViewNativeComponent.ts +32 -12
- package/lib/module/capabilities.js +4 -0
- package/lib/module/capabilities.js.map +1 -0
- package/lib/module/capabilities.native.js +18 -0
- package/lib/module/capabilities.native.js.map +1 -0
- package/lib/module/capabilityTypes.js +2 -0
- package/lib/module/capabilityTypes.js.map +1 -0
- package/lib/module/controllerInternals.js +28 -0
- package/lib/module/controllerInternals.js.map +1 -0
- package/lib/module/controllerLifecycle.js +9 -0
- package/lib/module/controllerLifecycle.js.map +1 -0
- package/lib/module/controllerTypes.js +4 -0
- package/lib/module/controllerTypes.js.map +1 -0
- package/lib/module/controllers.android.js +4 -0
- package/lib/module/controllers.android.js.map +1 -0
- package/lib/module/controllers.ios.js +4 -0
- package/lib/module/controllers.ios.js.map +1 -0
- package/lib/module/controllers.js +4 -0
- package/lib/module/controllers.js.map +1 -0
- package/lib/module/controllers.native.js +89 -0
- package/lib/module/controllers.native.js.map +1 -0
- package/lib/module/geometry.js +180 -30
- package/lib/module/geometry.js.map +1 -1
- package/lib/module/groupTypes.js +4 -0
- package/lib/module/groupTypes.js.map +1 -0
- package/lib/module/groups.android.js +4 -0
- package/lib/module/groups.android.js.map +1 -0
- package/lib/module/groups.ios.js +4 -0
- package/lib/module/groups.ios.js.map +1 -0
- package/lib/module/groups.js +4 -0
- package/lib/module/groups.js.map +1 -0
- package/lib/module/groups.native.js +399 -0
- package/lib/module/groups.native.js.map +1 -0
- package/lib/module/ids.js +8 -0
- package/lib/module/ids.js.map +1 -0
- package/lib/module/index.js +4 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/nativeCompletion.js +37 -16
- package/lib/module/nativeCompletion.js.map +1 -1
- package/lib/module/presentationCodec.js +81 -0
- package/lib/module/presentationCodec.js.map +1 -0
- package/lib/typescript/src/NativeSmoothClipModule.d.ts +20 -4
- package/lib/typescript/src/NativeSmoothClipModule.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipView.d.ts +2 -10
- package/lib/typescript/src/SmoothClipView.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipView.ios.d.ts +139 -4
- package/lib/typescript/src/SmoothClipView.ios.d.ts.map +1 -1
- package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts +16 -3
- package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/capabilities.d.ts +3 -0
- package/lib/typescript/src/capabilities.d.ts.map +1 -0
- package/lib/typescript/src/capabilities.native.d.ts +3 -0
- package/lib/typescript/src/capabilities.native.d.ts.map +1 -0
- package/lib/typescript/src/capabilityTypes.d.ts +4 -0
- package/lib/typescript/src/capabilityTypes.d.ts.map +1 -0
- package/lib/typescript/src/controllerInternals.d.ts +14 -0
- package/lib/typescript/src/controllerInternals.d.ts.map +1 -0
- package/lib/typescript/src/controllerLifecycle.d.ts +3 -0
- package/lib/typescript/src/controllerLifecycle.d.ts.map +1 -0
- package/lib/typescript/src/controllerTypes.d.ts +62 -0
- package/lib/typescript/src/controllerTypes.d.ts.map +1 -0
- package/lib/typescript/src/controllers.android.d.ts +2 -0
- package/lib/typescript/src/controllers.android.d.ts.map +1 -0
- package/lib/typescript/src/controllers.d.ts +2 -0
- package/lib/typescript/src/controllers.d.ts.map +1 -0
- package/lib/typescript/src/controllers.ios.d.ts +2 -0
- package/lib/typescript/src/controllers.ios.d.ts.map +1 -0
- package/lib/typescript/src/controllers.native.d.ts +3 -0
- package/lib/typescript/src/controllers.native.d.ts.map +1 -0
- package/lib/typescript/src/geometry.d.ts +49 -5
- package/lib/typescript/src/geometry.d.ts.map +1 -1
- package/lib/typescript/src/groupTypes.d.ts +31 -0
- package/lib/typescript/src/groupTypes.d.ts.map +1 -0
- package/lib/typescript/src/groups.android.d.ts +2 -0
- package/lib/typescript/src/groups.android.d.ts.map +1 -0
- package/lib/typescript/src/groups.d.ts +2 -0
- package/lib/typescript/src/groups.d.ts.map +1 -0
- package/lib/typescript/src/groups.ios.d.ts +2 -0
- package/lib/typescript/src/groups.ios.d.ts.map +1 -0
- package/lib/typescript/src/groups.native.d.ts +3 -0
- package/lib/typescript/src/groups.native.d.ts.map +1 -0
- package/lib/typescript/src/ids.d.ts +2 -0
- package/lib/typescript/src/ids.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +8 -5
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/nativeCompletion.d.ts +16 -6
- package/lib/typescript/src/nativeCompletion.d.ts.map +1 -1
- package/lib/typescript/src/presentationCodec.d.ts +10 -0
- package/lib/typescript/src/presentationCodec.d.ts.map +1 -0
- package/package.json +17 -13
- package/src/NativeSmoothClipModule.ts +80 -56
- package/src/SmoothClipView.ios.tsx +142 -16
- package/src/SmoothClipView.tsx +6 -76
- package/src/SmoothClipViewNativeComponent.ts +32 -12
- package/src/capabilities.native.ts +19 -0
- package/src/capabilities.ts +2 -0
- package/src/capabilityTypes.ts +3 -0
- package/src/controllerInternals.ts +52 -0
- package/src/controllerLifecycle.ts +6 -0
- package/src/controllerTypes.ts +87 -0
- package/src/controllers.android.ts +1 -0
- package/src/controllers.ios.ts +1 -0
- package/src/controllers.native.ts +104 -0
- package/src/controllers.ts +1 -0
- package/src/geometry.ts +293 -45
- package/src/groupTypes.ts +53 -0
- package/src/groups.android.ts +1 -0
- package/src/groups.ios.ts +1 -0
- package/src/groups.native.ts +604 -0
- package/src/groups.ts +1 -0
- package/src/ids.ts +6 -0
- package/src/index.ts +32 -12
- package/src/nativeCompletion.ts +67 -32
- package/src/presentationCodec.ts +134 -0
- package/lib/module/SmoothClipView.web.js +0 -54
- package/lib/module/SmoothClipView.web.js.map +0 -1
- package/lib/module/driverState.js +0 -110
- package/lib/module/driverState.js.map +0 -1
- package/lib/module/driverTypes.js +0 -4
- package/lib/module/driverTypes.js.map +0 -1
- package/lib/module/drivers.android.js +0 -6
- package/lib/module/drivers.android.js.map +0 -1
- package/lib/module/drivers.ios.js +0 -6
- package/lib/module/drivers.ios.js.map +0 -1
- package/lib/module/drivers.js +0 -284
- package/lib/module/drivers.js.map +0 -1
- package/lib/module/drivers.native.js +0 -556
- package/lib/module/drivers.native.js.map +0 -1
- package/lib/module/index.web.js +0 -8
- package/lib/module/index.web.js.map +0 -1
- package/lib/module/reactRequests.js +0 -56
- package/lib/module/reactRequests.js.map +0 -1
- package/lib/module/webClipPath.js +0 -25
- package/lib/module/webClipPath.js.map +0 -1
- package/lib/typescript/src/SmoothClipView.web.d.ts +0 -10
- package/lib/typescript/src/SmoothClipView.web.d.ts.map +0 -1
- package/lib/typescript/src/driverState.d.ts +0 -31
- package/lib/typescript/src/driverState.d.ts.map +0 -1
- package/lib/typescript/src/driverTypes.d.ts +0 -107
- package/lib/typescript/src/driverTypes.d.ts.map +0 -1
- package/lib/typescript/src/drivers.android.d.ts +0 -3
- package/lib/typescript/src/drivers.android.d.ts.map +0 -1
- package/lib/typescript/src/drivers.d.ts +0 -5
- package/lib/typescript/src/drivers.d.ts.map +0 -1
- package/lib/typescript/src/drivers.ios.d.ts +0 -3
- package/lib/typescript/src/drivers.ios.d.ts.map +0 -1
- package/lib/typescript/src/drivers.native.d.ts +0 -5
- package/lib/typescript/src/drivers.native.d.ts.map +0 -1
- package/lib/typescript/src/index.web.d.ts +0 -9
- package/lib/typescript/src/index.web.d.ts.map +0 -1
- package/lib/typescript/src/reactRequests.d.ts +0 -9
- package/lib/typescript/src/reactRequests.d.ts.map +0 -1
- package/lib/typescript/src/webClipPath.d.ts +0 -7
- package/lib/typescript/src/webClipPath.d.ts.map +0 -1
- package/src/SmoothClipView.web.tsx +0 -66
- package/src/driverState.ts +0 -158
- package/src/driverTypes.ts +0 -140
- package/src/drivers.android.ts +0 -8
- package/src/drivers.ios.ts +0 -8
- package/src/drivers.native.ts +0 -899
- package/src/drivers.ts +0 -427
- package/src/index.web.ts +0 -23
- package/src/reactRequests.ts +0 -76
- package/src/webClipPath.ts +0 -30
package/cpp/SmoothClipRegistry.h
CHANGED
|
@@ -7,23 +7,43 @@
|
|
|
7
7
|
|
|
8
8
|
namespace smoothclip {
|
|
9
9
|
|
|
10
|
+
enum class ClipCurve : int32_t {
|
|
11
|
+
Circular = 0,
|
|
12
|
+
Continuous = 1,
|
|
13
|
+
};
|
|
14
|
+
|
|
10
15
|
struct Geometry {
|
|
11
16
|
double x;
|
|
12
17
|
double y;
|
|
13
18
|
double width;
|
|
14
19
|
double height;
|
|
15
20
|
double radius;
|
|
21
|
+
// NaN means "use radius", allowing concise uniform-radius presentations.
|
|
22
|
+
double topLeftRadius = std::numeric_limits<double>::quiet_NaN();
|
|
23
|
+
double topRightRadius = std::numeric_limits<double>::quiet_NaN();
|
|
24
|
+
double bottomRightRadius = std::numeric_limits<double>::quiet_NaN();
|
|
25
|
+
double bottomLeftRadius = std::numeric_limits<double>::quiet_NaN();
|
|
26
|
+
ClipCurve curve = ClipCurve::Circular;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
struct Shadow {
|
|
30
|
+
bool enabled = false;
|
|
31
|
+
double red = 0;
|
|
32
|
+
double green = 0;
|
|
33
|
+
double blue = 0;
|
|
34
|
+
double alpha = 1;
|
|
35
|
+
double offsetX = 0;
|
|
36
|
+
double offsetY = 0;
|
|
37
|
+
double blurRadius = 0;
|
|
38
|
+
double spreadDistance = 0;
|
|
16
39
|
};
|
|
17
40
|
|
|
18
41
|
struct Presentation {
|
|
19
42
|
Geometry clip;
|
|
20
43
|
double contentTranslateX;
|
|
21
44
|
double contentTranslateY;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
struct Keyframe {
|
|
25
|
-
double offset;
|
|
26
|
-
Presentation presentation;
|
|
45
|
+
double contentScale = 1.0;
|
|
46
|
+
Shadow shadow{};
|
|
27
47
|
};
|
|
28
48
|
|
|
29
49
|
struct TimingAnimation {
|
|
@@ -42,6 +62,7 @@ struct SpringAnimation {
|
|
|
42
62
|
double initialVelocity;
|
|
43
63
|
bool inheritVelocity;
|
|
44
64
|
int32_t reduceMotion;
|
|
65
|
+
double energyThreshold = 6e-9;
|
|
45
66
|
};
|
|
46
67
|
|
|
47
68
|
struct AnimationStart {
|
|
@@ -62,19 +83,46 @@ struct CancelResult {
|
|
|
62
83
|
Presentation presentation;
|
|
63
84
|
};
|
|
64
85
|
|
|
86
|
+
struct DriverSnapshot {
|
|
87
|
+
uint64_t driverId;
|
|
88
|
+
Presentation presentation;
|
|
89
|
+
bool ready;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
struct BatchEntry {
|
|
93
|
+
uint64_t driverId;
|
|
94
|
+
Presentation presentation;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
struct GroupMotionEntry {
|
|
98
|
+
uint64_t driverId;
|
|
99
|
+
bool hasFrom;
|
|
100
|
+
Presentation from;
|
|
101
|
+
Presentation target;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
enum class GroupCancelBehavior : int32_t {
|
|
105
|
+
Freeze = 0,
|
|
106
|
+
Finish = 1,
|
|
107
|
+
};
|
|
108
|
+
|
|
65
109
|
using CompletionCallback =
|
|
66
|
-
std::function<void(uint64_t, int32_t, bool)>;
|
|
110
|
+
std::function<void(uint64_t, int32_t, int32_t, bool)>;
|
|
111
|
+
using GroupCompletionCallback =
|
|
112
|
+
std::function<
|
|
113
|
+
void(uint64_t, int32_t, int32_t, bool, std::vector<DriverSnapshot>)>;
|
|
67
114
|
|
|
68
115
|
void setCompletionCallback(
|
|
69
116
|
const void *owner,
|
|
70
117
|
CompletionCallback callback);
|
|
71
118
|
void clearCompletionCallback(const void *owner);
|
|
119
|
+
void setGroupCompletionCallback(
|
|
120
|
+
const void *owner,
|
|
121
|
+
GroupCompletionCallback callback);
|
|
122
|
+
void clearGroupCompletionCallback(const void *owner);
|
|
72
123
|
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
// setScalars hot writes on drivers without SmoothClipDriverOptions
|
|
76
|
-
// .velocityTracking, so the per-frame drag stream skips the clock read and
|
|
77
|
-
// channel copies. iOS callers never pass it and keep recording always.
|
|
124
|
+
// Interactive writes always keep the two samples needed by a later
|
|
125
|
+
// `initialVelocity: "inherit"` spring.
|
|
78
126
|
void setPresentation(
|
|
79
127
|
uint64_t driverId,
|
|
80
128
|
Presentation presentation,
|
|
@@ -82,23 +130,39 @@ void setPresentation(
|
|
|
82
130
|
bool overridePendingAnimation = false,
|
|
83
131
|
bool recordVelocity = true);
|
|
84
132
|
Presentation beginInteraction(uint64_t driverId);
|
|
133
|
+
Presentation snapshotCurrent(uint64_t driverId);
|
|
134
|
+
std::vector<DriverSnapshot> beginGroupInteraction(
|
|
135
|
+
const std::vector<uint64_t> &driverIds);
|
|
136
|
+
std::vector<DriverSnapshot> snapshotGroup(
|
|
137
|
+
const std::vector<uint64_t> &driverIds);
|
|
138
|
+
bool setPresentationBatch(const std::vector<BatchEntry> &entries);
|
|
139
|
+
int32_t animateTimingGroup(
|
|
140
|
+
uint64_t controllerId,
|
|
141
|
+
std::vector<GroupMotionEntry> entries,
|
|
142
|
+
TimingAnimation animation,
|
|
143
|
+
int32_t completionTag = 0,
|
|
144
|
+
double startedAtHintS = std::numeric_limits<double>::quiet_NaN());
|
|
145
|
+
int32_t animateSpringGroup(
|
|
146
|
+
uint64_t controllerId,
|
|
147
|
+
std::vector<GroupMotionEntry> entries,
|
|
148
|
+
SpringAnimation animation,
|
|
149
|
+
int32_t completionTag = 0,
|
|
150
|
+
double startedAtHintS = std::numeric_limits<double>::quiet_NaN());
|
|
151
|
+
std::vector<DriverSnapshot> cancelAnimationGroup(
|
|
152
|
+
int32_t groupId,
|
|
153
|
+
GroupCancelBehavior behavior);
|
|
85
154
|
int32_t animateTiming(
|
|
86
155
|
uint64_t driverId,
|
|
87
156
|
AnimationStart start,
|
|
88
157
|
Presentation presentation,
|
|
89
|
-
TimingAnimation animation
|
|
158
|
+
TimingAnimation animation,
|
|
159
|
+
int32_t completionTag = 0);
|
|
90
160
|
int32_t animateSpring(
|
|
91
161
|
uint64_t driverId,
|
|
92
162
|
AnimationStart start,
|
|
93
163
|
Presentation presentation,
|
|
94
|
-
SpringAnimation animation
|
|
95
|
-
int32_t
|
|
96
|
-
uint64_t driverId,
|
|
97
|
-
AnimationStart start,
|
|
98
|
-
Presentation presentation,
|
|
99
|
-
double durationMs,
|
|
100
|
-
std::vector<Keyframe> keyframes,
|
|
101
|
-
int32_t reduceMotion);
|
|
164
|
+
SpringAnimation animation,
|
|
165
|
+
int32_t completionTag = 0);
|
|
102
166
|
int32_t rejectAnimation(uint64_t driverId);
|
|
103
167
|
CancelResult cancelAnimation(
|
|
104
168
|
uint64_t driverId,
|
|
@@ -1,50 +1,159 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include "SmoothClipRegistry.h"
|
|
4
|
+
|
|
3
5
|
#include <algorithm>
|
|
4
6
|
#include <cmath>
|
|
5
7
|
|
|
6
8
|
namespace smoothclip {
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Host-independent canonical clip geometry expressed as edges.
|
|
12
|
+
*
|
|
13
|
+
* The moving aperture is allowed to cross or sit outside any host. Hosts crop
|
|
14
|
+
* the rendered result; they never rewrite the geometry stored by the driver.
|
|
15
|
+
*/
|
|
16
|
+
struct CanonicalClip {
|
|
9
17
|
double left = 0;
|
|
10
18
|
double top = 0;
|
|
11
19
|
double right = 0;
|
|
12
20
|
double bottom = 0;
|
|
13
21
|
double radius = 0;
|
|
22
|
+
double topLeftRadius = 0;
|
|
23
|
+
double topRightRadius = 0;
|
|
24
|
+
double bottomRightRadius = 0;
|
|
25
|
+
double bottomLeftRadius = 0;
|
|
26
|
+
ClipCurve curve = ClipCurve::Circular;
|
|
14
27
|
};
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
29
|
+
inline double SmoothClipResolvedRadius(double overrideValue, double radius) {
|
|
30
|
+
return std::isfinite(overrideValue) ? overrideValue : radius;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Canonicalizes a requested rectangle without consulting host dimensions.
|
|
35
|
+
*
|
|
36
|
+
* Negative dimensions collapse to zero. Corner radii follow the CSS
|
|
37
|
+
* corner-overlap rule against the requested rectangle, using one common scale
|
|
38
|
+
* factor so their proportions are preserved.
|
|
39
|
+
*/
|
|
40
|
+
inline bool SmoothClipCanonicalize(
|
|
21
41
|
double x,
|
|
22
42
|
double y,
|
|
23
43
|
double width,
|
|
24
44
|
double height,
|
|
25
45
|
double radius,
|
|
26
|
-
double
|
|
27
|
-
double
|
|
28
|
-
|
|
46
|
+
double topLeftRadius,
|
|
47
|
+
double topRightRadius,
|
|
48
|
+
double bottomRightRadius,
|
|
49
|
+
double bottomLeftRadius,
|
|
50
|
+
ClipCurve curve,
|
|
51
|
+
CanonicalClip &out) {
|
|
52
|
+
const double resolvedTopLeft =
|
|
53
|
+
SmoothClipResolvedRadius(topLeftRadius, radius);
|
|
54
|
+
const double resolvedTopRight =
|
|
55
|
+
SmoothClipResolvedRadius(topRightRadius, radius);
|
|
56
|
+
const double resolvedBottomRight =
|
|
57
|
+
SmoothClipResolvedRadius(bottomRightRadius, radius);
|
|
58
|
+
const double resolvedBottomLeft =
|
|
59
|
+
SmoothClipResolvedRadius(bottomLeftRadius, radius);
|
|
29
60
|
if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(width) ||
|
|
30
61
|
!std::isfinite(height) || !std::isfinite(radius) ||
|
|
31
|
-
!std::isfinite(
|
|
62
|
+
!std::isfinite(resolvedTopLeft) ||
|
|
63
|
+
!std::isfinite(resolvedTopRight) ||
|
|
64
|
+
!std::isfinite(resolvedBottomRight) ||
|
|
65
|
+
!std::isfinite(resolvedBottomLeft) ||
|
|
66
|
+
(curve != ClipCurve::Circular && curve != ClipCurve::Continuous)) {
|
|
32
67
|
return false;
|
|
33
68
|
}
|
|
34
69
|
|
|
35
|
-
const double
|
|
36
|
-
const double
|
|
37
|
-
const double
|
|
38
|
-
const double
|
|
39
|
-
|
|
40
|
-
out.
|
|
41
|
-
out.
|
|
42
|
-
out.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
70
|
+
const double canonicalWidth = std::max(0.0, width);
|
|
71
|
+
const double canonicalHeight = std::max(0.0, height);
|
|
72
|
+
const double right = x + canonicalWidth;
|
|
73
|
+
const double bottom = y + canonicalHeight;
|
|
74
|
+
if (!std::isfinite(right) || !std::isfinite(bottom)) return false;
|
|
75
|
+
out.left = x;
|
|
76
|
+
out.top = y;
|
|
77
|
+
out.right = right;
|
|
78
|
+
out.bottom = bottom;
|
|
79
|
+
|
|
80
|
+
double topLeft = std::max(0.0, resolvedTopLeft);
|
|
81
|
+
double topRight = std::max(0.0, resolvedTopRight);
|
|
82
|
+
double bottomRight = std::max(0.0, resolvedBottomRight);
|
|
83
|
+
double bottomLeft = std::max(0.0, resolvedBottomLeft);
|
|
84
|
+
double scale = 1.0;
|
|
85
|
+
const auto includeLimit = [&scale](double available, double requested) {
|
|
86
|
+
if (requested > 0) scale = std::min(scale, available / requested);
|
|
87
|
+
};
|
|
88
|
+
includeLimit(canonicalWidth, topLeft + topRight);
|
|
89
|
+
includeLimit(canonicalWidth, bottomLeft + bottomRight);
|
|
90
|
+
includeLimit(canonicalHeight, topLeft + bottomLeft);
|
|
91
|
+
includeLimit(canonicalHeight, topRight + bottomRight);
|
|
92
|
+
scale = std::clamp(scale, 0.0, 1.0);
|
|
93
|
+
|
|
94
|
+
out.topLeftRadius = topLeft * scale;
|
|
95
|
+
out.topRightRadius = topRight * scale;
|
|
96
|
+
out.bottomRightRadius = bottomRight * scale;
|
|
97
|
+
out.bottomLeftRadius = bottomLeft * scale;
|
|
98
|
+
const bool uniform = out.topLeftRadius == out.topRightRadius &&
|
|
99
|
+
out.topLeftRadius == out.bottomRightRadius &&
|
|
100
|
+
out.topLeftRadius == out.bottomLeftRadius;
|
|
101
|
+
out.radius = uniform ? out.topLeftRadius : 0.0;
|
|
102
|
+
out.curve = curve;
|
|
47
103
|
return true;
|
|
48
104
|
}
|
|
49
105
|
|
|
106
|
+
inline bool SmoothClipCanonicalize(
|
|
107
|
+
const Geometry &geometry,
|
|
108
|
+
CanonicalClip &out) {
|
|
109
|
+
return SmoothClipCanonicalize(
|
|
110
|
+
geometry.x,
|
|
111
|
+
geometry.y,
|
|
112
|
+
geometry.width,
|
|
113
|
+
geometry.height,
|
|
114
|
+
geometry.radius,
|
|
115
|
+
geometry.topLeftRadius,
|
|
116
|
+
geometry.topRightRadius,
|
|
117
|
+
geometry.bottomRightRadius,
|
|
118
|
+
geometry.bottomLeftRadius,
|
|
119
|
+
geometry.curve,
|
|
120
|
+
out);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
inline bool SmoothClipCanonicalize(
|
|
124
|
+
double x,
|
|
125
|
+
double y,
|
|
126
|
+
double width,
|
|
127
|
+
double height,
|
|
128
|
+
double radius,
|
|
129
|
+
CanonicalClip &out) {
|
|
130
|
+
return SmoothClipCanonicalize(
|
|
131
|
+
x,
|
|
132
|
+
y,
|
|
133
|
+
width,
|
|
134
|
+
height,
|
|
135
|
+
radius,
|
|
136
|
+
radius,
|
|
137
|
+
radius,
|
|
138
|
+
radius,
|
|
139
|
+
radius,
|
|
140
|
+
ClipCurve::Circular,
|
|
141
|
+
out);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
inline Geometry SmoothClipGeometry(const CanonicalClip &clip) {
|
|
145
|
+
Geometry geometry{
|
|
146
|
+
clip.left,
|
|
147
|
+
clip.top,
|
|
148
|
+
std::max(0.0, clip.right - clip.left),
|
|
149
|
+
std::max(0.0, clip.bottom - clip.top),
|
|
150
|
+
clip.radius};
|
|
151
|
+
geometry.topLeftRadius = clip.topLeftRadius;
|
|
152
|
+
geometry.topRightRadius = clip.topRightRadius;
|
|
153
|
+
geometry.bottomRightRadius = clip.bottomRightRadius;
|
|
154
|
+
geometry.bottomLeftRadius = clip.bottomLeftRadius;
|
|
155
|
+
geometry.curve = clip.curve;
|
|
156
|
+
return geometry;
|
|
157
|
+
}
|
|
158
|
+
|
|
50
159
|
} // namespace smoothclip
|
|
@@ -6,17 +6,18 @@
|
|
|
6
6
|
namespace smoothclip {
|
|
7
7
|
|
|
8
8
|
// The two most recent interactive samples used by `initialVelocity: 'inherit'`.
|
|
9
|
-
// Channels are the
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
9
|
+
// Channels are the eleven geometry/content presentation scalars. Scale is stored
|
|
10
|
+
// so histories remain coherent but excluded from velocity projection; each of
|
|
11
|
+
// the four radius channels contributes one quarter of the uniform-radius weight.
|
|
12
|
+
// Both platforms record raw canonical geometry, including off-host coordinates.
|
|
13
|
+
// The projection only needs both samples and the target in the same space.
|
|
13
14
|
// Shared by ios/SmoothClipView.mm and android/.../SmoothClipRegistry.cpp; a
|
|
14
15
|
// behavior change here changes both platforms.
|
|
15
16
|
struct VelocitySampleHistory {
|
|
16
17
|
bool hasPrevious = false;
|
|
17
18
|
bool hasLatest = false;
|
|
18
|
-
std::array<double,
|
|
19
|
-
std::array<double,
|
|
19
|
+
std::array<double, 11> previous{};
|
|
20
|
+
std::array<double, 11> latest{};
|
|
20
21
|
double previousTimeS = 0;
|
|
21
22
|
double latestTimeS = 0;
|
|
22
23
|
};
|
|
@@ -40,7 +41,7 @@ inline constexpr double kVelocityFullCreditS = 1.0 / 60.0;
|
|
|
40
41
|
|
|
41
42
|
inline void recordVelocitySample(
|
|
42
43
|
VelocitySampleHistory &history,
|
|
43
|
-
const std::array<double,
|
|
44
|
+
const std::array<double, 11> &channels,
|
|
44
45
|
double nowS) {
|
|
45
46
|
if (history.hasLatest && channels == history.latest) {
|
|
46
47
|
// Identical re-record (a `from` seed equal to the last interactive
|
|
@@ -68,7 +69,7 @@ inline void recordVelocitySample(
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
// Only explicit interactive writes route through this recorder. Native
|
|
71
|
-
// freeze
|
|
72
|
+
// Native freeze and endpoint-finalization paths deliberately do not, so an
|
|
72
73
|
// animation cannot manufacture a later inherited velocity. During ordinary
|
|
73
74
|
// interaction, the staleness decay below is the forgetting mechanism and it
|
|
74
75
|
// forgets gradually rather than all at once.
|
|
@@ -97,7 +98,7 @@ inline double velocityStalenessCredit(double holdS) {
|
|
|
97
98
|
// Android seeds each integrator channel with scalar·displacement.
|
|
98
99
|
inline double inheritedVelocity(
|
|
99
100
|
const VelocitySampleHistory &history,
|
|
100
|
-
const std::array<double,
|
|
101
|
+
const std::array<double, 11> &target,
|
|
101
102
|
double nowS) {
|
|
102
103
|
if (!history.hasPrevious || !history.hasLatest) return 0;
|
|
103
104
|
const double elapsed = history.latestTimeS - history.previousTimeS;
|
|
@@ -107,11 +108,14 @@ inline double inheritedVelocity(
|
|
|
107
108
|
}
|
|
108
109
|
double numerator = 0;
|
|
109
110
|
double denominator = 0;
|
|
110
|
-
for (int index = 0; index <
|
|
111
|
+
for (int index = 0; index < 11; index += 1) {
|
|
112
|
+
const double weight = index >= 4 && index <= 7
|
|
113
|
+
? 0.25
|
|
114
|
+
: (index == 10 ? 0.0 : 1.0);
|
|
111
115
|
const double sampleDelta = history.latest[index] - history.previous[index];
|
|
112
116
|
const double destinationDelta = target[index] - history.latest[index];
|
|
113
|
-
numerator += sampleDelta * destinationDelta;
|
|
114
|
-
denominator += destinationDelta * destinationDelta;
|
|
117
|
+
numerator += weight * sampleDelta * destinationDelta;
|
|
118
|
+
denominator += weight * destinationDelta * destinationDelta;
|
|
115
119
|
}
|
|
116
120
|
if (denominator <= 1e-12) return 0;
|
|
117
121
|
const double result = numerator / elapsed / denominator * credit;
|
package/ios/SmoothClipGeometry.h
CHANGED
|
@@ -2,40 +2,207 @@
|
|
|
2
2
|
|
|
3
3
|
#include <math.h>
|
|
4
4
|
|
|
5
|
+
typedef struct {
|
|
6
|
+
CGFloat topLeft;
|
|
7
|
+
CGFloat topRight;
|
|
8
|
+
CGFloat bottomRight;
|
|
9
|
+
CGFloat bottomLeft;
|
|
10
|
+
} SmoothClipCornerRadii;
|
|
11
|
+
|
|
12
|
+
typedef NS_ENUM(NSInteger, SmoothClipCornerCurve) {
|
|
13
|
+
SmoothClipCornerCurveCircular = 0,
|
|
14
|
+
SmoothClipCornerCurveContinuous = 1,
|
|
15
|
+
};
|
|
16
|
+
|
|
5
17
|
typedef struct {
|
|
6
18
|
CGRect rect;
|
|
19
|
+
// Uniform-radius fast-path value; zero when canonical radii are unequal.
|
|
7
20
|
CGFloat radius;
|
|
8
|
-
|
|
21
|
+
SmoothClipCornerRadii radii;
|
|
22
|
+
SmoothClipCornerCurve curve;
|
|
23
|
+
} SmoothClipCanonicalGeometry;
|
|
9
24
|
|
|
10
|
-
|
|
11
|
-
|
|
25
|
+
NS_INLINE BOOL SmoothClipCornerRadiiEqual(
|
|
26
|
+
SmoothClipCornerRadii first,
|
|
27
|
+
SmoothClipCornerRadii second) {
|
|
28
|
+
return first.topLeft == second.topLeft &&
|
|
29
|
+
first.topRight == second.topRight &&
|
|
30
|
+
first.bottomRight == second.bottomRight &&
|
|
31
|
+
first.bottomLeft == second.bottomLeft;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
NS_INLINE BOOL SmoothClipCornerRadiiAreUniform(
|
|
35
|
+
SmoothClipCornerRadii radii) {
|
|
36
|
+
return radii.topLeft == radii.topRight &&
|
|
37
|
+
radii.topLeft == radii.bottomRight &&
|
|
38
|
+
radii.topLeft == radii.bottomLeft;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
NS_INLINE CGFloat SmoothClipSafeOverlapFactor(
|
|
42
|
+
CGFloat extent,
|
|
43
|
+
CGFloat firstRadius,
|
|
44
|
+
CGFloat secondRadius) {
|
|
45
|
+
const CGFloat sum = firstRadius + secondRadius;
|
|
46
|
+
return sum > 0 ? extent / sum : 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Canonicalizes the rectangle and four radii without allocating.
|
|
51
|
+
*
|
|
52
|
+
* The proportional overlap rule matches CSS border radii and the shared
|
|
53
|
+
* Android/C++ contract: all corners use one scale factor, preserving their
|
|
54
|
+
* relative shape while no opposing pair exceeds its edge.
|
|
55
|
+
*/
|
|
56
|
+
NS_INLINE BOOL SmoothClipCanonicalizeGeometry(
|
|
12
57
|
CGFloat x,
|
|
13
58
|
CGFloat y,
|
|
14
59
|
CGFloat width,
|
|
15
60
|
CGFloat height,
|
|
16
|
-
CGFloat
|
|
17
|
-
|
|
18
|
-
|
|
61
|
+
CGFloat topLeftRadius,
|
|
62
|
+
CGFloat topRightRadius,
|
|
63
|
+
CGFloat bottomRightRadius,
|
|
64
|
+
CGFloat bottomLeftRadius,
|
|
65
|
+
NSInteger curveCode,
|
|
66
|
+
SmoothClipCanonicalGeometry *result) {
|
|
19
67
|
if (!isfinite(x) || !isfinite(y) || !isfinite(width) ||
|
|
20
|
-
!isfinite(height) || !isfinite(
|
|
21
|
-
!isfinite(
|
|
68
|
+
!isfinite(height) || !isfinite(topLeftRadius) ||
|
|
69
|
+
!isfinite(topRightRadius) || !isfinite(bottomRightRadius) ||
|
|
70
|
+
!isfinite(bottomLeftRadius) ||
|
|
71
|
+
(curveCode != SmoothClipCornerCurveCircular &&
|
|
72
|
+
curveCode != SmoothClipCornerCurveContinuous)) {
|
|
22
73
|
return NO;
|
|
23
74
|
}
|
|
24
75
|
|
|
25
|
-
const CGFloat hostWidth = MAX(0, hostSize.width);
|
|
26
|
-
const CGFloat hostHeight = MAX(0, hostSize.height);
|
|
27
76
|
const CGFloat requestedWidth = MAX(0, width);
|
|
28
77
|
const CGFloat requestedHeight = MAX(0, height);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
78
|
+
if (!isfinite(x + requestedWidth) || !isfinite(y + requestedHeight)) {
|
|
79
|
+
return NO;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
SmoothClipCornerRadii radii = {
|
|
83
|
+
MAX(0, topLeftRadius),
|
|
84
|
+
MAX(0, topRightRadius),
|
|
85
|
+
MAX(0, bottomRightRadius),
|
|
86
|
+
MAX(0, bottomLeftRadius),
|
|
87
|
+
};
|
|
88
|
+
const CGFloat overlapScale = MIN(
|
|
89
|
+
1,
|
|
90
|
+
MIN(
|
|
91
|
+
MIN(
|
|
92
|
+
SmoothClipSafeOverlapFactor(
|
|
93
|
+
requestedWidth, radii.topLeft, radii.topRight),
|
|
94
|
+
SmoothClipSafeOverlapFactor(
|
|
95
|
+
requestedWidth, radii.bottomLeft, radii.bottomRight)),
|
|
96
|
+
MIN(
|
|
97
|
+
SmoothClipSafeOverlapFactor(
|
|
98
|
+
requestedHeight, radii.topLeft, radii.bottomLeft),
|
|
99
|
+
SmoothClipSafeOverlapFactor(
|
|
100
|
+
requestedHeight, radii.topRight, radii.bottomRight))));
|
|
101
|
+
radii.topLeft *= overlapScale;
|
|
102
|
+
radii.topRight *= overlapScale;
|
|
103
|
+
radii.bottomRight *= overlapScale;
|
|
104
|
+
radii.bottomLeft *= overlapScale;
|
|
105
|
+
|
|
106
|
+
result->rect = CGRectMake(x, y, requestedWidth, requestedHeight);
|
|
107
|
+
result->radii = radii;
|
|
108
|
+
result->radius = SmoothClipCornerRadiiAreUniform(radii)
|
|
109
|
+
? radii.topLeft
|
|
110
|
+
: 0;
|
|
111
|
+
result->curve = (SmoothClipCornerCurve)curveCode;
|
|
40
112
|
return YES;
|
|
41
113
|
}
|
|
114
|
+
|
|
115
|
+
/** Mirrors the Android and JavaScript geometry contract without allocating. */
|
|
116
|
+
NS_INLINE BOOL SmoothClipCanonicalizeGeometry(
|
|
117
|
+
CGFloat x,
|
|
118
|
+
CGFloat y,
|
|
119
|
+
CGFloat width,
|
|
120
|
+
CGFloat height,
|
|
121
|
+
CGFloat radius,
|
|
122
|
+
SmoothClipCanonicalGeometry *result) {
|
|
123
|
+
return SmoothClipCanonicalizeGeometry(
|
|
124
|
+
x,
|
|
125
|
+
y,
|
|
126
|
+
width,
|
|
127
|
+
height,
|
|
128
|
+
radius,
|
|
129
|
+
radius,
|
|
130
|
+
radius,
|
|
131
|
+
radius,
|
|
132
|
+
SmoothClipCornerCurveCircular,
|
|
133
|
+
result);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Creates an empty path for an empty clip. Non-empty rounded rectangles use a
|
|
138
|
+
* stable nine-segment topology. Every corner is one cubic, including a
|
|
139
|
+
* zero-radius corner, so Core Animation can interpolate non-empty paths
|
|
140
|
+
* without a topology change.
|
|
141
|
+
*/
|
|
142
|
+
NS_INLINE CGPathRef SmoothClipCreateRoundedRectPath(
|
|
143
|
+
CGRect rect,
|
|
144
|
+
SmoothClipCornerRadii radii,
|
|
145
|
+
SmoothClipCornerCurve curve) CF_RETURNS_RETAINED {
|
|
146
|
+
CGMutablePathRef path = CGPathCreateMutable();
|
|
147
|
+
if (CGRectIsEmpty(rect)) return path;
|
|
148
|
+
const CGFloat minX = CGRectGetMinX(rect);
|
|
149
|
+
const CGFloat minY = CGRectGetMinY(rect);
|
|
150
|
+
const CGFloat maxX = CGRectGetMaxX(rect);
|
|
151
|
+
const CGFloat maxY = CGRectGetMaxY(rect);
|
|
152
|
+
// Circular is the exact cubic-circle coefficient. The portable continuous
|
|
153
|
+
// fallback uses kappa=1 so each cubic joins its straight edges with zero
|
|
154
|
+
// curvature, matching the portable Android path. Hit testing derives
|
|
155
|
+
// from this rendered path rather than maintaining a second curve model.
|
|
156
|
+
// Uniform
|
|
157
|
+
// continuous shapes use CALayer.cornerCurve for Apple's platform-native
|
|
158
|
+
// compositor shape and are intentionally not pixel-identical cross-platform.
|
|
159
|
+
const CGFloat controlFactor =
|
|
160
|
+
curve == SmoothClipCornerCurveContinuous
|
|
161
|
+
? 1.0
|
|
162
|
+
: 0.5522847498307936;
|
|
163
|
+
|
|
164
|
+
CGPathMoveToPoint(path, NULL, minX + radii.topLeft, minY);
|
|
165
|
+
CGPathAddLineToPoint(path, NULL, maxX - radii.topRight, minY);
|
|
166
|
+
CGPathAddCurveToPoint(
|
|
167
|
+
path,
|
|
168
|
+
NULL,
|
|
169
|
+
maxX - radii.topRight + radii.topRight * controlFactor,
|
|
170
|
+
minY,
|
|
171
|
+
maxX,
|
|
172
|
+
minY + radii.topRight - radii.topRight * controlFactor,
|
|
173
|
+
maxX,
|
|
174
|
+
minY + radii.topRight);
|
|
175
|
+
CGPathAddLineToPoint(path, NULL, maxX, maxY - radii.bottomRight);
|
|
176
|
+
CGPathAddCurveToPoint(
|
|
177
|
+
path,
|
|
178
|
+
NULL,
|
|
179
|
+
maxX,
|
|
180
|
+
maxY - radii.bottomRight + radii.bottomRight * controlFactor,
|
|
181
|
+
maxX - radii.bottomRight +
|
|
182
|
+
radii.bottomRight * controlFactor,
|
|
183
|
+
maxY,
|
|
184
|
+
maxX - radii.bottomRight,
|
|
185
|
+
maxY);
|
|
186
|
+
CGPathAddLineToPoint(path, NULL, minX + radii.bottomLeft, maxY);
|
|
187
|
+
CGPathAddCurveToPoint(
|
|
188
|
+
path,
|
|
189
|
+
NULL,
|
|
190
|
+
minX + radii.bottomLeft - radii.bottomLeft * controlFactor,
|
|
191
|
+
maxY,
|
|
192
|
+
minX,
|
|
193
|
+
maxY - radii.bottomLeft + radii.bottomLeft * controlFactor,
|
|
194
|
+
minX,
|
|
195
|
+
maxY - radii.bottomLeft);
|
|
196
|
+
CGPathAddLineToPoint(path, NULL, minX, minY + radii.topLeft);
|
|
197
|
+
CGPathAddCurveToPoint(
|
|
198
|
+
path,
|
|
199
|
+
NULL,
|
|
200
|
+
minX,
|
|
201
|
+
minY + radii.topLeft - radii.topLeft * controlFactor,
|
|
202
|
+
minX + radii.topLeft - radii.topLeft * controlFactor,
|
|
203
|
+
minY,
|
|
204
|
+
minX + radii.topLeft,
|
|
205
|
+
minY);
|
|
206
|
+
CGPathCloseSubpath(path);
|
|
207
|
+
return path;
|
|
208
|
+
}
|