WidgetFrame.java

/*
 * Copyright (C) 2021 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.state;

import androidx.constraintlayout.core.widgets.ConstraintWidget;

import java.util.HashMap;

/**
 * Utility class to encapsulate layout of a widget
 */
public class WidgetFrame {
    public ConstraintWidget widget = null;
    public int left = 0;
    public int top = 0;
    public int right = 0;
    public int bottom = 0;

    // transforms

    public float pivotX = Float.NaN;
    public float pivotY = Float.NaN;

    public float rotationX = Float.NaN;
    public float rotationY = Float.NaN;
    public float rotationZ = Float.NaN;

    public float translationX = Float.NaN;
    public float translationY = Float.NaN;

    public float scaleX = Float.NaN;
    public float scaleY = Float.NaN;

    public float alpha = Float.NaN;

    public HashMap<String, Color> mCustomColors = null;
    public HashMap<String, Float> mCustomFloats = null;

    public static class Color {
        public float r;
        public float g;
        public float b;
        public float a;

        public Color(float r, float g, float b, float a) {
            this.r = r;
            this.g = g;
            this.b = b;
            this.a = a;
        }

        public void copy(Color start) {
            this.r = start.r;
            this.g = start.g;
            this.b = start.b;
            this.a = start.a;
        }
    }


    public int width() { return right - left; }
    public int height() { return bottom - top; }

    public WidgetFrame() {}

    public WidgetFrame(ConstraintWidget widget) {
        this.widget = widget;
    }

    public WidgetFrame(WidgetFrame frame) {
        widget = frame.widget;
        left = frame.left;
        top = frame.top;
        right = frame.right;
        bottom = frame.bottom;
        pivotX = frame.pivotX;
        pivotY = frame.pivotY;
        rotationX = frame.rotationX;
        rotationY = frame.rotationY;
        rotationZ = frame.rotationZ;
        translationX = frame.translationX;
        translationY = frame.translationY;
        scaleX = frame.scaleX;
        scaleY = frame.scaleY;
        alpha = frame.alpha;
        if (frame.mCustomColors != null) {
            mCustomColors = new HashMap<>();
            mCustomColors.putAll(frame.mCustomColors);
        }
        if (frame.mCustomFloats != null) {
            mCustomFloats = new HashMap<>();
            mCustomFloats.putAll(frame.mCustomFloats);
        }
    }

    public boolean isDefaultTransform() {
        return Float.isNaN(rotationX)
                && Float.isNaN(rotationY)
                && Float.isNaN(rotationZ)
                && Float.isNaN(translationX)
                && Float.isNaN(translationY)
                && Float.isNaN(scaleX)
                && Float.isNaN(scaleY)
                && Float.isNaN(alpha);
    }

    public static void interpolate(WidgetFrame frame, WidgetFrame start, WidgetFrame end, float progress) {
        frame.widget = start.widget;
        frame.left = (int) (start.left + progress*(end.left - start.left));
        frame.top = (int) (start.top + progress*(end.top - start.top));
        frame.right = (int) (start.right + progress*(end.right - start.right));
        frame.bottom = (int) (start.bottom + progress*(end.bottom - start.bottom));

        frame.pivotX = interpolate(start.pivotX, end.pivotX, 0f, progress);
        frame.pivotY = interpolate(start.pivotY, end.pivotY, 0f, progress);

        frame.rotationX = interpolate(start.rotationX, end.rotationX, 0f, progress);
        frame.rotationY = interpolate(start.rotationY, end.rotationY, 0f, progress);
        frame.rotationZ = interpolate(start.rotationZ, end.rotationZ, 0f, progress);

        frame.scaleX = interpolate(start.scaleX, end.scaleX, 0f, progress);
        frame.scaleY = interpolate(start.scaleY, end.scaleY, 0f, progress);

        frame.translationX = interpolate(start.translationX, end.translationX, 0f, progress);
        frame.translationY = interpolate(start.translationY, end.translationY, 0f, progress);

        frame.alpha = interpolate(start.alpha, end.alpha, 0f, progress);
    }

    private static float interpolate(float start, float end, float defaultValue, float progress) {
        boolean isStartUnset = Float.isNaN(start);
        boolean isEndUnset = Float.isNaN(end);
        if (isStartUnset && isEndUnset) {
            return Float.NaN;
        }
        if (isStartUnset) {
            start = defaultValue;
        }
        if (isEndUnset) {
            end = defaultValue;
        }
        return (start + progress * (end - start));
    }

    public static void interpolateColor(Color result, Color start, Color end, float progress) {
        if (progress < 0) {
            result.copy(start);
        } else if (progress > 1) {
            result.copy(end);
        } else {
            result.r = (1f - progress) * start.r + progress * (end.r);
            result.g = (1f - progress) * start.g + progress * (end.g);
            result.b = (1f - progress) * start.b + progress * (end.b);
            result.a = (1f - progress) * start.a + progress * (end.a);
        }
    }

    public float centerX() {
        return left + (right - left)/2f;
    }

    public float centerY() {
        return top + (bottom - top)/2f;
    }

    public WidgetFrame update() {
        if (widget != null) {
            left = widget.getLeft();
            top = widget.getTop();
            right = widget.getRight();
            bottom = widget.getBottom();
            WidgetFrame frame = widget.frame;
            pivotX = frame.pivotX;
            pivotY = frame.pivotY;
            rotationX = frame.rotationX;
            rotationY = frame.rotationY;
            rotationZ = frame.rotationZ;
            translationX = frame.translationX;
            translationY = frame.translationY;
            scaleX = frame.scaleX;
            scaleY = frame.scaleY;
            alpha = frame.alpha;
            if (frame.mCustomColors != null) {
                mCustomColors = new HashMap<>();
                mCustomColors.putAll(frame.mCustomColors);
            }
            if (frame.mCustomFloats != null) {
                mCustomFloats = new HashMap<>();
                mCustomFloats.putAll(frame.mCustomFloats);
            }
        }
        return this;
    }

    public WidgetFrame update(ConstraintWidget widget) {
        if (widget == null) {
            return this;
        }
        this.widget = widget;
        update();
        return this;
    }

    public void addCustomColor(String name, float r, float g, float b, float a) {
        Color color = new Color(r, g, b, a);
        if (mCustomColors == null) {
            mCustomColors = new HashMap<>();
        }
        mCustomColors.put(name, color);
    }

    public Color getCustomColor(String name) {
        if (mCustomColors == null) {
            return null;
        }
        return mCustomColors.get(name);
    }

    public void addCustomFloat(String name, float value) {
        if (mCustomFloats == null) {
            mCustomFloats = new HashMap<>();
        }
        mCustomFloats.put(name, value);
    }

    public float getCustomFloat(String name) {
        if (mCustomFloats == null) {
            return 0f;
        }
        return mCustomFloats.get(name);
    }
}