/* * Copyright 2016-present The Material Motion Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.material.motion.sources; import android.support.v4.util.SimpleArrayMap; import com.google.android.indefinite.observable.IndefiniteObservable.Subscription; import com.google.android.indefinite.observable.Observer; import com.google.android.material.motion.MotionObserver; import com.google.android.material.motion.MotionObserver.SimpleMotionObserver; import com.google.android.material.motion.MotionState; import com.google.android.material.motion.interactions.MaterialSpring; import com.google.android.material.motion.physics.Integrator; import com.google.android.material.motion.physics.Integrator.Listener; import com.google.android.material.motion.physics.Integrator.SimpleListener; import com.google.android.material.motion.physics.forces.Spring; import com.google.android.material.motion.physics.integrators.Rk4Integrator; import com.google.android.material.motion.physics.math.Vector; /** * A source for physics springs. */ public final class PhysicsSpringSource<T> extends SpringSource<T> { public static final System SYSTEM = new System() { @Override public <T> SpringSource<T> create(MaterialSpring<?, T> spring) { return new PhysicsSpringSource<>(spring); } }; private final MaterialSpring<?, T> interaction; private final Integrator integrator; private final Spring springForce; private final SimpleArrayMap<Observer<T>, Listener> integratorListeners = new SimpleArrayMap<>(); private Subscription destinationSubscription; private Subscription frictionSubscription; private Subscription tensionSubscription; public PhysicsSpringSource(MaterialSpring<?, T> interaction) { super(interaction); this.interaction = interaction; integrator = new Rk4Integrator(); springForce = new Spring(); integrator.addForce(springForce); integrator.addListener(new SimpleListener() { @Override public void onStart() { interaction.state.write(MotionState.ACTIVE); for (int i = 0, count = integratorListeners.size(); i < count; i++) { integratorListeners.valueAt(i).onStart(); } } @Override public void onUpdate(Vector x, Vector v) { for (int i = 0, count = integratorListeners.size(); i < count; i++) { integratorListeners.valueAt(i).onUpdate(x, v); } } @Override public void onStop() { interaction.state.write(MotionState.AT_REST); for (int i = 0, count = integratorListeners.size(); i < count; i++) { integratorListeners.valueAt(i).onStop(); } } }); } @Override protected void onConnect(final MotionObserver<T> observer) { integratorListeners.put(observer, new SimpleListener() { @Override public void onUpdate(Vector x, Vector v) { T value = interaction.vectorizer.compose(x.getValues()); observer.next(value); } }); } @Override protected void onEnable() { tensionSubscription = interaction.tension.subscribe(new SimpleMotionObserver<Float>() { @Override public void next(Float value) { springForce.k = Spring.tensionFromOrigamiValue(value); integrator.start(); } }); frictionSubscription = interaction.friction.subscribe(new SimpleMotionObserver<Float>() { @Override public void next(Float value) { springForce.b = Spring.frictionFromOrigamiValue(value); integrator.start(); } }); final int count = interaction.vectorizer.getVectorLength(); float[] initialValues = new float[count]; interaction.vectorizer.vectorize(interaction.initialValue.read(), initialValues); float[] initialVelocities = new float[count]; interaction.vectorizer.vectorize(interaction.initialVelocity.read(), initialVelocities); for (int i = 0; i < count; i++) { integrator.setState(new Vector(initialValues), new Vector(initialVelocities)); } final float[] endValues = new float[count]; destinationSubscription = interaction.destination.subscribe(new SimpleMotionObserver<T>() { @Override public void next(T value) { interaction.vectorizer.vectorize(value, endValues); springForce.setAnchorPoint(new Vector(endValues)); integrator.start(); } }); integrator.start(); } @Override protected void onDisable() { integrator.stop(); if (tensionSubscription != null) { tensionSubscription.unsubscribe(); frictionSubscription.unsubscribe(); destinationSubscription.unsubscribe(); } } @Override protected void onDisconnect(MotionObserver<T> observer) { integratorListeners.remove(observer); } }