public class

Oscillator

extends java.lang.Object

 java.lang.Object

↳androidx.constraintlayout.core.motion.utils.Oscillator

Gradle dependencies

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

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

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

Overview

This generates variable frequency oscillation curves

Summary

Fields
public static final intBOUNCE

public static final intCOS_WAVE

public static final intCUSTOM

public static final intREVERSE_SAW_WAVE

public static final intSAW_WAVE

public static final intSIN_WAVE

public static final intSQUARE_WAVE

public static java.lang.StringTAG

public static final intTRIANGLE_WAVE

Constructors
publicOscillator()

Methods
public voidaddPoint(double position, float period)

public doublegetSlope(double time, double phase, double dphase)

public doublegetValue(double time, double phase)

public voidnormalize()

After adding point every thing must be normalized

public voidsetType(int type, java.lang.String customType)

public java.lang.StringtoString()

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

Fields

public static java.lang.String TAG

public static final int SIN_WAVE

public static final int SQUARE_WAVE

public static final int TRIANGLE_WAVE

public static final int SAW_WAVE

public static final int REVERSE_SAW_WAVE

public static final int COS_WAVE

public static final int BOUNCE

public static final int CUSTOM

Constructors

public Oscillator()

Methods

public java.lang.String toString()

public void setType(int type, java.lang.String customType)

public void addPoint(double position, float period)

public void normalize()

After adding point every thing must be normalized

public double getValue(double time, double phase)

public double getSlope(double time, double phase, double dphase)

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 java.util.Arrays;

/**
 * This generates variable frequency oscillation curves
 *
 *
 */
public class Oscillator {
    public static String TAG = "Oscillator";
    float[] mPeriod = {};
    double[] mPosition = {};
    double[] mArea;
    public static final int SIN_WAVE = 0; // theses must line up with attributes
    public static final int SQUARE_WAVE = 1;
    public static final int TRIANGLE_WAVE = 2;
    public static final int SAW_WAVE = 3;
    public static final int REVERSE_SAW_WAVE = 4;
    public static final int COS_WAVE = 5;
    public static final int BOUNCE = 6;
    public static final int CUSTOM = 7;
    String mCustomType;
    MonotonicCurveFit mCustomCurve;
    int mType;
    double mPI2 = Math.PI * 2;
    @SuppressWarnings("unused") private boolean mNormalized = false;

    public Oscillator() {
    }

    @Override
    public String toString() {
        return "pos =" + Arrays.toString(mPosition) + " period=" + Arrays.toString(mPeriod);
    }

    // @TODO: add description
    public void setType(int type, String customType) {
        mType = type;
        mCustomType = customType;
        if (mCustomType != null) {
            mCustomCurve = MonotonicCurveFit.buildWave(customType);
        }
    }

    // @TODO: add description
    public void addPoint(double position, float period) {
        int len = mPeriod.length + 1;
        int j = Arrays.binarySearch(mPosition, position);
        if (j < 0) {
            j = -j - 1;
        }
        mPosition = Arrays.copyOf(mPosition, len);
        mPeriod = Arrays.copyOf(mPeriod, len);
        mArea = new double[len];
        System.arraycopy(mPosition, j, mPosition, j + 1, len - j - 1);
        mPosition[j] = position;
        mPeriod[j] = period;
        mNormalized = false;
    }

    /**
     * After adding point every thing must be normalized
     */
    public void normalize() {
        double totalArea = 0;
        double totalCount = 0;
        for (int i = 0; i < mPeriod.length; i++) {
            totalCount += mPeriod[i];
        }
        for (int i = 1; i < mPeriod.length; i++) {
            float h = (mPeriod[i - 1] + mPeriod[i]) / 2;
            double w = mPosition[i] - mPosition[i - 1];
            totalArea = totalArea + w * h;
        }
        // scale periods to normalize it
        for (int i = 0; i < mPeriod.length; i++) {
            mPeriod[i] *= (float) (totalCount / totalArea);
        }
        mArea[0] = 0;
        for (int i = 1; i < mPeriod.length; i++) {
            float h = (mPeriod[i - 1] + mPeriod[i]) / 2;
            double w = mPosition[i] - mPosition[i - 1];
            mArea[i] = mArea[i - 1] + w * h;
        }
        mNormalized = true;
    }

    double getP(double time) {
        if (time <= 0.0) {
            return 0.0;
        } else if (time >= 1.0) {
            return 1.0;
        }
        // At this point, `index` is guaranteed to be != 0 for any time value (assuming mPosition
        // includes 0.0)
        int index = Arrays.binarySearch(mPosition, time);
        if (index < 0) {
            index = -index - 1;
        }

        double m = (mPeriod[index] - mPeriod[index - 1])
                / (mPosition[index] - mPosition[index - 1]);
        return mArea[index - 1]
                + (mPeriod[index - 1] - m * mPosition[index - 1]) * (time - mPosition[index - 1])
                + m * (time * time - mPosition[index - 1] * mPosition[index - 1]) / 2;
    }

    // @TODO: add description
    public double getValue(double time, double phase) {
        double angle = phase + getP(time); // angle is / by 360
        switch (mType) {
            default:
            case SIN_WAVE:
                return Math.sin(mPI2 * angle);
            case SQUARE_WAVE:
                return Math.signum(0.5 - angle % 1);
            case TRIANGLE_WAVE:
                return 1 - Math.abs((angle * 4 + 1) % 4 - 2);
            case SAW_WAVE:
                return ((angle * 2 + 1) % 2) - 1;
            case REVERSE_SAW_WAVE:
                return (1 - ((angle * 2 + 1) % 2));
            case COS_WAVE:
                return Math.cos(mPI2 * (phase + angle));
            case BOUNCE:
                double x = 1 - Math.abs((angle * 4) % 4 - 2);
                return 1 - x * x;
            case CUSTOM:
                return mCustomCurve.getPos(angle % 1, 0);
        }
    }

    double getDP(double time) {
        if (time <= 0.0) {
            return 0.0;
        } else if (time >= 1.0) {
            return 1.0;
        }
        // At this point, `index` is guaranteed to be != 0 for any time value (assuming mPosition
        // includes 0.0)
        int index = Arrays.binarySearch(mPosition, time);
        if (index < 0) {
            index = -index - 1;
        }
        double m = (mPeriod[index] - mPeriod[index - 1])
                / (mPosition[index] - mPosition[index - 1]);
        return m * time + (mPeriod[index - 1] - m * mPosition[index - 1]);
    }

    // @TODO: add description
    public double getSlope(double time, double phase, double dphase) {
        double angle = phase + getP(time);

        double dangle_dtime = getDP(time) + dphase;
        switch (mType) {
            default:
            case SIN_WAVE:
                return mPI2 * dangle_dtime * Math.cos(mPI2 * angle);
            case SQUARE_WAVE:
                return 0;
            case TRIANGLE_WAVE:
                return 4 * dangle_dtime * Math.signum((angle * 4 + 3) % 4 - 2);
            case SAW_WAVE:
                return dangle_dtime * 2;
            case REVERSE_SAW_WAVE:
                return -dangle_dtime * 2;
            case COS_WAVE:
                return -mPI2 * dangle_dtime * Math.sin(mPI2 * angle);
            case BOUNCE:
                return 4 * dangle_dtime * ((angle * 4 + 2) % 4 - 2);
            case CUSTOM:
                return mCustomCurve.getSlope(angle % 1, 0);
        }
    }
}