|
1 | 1 | import type { Vector, PathCommand } from '@shopify/react-native-skia'
|
2 | 2 | import { PathVerb, vec } from '@shopify/react-native-skia'
|
3 | 3 |
|
4 |
| -// code from William Candillon |
| 4 | +const GET_Y_FOR_X_PRECISION = 2 |
5 | 5 |
|
| 6 | +// code from William Candillon |
6 | 7 | const round = (value: number, precision = 0): number => {
|
7 | 8 | 'worklet'
|
8 | 9 |
|
@@ -156,15 +157,85 @@ export const selectCurve = (
|
156 | 157 | return undefined
|
157 | 158 | }
|
158 | 159 |
|
| 160 | +const linearInterpolation = (x: number, from: Vector, to: Vector): number => { |
| 161 | + 'worklet' |
| 162 | + if (from.x === to.x) return from.y |
| 163 | + return from.y + ((to.y - from.y) * (x - from.x)) / (to.x - from.x) |
| 164 | +} |
| 165 | + |
| 166 | +export const selectSegment = ( |
| 167 | + cmds: PathCommand[], |
| 168 | + x: number, |
| 169 | + disableSmoothing: boolean |
| 170 | +): Cubic | { from: Vector; to: Vector } | undefined => { |
| 171 | + 'worklet' |
| 172 | + |
| 173 | + let from: Vector = vec(0, 0) |
| 174 | + for (let i = 0; i < cmds.length; i++) { |
| 175 | + const cmd = cmds[i] |
| 176 | + if (cmd == null) continue |
| 177 | + |
| 178 | + switch (cmd[0]) { |
| 179 | + case PathVerb.Move: |
| 180 | + from = vec(cmd[1], cmd[2]) |
| 181 | + break |
| 182 | + case PathVerb.Line: |
| 183 | + const lineTo = vec(cmd[1], cmd[2]) |
| 184 | + if ( |
| 185 | + x >= Math.min(from.x, lineTo.x) && |
| 186 | + x <= Math.max(from.x, lineTo.x) |
| 187 | + ) { |
| 188 | + return { from, to: lineTo } |
| 189 | + } |
| 190 | + from = lineTo |
| 191 | + break |
| 192 | + case PathVerb.Cubic: |
| 193 | + const cubicTo = vec(cmd[5], cmd[6]) |
| 194 | + if (disableSmoothing) { |
| 195 | + if ( |
| 196 | + x >= Math.min(from.x, cubicTo.x) && |
| 197 | + x <= Math.max(from.x, cubicTo.x) |
| 198 | + ) { |
| 199 | + return { from, to: cubicTo } |
| 200 | + } |
| 201 | + } else { |
| 202 | + const c1 = vec(cmd[1], cmd[2]) |
| 203 | + const c2 = vec(cmd[3], cmd[4]) |
| 204 | + if ( |
| 205 | + x >= Math.min(from.x, cubicTo.x) && |
| 206 | + x <= Math.max(from.x, cubicTo.x) |
| 207 | + ) { |
| 208 | + return { from, c1, c2, to: cubicTo } |
| 209 | + } |
| 210 | + } |
| 211 | + from = cubicTo |
| 212 | + break |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | + return undefined |
| 217 | +} |
| 218 | + |
159 | 219 | export const getYForX = (
|
160 | 220 | cmds: PathCommand[],
|
161 | 221 | x: number,
|
162 |
| - precision = 2 |
| 222 | + disableSmoothing: boolean |
163 | 223 | ): number | undefined => {
|
164 | 224 | 'worklet'
|
165 | 225 |
|
166 |
| - const c = selectCurve(cmds, x) |
167 |
| - if (c == null) return undefined |
168 |
| - |
169 |
| - return cubicBezierYForX(x, c.from, c.c1, c.c2, c.to, precision) |
| 226 | + const segment = selectSegment(cmds, x, disableSmoothing) |
| 227 | + if (!segment) return undefined |
| 228 | + |
| 229 | + if ('c1' in segment) { |
| 230 | + return cubicBezierYForX( |
| 231 | + x, |
| 232 | + segment.from, |
| 233 | + segment.c1, |
| 234 | + segment.c2, |
| 235 | + segment.to, |
| 236 | + GET_Y_FOR_X_PRECISION |
| 237 | + ) |
| 238 | + } else { |
| 239 | + return linearInterpolation(x, segment.from, segment.to) |
| 240 | + } |
170 | 241 | }
|
0 commit comments