All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.talosvfx.talos.runtime.modules.OffsetModule Maven / Gradle / Ivy

There is a newer version: 1.4.1
Show newest version
/*******************************************************************************
 * Copyright 2019 See AUTHORS file.
 *
 * 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.talosvfx.talos.runtime.modules;

import com.badlogic.gdx.math.*;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.JsonValue;
import com.talosvfx.talos.runtime.ScopePayload;
import com.talosvfx.talos.runtime.values.NumericalValue;

import java.util.Comparator;
import java.util.Random;

public class OffsetModule extends AbstractModule {

    public static final int ALPHA = 0;

    public static final int OUTPUT = 0;

    NumericalValue alpha;
    NumericalValue output;

    NumericalValue lowPos;
    NumericalValue lowSize;
    NumericalValue highPos;
    NumericalValue highSize;

    private int lowShape;
    private int highShape;

    private boolean lowEdge = true;
    private boolean highEdge = true;

    private float tolerance = 0;

    public static final int TYPE_SQUARE = 0;
    public static final int TYPE_ELLIPSE = 1;
    public static final int TYPE_LINE = 2;

    public static final int SIDE_ALL = 0;
    public static final int SIDE_TOP = 1;
    public static final int SIDE_BOTTOM = 2;
    public static final int SIDE_LEFT = 3;
    public static final int SIDE_RIGHT = 4;

    private int lowSide = SIDE_BOTTOM;
    private int highSide = SIDE_RIGHT;

    private Vector2 randLow = new Vector2();
    private Vector2 randHigh = new Vector2();

    private Random random = new Random();

    private Rectangle rect = new Rectangle();
    private Vector2 tmp = new Vector2();

    private Array points;

    Comparator comparator = new Comparator() {
        @Override
        public int compare(Vector2 o1, Vector2 o2) {
            if(o1.x < o2.x) return -1;
            if(o1.x > o2.x) return 1;

            return 0;
        }
    };

    @Override
    public void init () {
        super.init();
        resetPoints();
    }

    @Override
    protected void defineSlots() {
        alpha = createInputSlot(ALPHA);
        output = createOutputSlot(OUTPUT);

        lowPos = new NumericalValue();
        lowSize = new NumericalValue();
        highPos = new NumericalValue();
        highSize = new NumericalValue();
    }

    @Override
    public void processValues() {
        processAlphaDefaults();

        float alpha = this.alpha.getFloat();

        alpha = interpolate(alpha); // apply the curve

        // let's find pos by shape
        getRandomPosOn(lowSide, lowEdge, lowShape, lowPos, lowSize, randLow);
        getRandomPosOn(highSide, highEdge, highShape, highPos, highSize, randHigh);

        float x = Interpolation.linear.apply(randLow.x, randHigh.x, alpha);
        float y = Interpolation.linear.apply(randLow.y, randHigh.y, alpha);

        output.set(x, y);
    }

    private void getRandomPosOn(int side, boolean edge, int shape, NumericalValue pos, NumericalValue size, Vector2 result) {
        random.setSeed((long) ((getScope().getFloat(ScopePayload.PARTICLE_SEED) * 10000 * index * 1000)));
        float angle = random.nextFloat();

        if(side == SIDE_TOP) angle = angle/2f;
        if(side == SIDE_BOTTOM) angle = angle/2f + 0.5f;
        if(side == SIDE_LEFT) angle = angle/2f + 0.25f;
        if(side == SIDE_RIGHT) angle = angle/2f - 0.25f;

        if(shape == TYPE_SQUARE) {
            findRandomSquarePos(angle, pos, size, result);
        } else if (shape == TYPE_ELLIPSE) {
            findRandomEllipsePos(angle, pos, size, result);
        } else if (shape == TYPE_LINE){
            findRandomLinePos(angle, pos, size, result);
        }

        float endX = result.x + tolerance;
        float endY = result.y + tolerance;
        float startX = result.x - tolerance;
        float startY = result.y - tolerance;
        if(!edge) {
            startX = pos.get(0);
            startY = pos.get(1);
        }

        float resultX = random.nextFloat() * (endX - startX) + startX;
        float resultY = random.nextFloat() * (endY - startY) + startY;

        result.set(resultX, resultY);
    }

    private void findRandomEllipsePos(float angle, NumericalValue pos, NumericalValue size, Vector2 result) {
        angle = angle * 360;

        float x = MathUtils.cosDeg(angle) * size.get(0)/2f + pos.get(0);
        float y = MathUtils.sinDeg(angle) * size.get(1)/2f + pos.get(1);

        result.set(x, y);
    }

    private void findRandomSquarePos(float angle, NumericalValue pos, NumericalValue size, Vector2 result) {
        angle = angle * 360;
        rect.set(pos.get(0) - size.get(0)/2f, pos.get(1) - size.get(1)/2f, size.get(0), size.get(1));
        intersectSegmentRectangle(pos.get(0), pos.get(1), pos.get(0) + rect.width * MathUtils.cosDeg(angle), pos.get(1) + rect.height * MathUtils.sinDeg(angle), rect, result);
    }

    private void findRandomLinePos(float angle, NumericalValue pos, NumericalValue size, Vector2 result) {
        angle = angle * 360;
        rect.set(pos.get(0) - size.get(0)/2f, pos.get(1) - size.get(1)/2f, size.get(0), size.get(1));
        tmp.set(rect.width, rect.height); // initial segment vector; for alpha
        float alpha = tmp.angle();
        float beta = angle - alpha;
        float r3 = tmp.set(MathUtils.cosDeg(angle) * rect.width/2f, MathUtils.sinDeg(angle) * rect.height/2f).len();
        float r4 = MathUtils.cosDeg(beta) * r3;
        float posX = r4 * MathUtils.cosDeg(alpha) + pos.get(0);
        float posY = r4 * MathUtils.sinDeg(alpha) + pos.get(1);

        result.set(posX, posY);
    }

    protected void processAlphaDefaults() {
        if(alpha.isEmpty()) {
            // as default we are going to fetch the lifetime or duration depending on context
            float requester = getScope().getFloat(ScopePayload.REQUESTER_ID);
            if(requester < 1) {
                // particle
                alpha.set(getScope().get(ScopePayload.PARTICLE_ALPHA));
                alpha.setEmpty(false);
            } else if(requester > 1) {
                // emitter
                alpha.set(getScope().get(ScopePayload.EMITTER_ALPHA));
                alpha.setEmpty(false);
            } else {
                // whaat?
                alpha.set(0);
            }
        }
    }

    public static boolean intersectSegmentRectangle (float startX, float startY, float endX, float endY, Rectangle rectangle, Vector2 intersection) {
        float rectangleEndX = rectangle.x + rectangle.width;
        float rectangleEndY = rectangle.y + rectangle.height;

        if (Intersector.intersectSegments(startX, startY, endX, endY, rectangle.x, rectangle.y, rectangle.x, rectangleEndY, intersection)) return true;

        if (Intersector.intersectSegments(startX, startY, endX, endY, rectangle.x, rectangle.y, rectangleEndX, rectangle.y, intersection)) return true;

        if (Intersector.intersectSegments(startX, startY, endX, endY, rectangleEndX, rectangle.y, rectangleEndX, rectangleEndY, intersection))
            return true;

        if (Intersector.intersectSegments(startX, startY, endX, endY, rectangle.x, rectangleEndY, rectangleEndX, rectangleEndY, intersection))
            return true;

        return rectangle.contains(startX, startY);
    }

    private float interpolate(float alpha) {
        // interpolate alpha in this point space

        if(points.get(0).x >= 0 && alpha <= points.get(0).x) {
            return points.get(0).y;
        }

        for(int i = 0; i < points.size-1; i++) {
            Vector2 from = points.get(i);
            Vector2 to = points.get(i+1);
            if(alpha > from.x && alpha <= to.x) {
                float localAlpha = 1f;
                if(from.x != to.x) {
                    localAlpha = (alpha - from.x) / (to.x - from.x);
                }
                return interpolate(localAlpha, from.x, from.y, to.x, to.y);
            }
        }

        if(points.get(points.size-1).x <= 1f && alpha >= points.get(points.size-1).x) {
            return points.get(points.size-1).y;
        }

        return 0;
    }

    private float interpolate(float alpha, float x1, float y1, float x2, float y2) {
        if(y1 == y2) return y1;
        if(x1 == x2) return y1;

        tmp.set(x2, y2);
        tmp.sub(x1, y1);
        tmp.scl(alpha);
        tmp.add(x1, y1);

        return tmp.y;
    }

    public void setLowPos(Vector2 pos) {
        lowPos.set(pos.x, pos.y);
    }

    public void getLowPos(Vector2 result) {
        result.set(lowPos.get(0), lowPos.get(1));
    }
    public void getHighPos(Vector2 result) {
        result.set(highPos.get(0), highPos.get(1));
    }
    public void getLowSize(Vector2 result) {
        result.set(lowSize.get(0), lowSize.get(1));
    }
    public void getHighSize(Vector2 result) {
        result.set(highSize.get(0), highSize.get(1));
    }

    public void setLowSize(Vector2 size) {
        lowSize.set(size.x, size.y);
    }

    public void setHighPos(Vector2 pos) {
        highPos.set(pos.x, pos.y);
    }

    public void setHighSize(Vector2 size) {
        highSize.set(size.x, size.y);
    }

    public void setLowShape(int shape) {
        lowShape = shape;
    }

    public void setHighShape(int shape) {
        highShape = shape;
    }

    private void resetPoints() {
        // need to guarantee at least one point
        points = new Array<>();
        Vector2 point = new Vector2(0, 0.5f);
        points.add(point);
    }

    public void setPoints(Array newPoints) {
        points.clear();
        for(Vector2 point : newPoints) {
            Vector2 newPoint = new Vector2(point);
            points.add(newPoint);
        }
    }

    public Array getPoints() {
        return points;
    }


    public int createPoint(float x, float y) {

        if(x < 0) x = 0;
        if(x > 1) x = 1;
        if(y < 0) y = 0;
        if(y > 1) y = 1;

        Vector2 point = new Vector2(x, y);
        points.add(point);

        sortPoints();

        return points.indexOf(point, true);
    }

    private void sortPoints() {
        points.sort(comparator);
    }

    public void removePoint(int i) {
        if(points.size > 1) {
            points.removeIndex(i);
        }
    }

    public void setLowEdge(boolean edge) {
        lowEdge = edge;
    }

    public void setHighEdge(boolean edge) {
        highEdge = edge;
    }

    public void setLowSide(int side) {
        lowSide = side;
    }

    public void setHighSide(int side) {
        highSide = side;
    }

    public int getLowShape() {
        return lowShape;
    }

    public int getHighShape() {
        return highShape;
    }

    public boolean getLowEdge() {
        return lowEdge;
    }

    public boolean getHighEdge() {
        return highEdge;
    }

    public int getLowSide() {
        return lowSide;
    }

    public int getHighSide() {
        return highSide;
    }

    @Override
    public void read(Json json, JsonValue jsonData) {
        super.read(json, jsonData);

        points.clear();
        final JsonValue points = jsonData.get("points");
        for (JsonValue point : points) {
            createPoint(point.get(0).asFloat(), point.get(1).asFloat());
        }

        final JsonValue low = jsonData.get("low");
        final JsonValue high = jsonData.get("high");

        lowEdge = low.getBoolean("edge");
        highEdge = high.getBoolean("edge");

        lowShape = low.getInt("shape");
        highShape = high.getInt("shape");

        lowSide = low.getInt("side");
        highSide = high.getInt("side");

        final JsonValue lowRect = low.get("rect");
        final JsonValue highRect = high.get("rect");

        lowPos.set(lowRect.getFloat("x"), lowRect.getFloat("y"));
        lowSize.set(lowRect.getFloat("width"), lowRect.getFloat("height"));

        highPos.set(highRect.getFloat("x"), highRect.getFloat("y"));
        highSize.set(highRect.getFloat("width"), highRect.getFloat("height"));
    }

    @Override
    public void write(Json json) {
        super.write(json);

        json.writeArrayStart("points");
        for (Vector2 point : getPoints()) {
            json.writeObjectStart();
            json.writeValue("x", point.x);
            json.writeValue("y", point.y);
            json.writeObjectEnd();
        }
        json.writeArrayEnd();

        json.writeObjectStart("low");
        json.writeValue("edge", lowEdge);
        json.writeValue("shape", lowShape);
        json.writeValue("side", lowSide);
        json.writeObjectStart("rect");
        json.writeValue("x", lowPos.get(0));
        json.writeValue("y", lowPos.get(1));
        json.writeValue("width", lowSize.get(0));
        json.writeValue("height", lowSize.get(1));
        json.writeObjectEnd();
        json.writeObjectEnd();

        json.writeObjectStart("high");
        json.writeValue("edge", highEdge);
        json.writeValue("shape", highShape);
        json.writeValue("side", highSide);
        json.writeObjectStart("rect");
        json.writeValue("x", highPos.get(0));
        json.writeValue("y", highPos.get(1));
        json.writeValue("width", highSize.get(0));
        json.writeValue("height", highSize.get(1));
        json.writeObjectEnd();
        json.writeObjectEnd();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy