Skip to content

Commit 23e6e60

Browse files
author
Christoph Pader
committed
fix: getYForX
1 parent d9461e5 commit 23e6e60

File tree

2 files changed

+79
-8
lines changed

2 files changed

+79
-8
lines changed

src/AnimatedLineGraph.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ export function AnimatedLineGraph({
170170
() => Math.floor(lineWidth) + horizontalPadding
171171
)
172172
const indicatorY = useDerivedValue(
173-
() => getYForX(commands.value, indicatorX.value) || 0
173+
() => getYForX(commands.value, indicatorX.value, disableSmoothing) || 0
174174
)
175175

176176
const indicatorPulseColor = useMemo(() => hexToRgba(color, 0.4), [color])
@@ -372,7 +372,7 @@ export function AnimatedLineGraph({
372372
(fingerX: number) => {
373373
'worklet'
374374

375-
const y = getYForX(commands.value, fingerX)
375+
const y = getYForX(commands.value, fingerX, disableSmoothing)
376376

377377
if (y != null) {
378378
circleX.value = fingerX

src/GetYForX.ts

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { Vector, PathCommand } from '@shopify/react-native-skia'
22
import { PathVerb, vec } from '@shopify/react-native-skia'
33

4-
// code from William Candillon
4+
const GET_Y_FOR_X_PRECISION = 2
55

6+
// code from William Candillon
67
const round = (value: number, precision = 0): number => {
78
'worklet'
89

@@ -156,15 +157,85 @@ export const selectCurve = (
156157
return undefined
157158
}
158159

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+
159219
export const getYForX = (
160220
cmds: PathCommand[],
161221
x: number,
162-
precision = 2
222+
disableSmoothing: boolean
163223
): number | undefined => {
164224
'worklet'
165225

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+
}
170241
}

0 commit comments

Comments
 (0)