public abstract class

KeyCycleOscillator

extends java.lang.Object

 java.lang.Object

↳androidx.constraintlayout.core.motion.utils.KeyCycleOscillator

Subclasses:

KeyCycleOscillator.PathRotateSet, ViewOscillator, ViewOscillator.PathRotateSet

Gradle dependencies

compile group: 'androidx.constraintlayout', name: 'constraintlayout-core', version: '1.1.0-alpha01'

  • groupId: androidx.constraintlayout
  • artifactId: constraintlayout-core
  • version: 1.1.0-alpha01

Artifact androidx.constraintlayout:constraintlayout-core:1.1.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

Provide the engine for executing cycles. KeyCycleOscillator

Summary

Fields
public intmVariesBy

Constructors
publicKeyCycleOscillator()

Methods
public floatget(float t)

public CurveFitgetCurveFit()

public floatgetSlope(float position)

public static KeyCycleOscillatormakeWidgetCycle(java.lang.String attribute)

protected voidsetCustom(java.lang.Object custom)

public voidsetPoint(int framePosition, int shape, java.lang.String waveString, int variesBy, float period, float offset, float phase, float value)

sets a oscillator wave point

public voidsetPoint(int framePosition, int shape, java.lang.String waveString, int variesBy, float period, float offset, float phase, float value, java.lang.Object custom)

sets a oscillator wave point

public voidsetProperty(MotionWidget widget, float t)

public voidsetType(java.lang.String type)

public voidsetup(float pathLength)

public java.lang.StringtoString()

public booleanvariesByPath()

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Fields

public int mVariesBy

Constructors

public KeyCycleOscillator()

Methods

public static KeyCycleOscillator makeWidgetCycle(java.lang.String attribute)

public boolean variesByPath()

public java.lang.String toString()

public void setType(java.lang.String type)

public float get(float t)

public float getSlope(float position)

public CurveFit getCurveFit()

protected void setCustom(java.lang.Object custom)

public void setPoint(int framePosition, int shape, java.lang.String waveString, int variesBy, float period, float offset, float phase, float value, java.lang.Object custom)

sets a oscillator wave point

Parameters:

framePosition: the position
variesBy: only varies by path supported for now
period: the period of the wave
offset: the offset value
value: the adder
custom: The ConstraintAttribute used to set the value

public void setPoint(int framePosition, int shape, java.lang.String waveString, int variesBy, float period, float offset, float phase, float value)

sets a oscillator wave point

Parameters:

framePosition: the position
variesBy: only varies by path supported for now
period: the period of the wave
offset: the offset value
value: the adder

public void setup(float pathLength)

public void setProperty(MotionWidget widget, float t)

Source

/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * 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 androidx.constraintlayout.core.motion.utils;

import androidx.constraintlayout.core.motion.MotionWidget;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

/**
 * Provide the engine for executing cycles.
 * KeyCycleOscillator
 *
 * @DoNotShow
 */
public abstract class KeyCycleOscillator {
    private static final String TAG = "KeyCycleOscillator";
    private CurveFit mCurveFit;
    private CycleOscillator mCycleOscillator;
    private String mType;
    private int mWaveShape = 0;
    private String mWaveString = null;

    public int mVariesBy = 0; // 0 = position, 2=path
    ArrayList<WavePoint> mWavePoints = new ArrayList<>();

    /**
     * @TODO: add description
     */
    public static KeyCycleOscillator makeWidgetCycle(String attribute) {
        if (attribute.equals(TypedValues.AttributesType.S_PATH_ROTATE)) {
            return new PathRotateSet(attribute);
        }
        return new CoreSpline(attribute);
    }

    private static class CoreSpline extends KeyCycleOscillator {
        String mType;
        int mTypeId;

        CoreSpline(String str) {
            mType = str;
            mTypeId = TypedValues.CycleType.getId(mType);
        }

        public void setProperty(MotionWidget widget, float t) {
            widget.setValue(mTypeId, get(t));
        }
    }

    public static class PathRotateSet extends KeyCycleOscillator {
        String mType;
        int mTypeId;

        public PathRotateSet(String str) {
            mType = str;
            mTypeId = TypedValues.CycleType.getId(mType);
        }

        @Override
        public void setProperty(MotionWidget widget, float t) {
            widget.setValue(mTypeId, get(t));
        }

        /**
         * @TODO: add description
         */
        public void setPathRotate(MotionWidget view, float t, double dx, double dy) {
            view.setRotationZ(get(t) + (float) Math.toDegrees(Math.atan2(dy, dx)));
        }
    }

    /**
     * @TODO: add description
     */
    public boolean variesByPath() {
        return mVariesBy == 1;
    }

    static class WavePoint {
        int mPosition;
        float mValue;
        float mOffset;
        float mPeriod;
        float mPhase;

        WavePoint(int position, float period, float offset, float phase, float value) {
            mPosition = position;
            mValue = value;
            mOffset = offset;
            mPeriod = period;
            mPhase = phase;
        }
    }

    @Override
    public String toString() {
        String str = mType;
        DecimalFormat df = new DecimalFormat("##.##");
        for (WavePoint wp : mWavePoints) {
            str += "[" + wp.mPosition + " , " + df.format(wp.mValue) + "] ";
        }
        return str;
    }

    public void setType(String type) {
        mType = type;
    }

    /**
     * @TODO: add description
     */
    public float get(float t) {
        return (float) mCycleOscillator.getValues(t);
    }

    /**
     * @TODO: add description
     */
    public float getSlope(float position) {
        return (float) mCycleOscillator.getSlope(position);
    }

    public CurveFit getCurveFit() {
        return mCurveFit;
    }

    protected void setCustom(Object custom) {

    }

    /**
     * sets a oscillator wave point
     *
     * @param framePosition the position
     * @param variesBy      only varies by path supported for now
     * @param period        the period of the wave
     * @param offset        the offset value
     * @param value         the adder
     * @param custom        The ConstraintAttribute used to set the value
     */
    public void setPoint(int framePosition,
            int shape,
            String waveString,
            int variesBy,
            float period,
            float offset,
            float phase,
            float value,
            Object custom) {
        mWavePoints.add(new WavePoint(framePosition, period, offset, phase, value));
        if (variesBy != -1) {
            mVariesBy = variesBy;
        }
        mWaveShape = shape;
        setCustom(custom);
        mWaveString = waveString;
    }

    /**
     * sets a oscillator wave point
     *
     * @param framePosition the position
     * @param variesBy      only varies by path supported for now
     * @param period        the period of the wave
     * @param offset        the offset value
     * @param value         the adder
     */
    public void setPoint(int framePosition,
            int shape,
            String waveString,
            int variesBy,
            float period,
            float offset,
            float phase,
            float value) {
        mWavePoints.add(new WavePoint(framePosition, period, offset, phase, value));
        if (variesBy != -1) {
            mVariesBy = variesBy;
        }
        mWaveShape = shape;
        mWaveString = waveString;
    }

    /**
     * @TODO: add description
     */
    public void setup(float pathLength) {
        int count = mWavePoints.size();
        if (count == 0) {
            return;
        }
        Collections.sort(mWavePoints, new Comparator<WavePoint>() {
            @Override
            public int compare(WavePoint lhs, WavePoint rhs) {
                return Integer.compare(lhs.mPosition, rhs.mPosition);
            }
        });
        double[] time = new double[count];
        double[][] values = new double[count][3];
        mCycleOscillator = new CycleOscillator(mWaveShape, mWaveString, mVariesBy, count);
        int i = 0;
        for (WavePoint wp : mWavePoints) {
            time[i] = wp.mPeriod * 1E-2;
            values[i][0] = wp.mValue;
            values[i][1] = wp.mOffset;
            values[i][2] = wp.mPhase;
            mCycleOscillator.setPoint(i, wp.mPosition, wp.mPeriod,
                    wp.mOffset, wp.mPhase, wp.mValue);
            i++;
        }
        mCycleOscillator.setup(pathLength);
        mCurveFit = CurveFit.get(CurveFit.SPLINE, time, values);
    }

    private static class IntDoubleSort {
        static void sort(int[] key, float[] value, int low, int hi) {
            int[] stack = new int[key.length + 10];
            int count = 0;
            stack[count++] = hi;
            stack[count++] = low;
            while (count > 0) {
                low = stack[--count];
                hi = stack[--count];
                if (low < hi) {
                    int p = partition(key, value, low, hi);
                    stack[count++] = p - 1;
                    stack[count++] = low;
                    stack[count++] = hi;
                    stack[count++] = p + 1;
                }
            }
        }

        private static int partition(int[] array, float[] value, int low, int hi) {
            int pivot = array[hi];
            int i = low;
            for (int j = low; j < hi; j++) {
                if (array[j] <= pivot) {
                    swap(array, value, i, j);
                    i++;
                }
            }
            swap(array, value, i, hi);
            return i;
        }

        private static void swap(int[] array, float[] value, int a, int b) {
            int tmp = array[a];
            array[a] = array[b];
            array[b] = tmp;
            float tmpv = value[a];
            value[a] = value[b];
            value[b] = tmpv;
        }
    }

    private static class IntFloatFloatSort {
        static void sort(int[] key, float[] value1, float[] value2, int low, int hi) {
            int[] stack = new int[key.length + 10];
            int count = 0;
            stack[count++] = hi;
            stack[count++] = low;
            while (count > 0) {
                low = stack[--count];
                hi = stack[--count];
                if (low < hi) {
                    int p = partition(key, value1, value2, low, hi);
                    stack[count++] = p - 1;
                    stack[count++] = low;
                    stack[count++] = hi;
                    stack[count++] = p + 1;
                }
            }
        }

        private static int partition(int[] array, float[] value1, float[] value2, int low, int hi) {
            int pivot = array[hi];
            int i = low;
            for (int j = low; j < hi; j++) {
                if (array[j] <= pivot) {
                    swap(array, value1, value2, i, j);
                    i++;
                }
            }
            swap(array, value1, value2, i, hi);
            return i;
        }

        private static void swap(int[] array, float[] value1, float[] value2, int a, int b) {
            int tmp = array[a];
            array[a] = array[b];
            array[b] = tmp;
            float tmpFloat = value1[a];
            value1[a] = value1[b];
            value1[b] = tmpFloat;
            tmpFloat = value2[a];
            value2[a] = value2[b];
            value2[b] = tmpFloat;
        }
    }

    static class CycleOscillator {
        static final int UNSET = -1; // -1 is typically used through out android to the UNSET value
        private static final String TAG = "CycleOscillator";
        private final int mVariesBy;
        Oscillator mOscillator = new Oscillator();
        private final int mOffst = 0;
        private final int mPhase = 1;
        private final int mValue = 2;

        float[] mValues;
        double[] mPosition;
        float[] mPeriod;
        float[] mOffsetArr; // offsets will be spline interpolated
        float[] mPhaseArr; // phase will be spline interpolated
        float[] mScale; // scales will be spline interpolated
        int mWaveShape;
        CurveFit mCurveFit;
        double[] mSplineValueCache; // for the return value of the curve fit
        double[] mSplineSlopeCache; // for the return value of the curve fit
        float mPathLength;

        CycleOscillator(int waveShape, String customShape, int variesBy, int steps) {
            mWaveShape = waveShape;
            mVariesBy = variesBy;
            mOscillator.setType(waveShape, customShape);
            mValues = new float[steps];
            mPosition = new double[steps];
            mPeriod = new float[steps];
            mOffsetArr = new float[steps];
            mPhaseArr = new float[steps];
            mScale = new float[steps];
        }

        public double getValues(float time) {
            if (mCurveFit != null) {
                mCurveFit.getPos(time, mSplineValueCache);
            } else { // only one value no need to interpolate
                mSplineValueCache[mOffst] = mOffsetArr[0];
                mSplineValueCache[mPhase] = mPhaseArr[0];
                mSplineValueCache[mValue] = mValues[0];

            }
            double offset = mSplineValueCache[mOffst];
            double phase = mSplineValueCache[this.mPhase];
            double waveValue = mOscillator.getValue(time, phase);
            return offset + waveValue * mSplineValueCache[mValue];
        }

        public double getLastPhase() {
            return mSplineValueCache[1];
        }

        public double getSlope(float time) {
            if (mCurveFit != null) {
                mCurveFit.getSlope(time, mSplineSlopeCache);
                mCurveFit.getPos(time, mSplineValueCache);
            } else { // only one value no need to interpolate
                mSplineSlopeCache[mOffst] = 0;
                mSplineSlopeCache[mPhase] = 0;
                mSplineSlopeCache[mValue] = 0;
            }
            double waveValue = mOscillator.getValue(time, mSplineValueCache[mPhase]);
            double waveSlope = mOscillator.getSlope(time,
                    mSplineValueCache[mPhase], mSplineSlopeCache[mPhase]);
            return mSplineSlopeCache[mOffst] + waveValue * mSplineSlopeCache[mValue]
                    + waveSlope * mSplineValueCache[mValue];
        }

        /**
         *
         */
        public void setPoint(int index,
                int framePosition,
                float wavePeriod,
                float offset,
                float phase,
                float values) {
            mPosition[index] = framePosition / 100.0;
            mPeriod[index] = wavePeriod;
            mOffsetArr[index] = offset;
            mPhaseArr[index] = phase;
            mValues[index] = values;
        }

        public void setup(float pathLength) {
            mPathLength = pathLength;
            double[][] splineValues = new double[mPosition.length][3];
            mSplineValueCache = new double[2 + mValues.length];
            mSplineSlopeCache = new double[2 + mValues.length];
            if (mPosition[0] > 0) {
                mOscillator.addPoint(0, mPeriod[0]);
            }
            int last = mPosition.length - 1;
            if (mPosition[last] < 1.0f) {
                mOscillator.addPoint(1, mPeriod[last]);
            }

            for (int i = 0; i < splineValues.length; i++) {
                splineValues[i][mOffst] = mOffsetArr[i];
                splineValues[i][mPhase] = mPhaseArr[i];
                splineValues[i][mValue] = mValues[i];
                mOscillator.addPoint(mPosition[i], mPeriod[i]);
            }

            // TODO: add mVariesBy and get total time and path length
            mOscillator.normalize();
            if (mPosition.length > 1) {
                mCurveFit = CurveFit.get(CurveFit.SPLINE, mPosition, splineValues);
            } else {
                mCurveFit = null;
            }
        }
    }

    /**
     * @TODO: add description
     */
    public void setProperty(MotionWidget widget, float t) {

    }

}