( node: AstNode | undefined, scope: ScopeBindings, )
| 842 | } |
| 843 | |
| 844 | function parseMotionPathNode( |
| 845 | node: AstNode | undefined, |
| 846 | scope: ScopeBindings, |
| 847 | ): MotionPathParseResult | undefined { |
| 848 | if (!node) return undefined; |
| 849 | |
| 850 | let pathNode: AstNode | undefined; |
| 851 | let autoRotate: boolean | number = false; |
| 852 | let curviness = 1; |
| 853 | let isCubic = false; |
| 854 | |
| 855 | if (node.type === "ObjectExpression") { |
| 856 | for (const prop of node.properties ?? []) { |
| 857 | if (!isObjectProperty(prop)) continue; |
| 858 | const key = propKeyName(prop); |
| 859 | if (key === "path") pathNode = prop.value; |
| 860 | else if (key === "autoRotate") { |
| 861 | const val = resolveNode(prop.value, scope); |
| 862 | autoRotate = typeof val === "number" ? val : val === true; |
| 863 | } else if (key === "curviness") { |
| 864 | const val = resolveNode(prop.value, scope); |
| 865 | if (typeof val === "number") curviness = val; |
| 866 | } else if (key === "type") { |
| 867 | const val = resolveNode(prop.value, scope); |
| 868 | if (val === "cubic") isCubic = true; |
| 869 | } |
| 870 | } |
| 871 | } else if (node.type === "ArrayExpression") { |
| 872 | pathNode = node; |
| 873 | } |
| 874 | |
| 875 | if (!pathNode || pathNode.type !== "ArrayExpression") return undefined; |
| 876 | |
| 877 | const elements = pathNode.elements ?? []; |
| 878 | const coords: Array<{ x: number; y: number }> = []; |
| 879 | for (const elem of elements) { |
| 880 | if (!elem || elem.type !== "ObjectExpression") continue; |
| 881 | const rec = objectExpressionToRecord(elem, scope); |
| 882 | const x = typeof rec.x === "number" ? rec.x : undefined; |
| 883 | const y = typeof rec.y === "number" ? rec.y : undefined; |
| 884 | if (x !== undefined && y !== undefined) coords.push({ x, y }); |
| 885 | } |
| 886 | |
| 887 | if (coords.length < 2) return undefined; |
| 888 | |
| 889 | let waypoints: Array<{ x: number; y: number }>; |
| 890 | const segments: ArcPathSegment[] = []; |
| 891 | |
| 892 | if (isCubic && coords.length >= 4) { |
| 893 | // type: "cubic" — coords are [anchor, cp1, cp2, anchor, cp1, cp2, anchor, ...] |
| 894 | // Every 3rd coord starting from 0 is an anchor, the two between are control points. |
| 895 | waypoints = []; |
| 896 | waypoints.push(coords[0]!); |
| 897 | for (let i = 1; i + 2 < coords.length; i += 3) { |
| 898 | const cp1 = coords[i]!; |
| 899 | const cp2 = coords[i + 1]!; |
| 900 | const anchor = coords[i + 2]!; |
| 901 | waypoints.push(anchor); |
no test coverage detected