com.o2d.pkayjava.editor.view.ui.followers.PolygonFollower.java Source code

Java tutorial

Introduction

Here is the source code for com.o2d.pkayjava.editor.view.ui.followers.PolygonFollower.java

Source

/*
 * ******************************************************************************
 *  * Copyright 2015 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.o2d.pkayjava.editor.view.ui.followers;

import com.badlogic.ashley.core.Entity;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.*;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.o2d.pkayjava.runtime.components.PolygonComponent;
import com.o2d.pkayjava.runtime.components.TransformComponent;
import com.o2d.pkayjava.runtime.utils.PolygonUtils;
import com.o2d.pkayjava.runtime.utils.ComponentRetriever;
import com.o2d.pkayjava.editor.view.stage.Sandbox;
import com.o2d.pkayjava.editor.view.ui.followers.*;
import com.o2d.pkayjava.editor.view.ui.followers.PolygonTransformationListener;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * Created by azakhary on 7/2/2015.
 */
public class PolygonFollower extends SubFollower {

    private TransformComponent transformComponent;
    private PolygonComponent polygonComponent;

    private ArrayList<Vector2> originalPoints;
    private Vector2[] drawPoints;

    private ShapeRenderer shapeRenderer;

    public static final int ANCHOR_SIZE = 9;
    public static final int CIRCLE_RADIUS = 10;

    private static final Color outlineColor = new Color(200f / 255f, 156f / 255f, 71f / 255f, 1f);
    private static final Color innerColor = new Color(200f / 255f, 200f / 255f, 200f / 255f, 0.2f);
    private static final Color overColor = new Color(255f / 255f, 94f / 255f, 0f / 255f, 1f);
    private static final Color problemColor = new Color(200f / 255f, 0f / 255f, 0f / 255f, 1f);

    private int lineIndex = -1;
    public int draggingAnchorId = -1;

    private int[] intersections = null;

    private int selectedAnchorId = -1;

    private int pixelsPerWU = 1;

    OrthographicCamera runtimeCamera = Sandbox.getInstance().getCamera();

    public PolygonFollower(Entity entity) {
        super(entity);
        pixelsPerWU = Sandbox.getInstance().getPixelPerWU();
        setTouchable(Touchable.enabled);
    }

    public void create() {
        polygonComponent = ComponentRetriever.get(entity, PolygonComponent.class);
        transformComponent = ComponentRetriever.get(entity, TransformComponent.class);
        shapeRenderer = new ShapeRenderer();
    }

    @Override
    protected void setStage(Stage stage) {
        super.setStage(stage);
        if (stage != null) {
            shapeRenderer.setProjectionMatrix(getStage().getCamera().combined);
        }
    }

    public void update() {
        if (polygonComponent != null && polygonComponent.vertices != null) {
            computeOriginalPoints();
            computeDrawPoints();
            if (selectedAnchorId == -1)
                selectedAnchorId = 0;
        }
    }

    public void updateDraw() {
        computeDrawPoints();
    }

    private void computeOriginalPoints() {
        originalPoints = new ArrayList<>();
        if (polygonComponent == null)
            return;

        originalPoints = new ArrayList<>(
                Arrays.asList(PolygonUtils.mergeTouchingPolygonsToOne(polygonComponent.vertices)));

    }

    private void computeDrawPoints() {
        drawPoints = originalPoints.toArray(new Vector2[0]);
    }

    @Override
    public void draw(Batch batch, float parentAlpha) {
        if (polygonComponent != null && polygonComponent.vertices != null) {
            batch.end();

            Gdx.gl.glLineWidth(1.7f);
            Gdx.gl.glEnable(GL20.GL_BLEND);
            Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);

            shapeRenderer.setProjectionMatrix(getStage().getCamera().combined);
            Matrix4 matrix = batch.getTransformMatrix();
            matrix.scale(pixelsPerWU / runtimeCamera.zoom, pixelsPerWU / runtimeCamera.zoom, 1f);
            shapeRenderer.setTransformMatrix(matrix);

            drawTriangulatedPolygons();
            drawOutlines();
            drawPoints();

            Gdx.gl.glDisable(GL20.GL_BLEND);

            batch.begin();
        }
    }

    public void drawOutlines() {
        if (drawPoints.length > 0) {
            shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
            for (int i = 1; i < drawPoints.length; i++) {
                shapeRenderer.setColor(outlineColor);
                if (lineIndex == i && draggingAnchorId == -1) {
                    shapeRenderer.setColor(overColor);
                }
                if (checkIfLineIntersects(i - 1)) {
                    shapeRenderer.setColor(problemColor);
                }
                shapeRenderer.line(drawPoints[i].x * transformComponent.getScaleX(),
                        drawPoints[i].y * transformComponent.getScaleY(),
                        drawPoints[i - 1].x * transformComponent.getScaleX(),
                        drawPoints[i - 1].y * transformComponent.getScaleY());
            }
            shapeRenderer.setColor(outlineColor);
            if (lineIndex == 0 && draggingAnchorId == -1) {
                shapeRenderer.setColor(overColor);
            }
            if (checkIfLineIntersects(drawPoints.length - 1)) {
                shapeRenderer.setColor(problemColor);
            }
            shapeRenderer.line(drawPoints[drawPoints.length - 1].x * transformComponent.getScaleX(),
                    drawPoints[drawPoints.length - 1].y * transformComponent.getScaleY(),
                    drawPoints[0].x * transformComponent.getScaleX(),
                    drawPoints[0].y * transformComponent.getScaleY());
            shapeRenderer.end();
        }

    }

    private boolean checkIfLineIntersects(int index) {
        if (intersections == null)
            return false;
        for (int i = 0; i < intersections.length; i++) {
            if (intersections[i] == index)
                return true;
        }

        return false;
    }

    public void drawTriangulatedPolygons() {
        if (polygonComponent.vertices == null) {
            return;
        }
        if (intersections != null) {
            return;
        }

        shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
        shapeRenderer.setColor(innerColor);
        for (Vector2[] poly : polygonComponent.vertices) {
            for (int i = 1; i < poly.length; i++) {
                shapeRenderer.line(poly[i - 1].x * transformComponent.getScaleX(),
                        poly[i - 1].y * transformComponent.getScaleY(), poly[i].x * transformComponent.getScaleX(),
                        poly[i].y * transformComponent.getScaleY());
            }
            if (poly.length > 0)
                shapeRenderer.line(poly[poly.length - 1].x * transformComponent.getScaleX(),
                        poly[poly.length - 1].y * transformComponent.getScaleY(),
                        poly[0].x * transformComponent.getScaleX(), poly[0].y * transformComponent.getScaleY());
        }
        shapeRenderer.end();
    }

    public void drawPoints() {
        for (int i = 0; i < originalPoints.size(); i++) {
            shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
            shapeRenderer.setColor(Color.WHITE);
            if (selectedAnchorId == i) {
                shapeRenderer.setColor(Color.ORANGE);
            }
            float side = (float) (ANCHOR_SIZE) / ((float) pixelsPerWU / runtimeCamera.zoom);
            float onePixel = 1f / ((float) pixelsPerWU / runtimeCamera.zoom);
            shapeRenderer.rect(originalPoints.get(i).x * transformComponent.getScaleX() - side / 2f,
                    originalPoints.get(i).y * transformComponent.getScaleY() - side / 2f, side, side);
            shapeRenderer.setColor(Color.BLACK);
            shapeRenderer.rect(originalPoints.get(i).x * transformComponent.getScaleX() - side / 2f + onePixel,
                    originalPoints.get(i).y * transformComponent.getScaleY() - side / 2f + onePixel,
                    side - 2 * onePixel, side - 2 * onePixel);
            shapeRenderer.end();
        }
    }

    public void setListener(final PolygonTransformationListener listener) {
        clearListeners();
        addListener(new ClickListener() {

            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                super.touchDown(event, x, y, pointer, button);
                x = x / pixelsPerWU;
                y = y / pixelsPerWU;
                if (button != Input.Buttons.LEFT)
                    return true;
                int anchorId = anchorHitTest(x, y);
                if (anchorId >= 0) {
                    draggingAnchorId = anchorId;
                    listener.anchorDown(PolygonFollower.this, anchorId,
                            x * runtimeCamera.zoom / transformComponent.getScaleX(),
                            y * runtimeCamera.zoom / transformComponent.getScaleY());
                } else if (lineIndex > -1) {
                    // not anchor but line is selected gotta make new point
                    listener.vertexDown(PolygonFollower.this, lineIndex,
                            x * runtimeCamera.zoom / transformComponent.getScaleX(),
                            y * runtimeCamera.zoom / transformComponent.getScaleY());
                }
                return true;
            }

            @Override
            public void touchDragged(InputEvent event, float x, float y, int pointer) {
                x = x / pixelsPerWU;
                y = y / pixelsPerWU;
                int anchorId = draggingAnchorId;
                if (anchorId >= 0) {
                    listener.anchorDragged(PolygonFollower.this, anchorId,
                            x * runtimeCamera.zoom / transformComponent.getScaleX(),
                            y * runtimeCamera.zoom / transformComponent.getScaleY());
                } else if (lineIndex > -1) {

                }
            }

            @Override
            public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
                x = x / pixelsPerWU;
                y = y / pixelsPerWU;
                if (button != Input.Buttons.LEFT)
                    return;
                int anchorId = anchorHitTest(x, y);
                lineIndex = vertexHitTest(x, y);
                if (anchorId >= 0) {
                    listener.anchorUp(PolygonFollower.this, anchorId,
                            x * runtimeCamera.zoom / transformComponent.getScaleX(),
                            y * runtimeCamera.zoom / transformComponent.getScaleY());
                } else if (lineIndex > -1) {
                    listener.vertexUp(PolygonFollower.this, lineIndex,
                            x * runtimeCamera.zoom / transformComponent.getScaleX(),
                            y * runtimeCamera.zoom / transformComponent.getScaleY());
                }
                draggingAnchorId = -1;
            }

            @Override
            public boolean mouseMoved(InputEvent event, float x, float y) {
                x = x / pixelsPerWU;
                y = y / pixelsPerWU;
                int anchorId = anchorHitTest(x, y);
                lineIndex = vertexHitTest(x, y);
                if (anchorId >= 0) {
                    lineIndex = -1;
                }
                if (lineIndex > -1) {

                }

                return super.mouseMoved(event, x, y);
            }
        });
    }

    @Override
    public Actor hit(float x, float y, boolean touchable) {
        if (originalPoints == null || originalPoints.size() == 0)
            return null;

        x = x / pixelsPerWU;
        y = y / pixelsPerWU;

        int anchorId = anchorHitTest(x, y);
        if (anchorId > -1) {
            return this;
        }

        // checking for vertex intersect
        lineIndex = vertexHitTest(x, y);
        if (lineIndex > -1) {
            return this;
        }

        return null;
    }

    private int vertexHitTest(float x, float y) {
        Vector2 tmpVector = new Vector2(x, y);
        int lineIndex = -1;

        float circleSqr = ((float) CIRCLE_RADIUS / pixelsPerWU) * ((float) CIRCLE_RADIUS / pixelsPerWU);

        for (int i = 1; i < drawPoints.length; i++) {
            Vector2 pointOne = drawPoints[i - 1].cpy().scl(1f / runtimeCamera.zoom * transformComponent.getScaleX(),
                    1f / runtimeCamera.zoom * transformComponent.getScaleY());
            Vector2 pointTwo = drawPoints[i].cpy().scl(1f / runtimeCamera.zoom * transformComponent.getScaleX(),
                    1f / runtimeCamera.zoom * transformComponent.getScaleY());
            if (Intersector.intersectSegmentCircle(pointOne, pointTwo, tmpVector, circleSqr)) {
                lineIndex = i;
                break;
            }
        }
        Vector2 pointOne = drawPoints[drawPoints.length - 1].cpy().scl(
                1f / runtimeCamera.zoom * transformComponent.getScaleX(),
                1f / runtimeCamera.zoom * transformComponent.getScaleY());
        Vector2 pointTwo = drawPoints[0].cpy().scl(1f / runtimeCamera.zoom * transformComponent.getScaleX(),
                1f / runtimeCamera.zoom * transformComponent.getScaleY());
        if (drawPoints.length > 0 && Intersector.intersectSegmentCircle(pointOne, pointTwo, tmpVector, circleSqr)) {
            lineIndex = 0;
        }

        if (lineIndex > -1) {
            return lineIndex;
        }

        return -1;
    }

    private int anchorHitTest(float x, float y) {
        if (originalPoints == null || originalPoints.size() == 0)
            return -1;

        for (int i = 0; i < drawPoints.length; i++) {
            Circle pointCircle = new Circle(drawPoints[i].x / runtimeCamera.zoom * transformComponent.getScaleX(),
                    drawPoints[i].y / runtimeCamera.zoom * transformComponent.getScaleY(),
                    (float) CIRCLE_RADIUS / pixelsPerWU);
            if (pointCircle.contains(x, y)) {
                return i;
            }
        }
        return -1;
    }

    public Entity getEntity() {
        return entity;
    }

    public ArrayList<Vector2> getOriginalPoints() {
        return originalPoints;
    }

    public void setSelectedAnchor(int anchorId) {
        if (anchorId == -1)
            return;

        selectedAnchorId = anchorId;
    }

    public int getSelectedAnchorId() {
        return selectedAnchorId;
    }

    public void getSelectedAnchorId(int id) {
        if (id < 0)
            id = 0;
        selectedAnchorId = id;
    }

    public void setProblems(int[] intersections) {
        this.intersections = intersections;
    }
}