# Code doesn't animate as expected

Hi, I a have some code but doesn’t do what I expect it to do. The code is supposed to animate different shapes into each other. Here is my code:
(FYI: I describe each shape with curves going from the leading to the trailing of the frame. Then the code automatically closes the path.)

``````struct Curve {
var destination: CGPoint
var control1: CGPoint
var control2: CGPoint
}

/// A landscape shape defined by a start y-coordinate and an array of curves describing the surface of the landscape
struct AnimatableLandscape: View {

var startY: Double
var curves: [Curve]

init(startY: Double, curves: [Curve]) {
self.startY = startY

// Add lines to close the path of the shape
var newCurves = curves
// If the last destination isn't at the bottom of the frame, draw a line straight down
if let last = newCurves.last, last.destination.y != 1 {
newCurves.append(Curve(destination: CGPoint(x: last.destination.x, y: 1), control1: CGPoint(x: last.destination.x, y: (1+last.destination.y)/2), control2: CGPoint(x: last.destination.x, y: (1+last.destination.y)/2)))
}
// Draw a line to the bottom-leftcorner
newCurves.append(Curve(destination: CGPoint(x: 0, y: 1), control1: CGPoint(x: 0.5, y: 1), control2: CGPoint(x: 0.5, y: 1)))
// If needed, draw a straight line to the start y-coordinate of the first curve
if startY != 1 {
newCurves.append(Curve(destination: CGPoint(x: 0, y: startY), control1: CGPoint(x: 0, y: (1+startY)/2), control2: CGPoint(x: 0, y: (1+startY)/2)))
}
self.curves = newCurves
}

var body: some View {
AnimatableBezier(
yStartPoint: startY,
xDestinations: curves.map {\$0.destination.x},
yDestinations: curves.map {\$0.destination.y},
xControl1: curves.map {\$0.control1.x},
yControl1: curves.map {\$0.control1.y},
xControl2: curves.map {\$0.control2.x},
yControl2: curves.map {\$0.control2.y}
)
}
}
``````
``````/// An animatable bezier shape which is defined by a list of destinations and their control points with their x- and y-coordinates passed through in seperate arrays
struct AnimatableBezier: Shape {

// Properties
// Define only the y-coordinate of the start point as the bezier should always start at x zero
var yStartPoint: Double

// Define the x- and y-coordinates as different properties to calculate these values seperately using AnimatableValues
var xDestinations: AnimatableValues
var yDestinations: AnimatableValues

var xControl1: AnimatableValues
var yControl1: AnimatableValues

var xControl2: AnimatableValues
var yControl2: AnimatableValues

init(yStartPoint: Double, xDestinations: [Double], yDestinations: [Double], xControl1: [Double], yControl1: [Double], xControl2: [Double], yControl2: [Double]) {
self.yStartPoint = yStartPoint
self.xDestinations = AnimatableValues(xDestinations)
self.yDestinations = AnimatableValues(yDestinations)
self.xControl1 = AnimatableValues(xControl1)
self.yControl1 = AnimatableValues(yControl1)
self.xControl2 = AnimatableValues(xControl2)
self.yControl2 = AnimatableValues(yControl2)
}

// Declare the animatable data
typealias Points = AnimatablePair<AnimatableValues, AnimatableValues>
typealias Curves = AnimatablePair<Points, AnimatablePair<Points, Points>>

var animatableData: AnimatablePair<Double, Curves> {
get {
let destinationPoints = Points(xDestinations, yDestinations)
let control1Points = Points(xControl1, yControl1)
let control2Points = Points(xControl2, yControl2)
let curves = Curves(destinationPoints, AnimatablePair(control1Points, control2Points))

return AnimatablePair(yStartPoint, curves)
}

set {
yStartPoint = newValue.first

let curves: Curves = newValue.second
let destinationPoints: Points = curves.first
xDestinations = destinationPoints.first
yDestinations = destinationPoints.second

let control1Points: Points = curves.second.first
xControl1 = control1Points.first
yControl1 = control1Points.second

let control2Points: Points = curves.second.second
xControl2 = control2Points.first
yControl2 = control2Points.second
}
}

func path(in rect: CGRect) -> Path {
var path = Path()
let width = rect.size.width
let height = rect.size.height

// Move the the start point
let startPoint = CGPoint(x: 0, y: yStartPoint*height)
path.move(to: startPoint)

// Draw the curves
let count = min(xDestinations.values.count, yDestinations.values.count, xControl1.values.count, yControl1.values.count, xControl2.values.count, yControl2.values.count)
for i in 0..<count {
let destination = CGPoint(x: xDestinations.values[i], y: yDestinations.values[i])
let control1 = CGPoint(x: xControl1.values[i], y: yControl1.values[i])
let control2 = CGPoint(x: xControl2.values[i], y: yControl2.values[i])

control1: CGPoint(x: control1.x*width, y: control1.y*height),
control2: CGPoint(x: control2.x*width, y: control2.y*height))
}

// Close the path
path.closeSubpath()

return path
}
}
``````
``````/// Uses the Accelerate framework to utilize its high-performance vector-based arithmetic for animating large arrays.
import enum Accelerate.vDSP

/// Holds an array of Doubles anc conforms to VectorArithmetic allowing it to be used as AnimatableValue
struct AnimatableValues: VectorArithmetic {
init(_ values: [Double]) {
self.values = values
}

var values: [Double]

static var zero: AnimatableValues = AnimatableValues([0.0])

var magnitudeSquared: Double {
vDSP.sum(vDSP.multiply(values, values))
}

static func + (lhs: AnimatableValues, rhs: AnimatableValues) -> AnimatableValues {
let count: Int = min(lhs.values.count, rhs.values.count)
}

static func += (lhs: inout AnimatableValues, rhs: AnimatableValues) {
let count: Int = min(lhs.values.count, rhs.values.count)
}

static func - (lhs: AnimatableValues, rhs: AnimatableValues) -> AnimatableValues {
let count: Int = min(lhs.values.count, rhs.values.count)
return AnimatableValues(vDSP.subtract(lhs.values[0..<count], rhs.values[0..<count]))
}

static func -= (lhs: inout AnimatableValues, rhs: AnimatableValues) {
let count: Int = min(lhs.values.count, rhs.values.count)
vDSP.subtract(lhs.values[0..<count], rhs.values[0..<count], result: &lhs.values[0..<count])
}

mutating func scale(by rhs: Double) {
values = vDSP.multiply(rhs, values)
}
}
``````

When I use this code to show a shape without animating it, it shows as expected, but when I change a shape into another shape, the other shape doesn’t look like it normally does when not animated.

Shape 1:

``````let firstStartY = 0.90164
let firstCurves = [
Curve(destination: CGPoint(x: 0.48065, y: 0.53167),
control1: CGPoint(x: 0.09511, y: 0.67288),
control2: CGPoint(x: 0.25329, y: 0.48162)),
Curve(destination: CGPoint(x: 0.92773, y: 0.13855),
control1: CGPoint(x: 0.79214, y: 0.60023),
control2: CGPoint(x: 0.87503, y: 0.318)),
Curve(destination: CGPoint(x: 1, y: 0.0002),
control1: CGPoint(x: 0.95181, y: 0.05655),
control2: CGPoint(x: 0.9696, y: -0.00398))
]
``````

Shape 2:

``````let secondStartY = 0.03728
let secondCurves = [
Curve(destination: CGPoint(x: 0.69054, y: 0.20178),
control1: CGPoint(x: 0, y: 0.03728),
control2: CGPoint(x: 0.45736, y: -0.11688)),
Curve(destination: CGPoint(x: 1, y: 1),
control1: CGPoint(x: 0.82381, y: 0.38388),
control2: CGPoint(x: 0.94161, y: 0.78003))
]
``````

Animating Shape 1 into Shape 2:
(See file”Animating Shape1 into Shape2.mov” for visual):

``````struct ContentView: View {

@State var startPointY = 0.0
@State var curves = [Curve]()

var body: some View {

AnimatableLandscape(startY: startPointY, curves: curves)
.frame(height: geo.size.width * 0.5)
}
.onAppear {
self.startPointY = firstStartY
self.curves = firstCurves
}
.onTapGesture {
withAnimation {
if startPointY == firstStartY {
self.startPointY = secondStartY
self.curves = secondCurves
} else {
self.startPointY = firstStartY
self.curves = firstCurves
}
}
}

}
}
``````

As you can see, when I animate shape 1 into shape 2, the result doesn’t look like the original shape 2 at all. I can’t figure out why this happens, if anyone could help me with this that’d be great!