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

jfxtras.labs.internal.scene.control.skin.MatrixPanelSkin Maven / Gradle / Ivy

There is a newer version: 9.0-r1
Show newest version
/**
 * MatrixPanelSkin.java
 *
 * Copyright (c) 2011-2015, JFXtras
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the organization nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL  BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package jfxtras.labs.internal.scene.control.skin;

import java.io.File;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javafx.animation.AnimationTimer;
import javafx.animation.PauseTransition;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent;
import javafx.geometry.Point2D;
import javafx.scene.control.Skin;
import javafx.scene.control.SkinBase;
import javafx.scene.image.Image;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundImage;
import javafx.scene.layout.BackgroundPosition;
import javafx.scene.layout.BackgroundRepeat;
import javafx.scene.layout.BackgroundSize;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.util.Duration;
import jfxtras.labs.scene.control.gauge.Content;
import jfxtras.labs.scene.control.gauge.Content.MatrixColor;
import jfxtras.labs.scene.control.gauge.Content.RotationOrder;
import jfxtras.labs.scene.control.gauge.MatrixPanel;
import jfxtras.labs.scene.control.gauge.UtilHex;
import jfxtras.labs.util.ConicalGradient;
import jfxtras.labs.util.Util;


/**
 * Created by
 * User: hansolo
 * Date: 09.01.12
 * Time: 18:04
 * Modified by Jose Pereda Llamas <jperedadnr>
 * On : 23-jun-2012, 11:47:23
 */
public class MatrixPanelSkin extends SkinBase implements Skin {
    private static final double      PREFERRED_WIDTH  = 170;
    private static final double      PREFERRED_HEIGHT = 350;
    private static final double      MINIMUM_WIDTH    = 17;
    private static final double      MINIMUM_HEIGHT   = 35;
    private static final double      MAXIMUM_WIDTH    = 1700;
    private static final double      MAXIMUM_HEIGHT   = 3500;
    private static double            aspectRatio      = PREFERRED_HEIGHT / PREFERRED_WIDTH;
    private double                   width;
    private double                   height;
    private Pane                     pane;
    private Region                   main;
    private Region                   mainFrameOut;
    private Region                   mainFrame;
    private Region                   mainFrameIn;
    private Region                   mainForeground;
    private Pane                     dots;
    private Map      dotMap;
    private int                      iDots;
    private double                   radio=0d;
    private final int                toneScale=85;
    private BooleanProperty[]        visibleContent=null;
    private final Color              COLOR_OFF = Color.rgb(39, 39, 39,0.25);
    private String                   jpgFrame;
    private Background               fillFrame;
    // ******************** Constructors **************************************
    public MatrixPanelSkin(final MatrixPanel CONTROL) {
        super(CONTROL);
        
        init();
        initGraphics();
        registerListeners();
    }

    // ******************** Initialization ************************************
    private void init() {
        if (Double.compare(getSkinnable().getPrefWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getPrefHeight(), 0.0) <= 0 ||
            Double.compare(getSkinnable().getWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getHeight(), 0.0) <= 0) {
            setSize();
        }

        if (Double.compare(getSkinnable().getMinWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getMinHeight(), 0.0) <= 0) {
            getSkinnable().setMinSize(MINIMUM_WIDTH, MINIMUM_HEIGHT);
        }

        if (Double.compare(getSkinnable().getMaxWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getMaxHeight(), 0.0) <= 0) {
            getSkinnable().setMaxSize(MAXIMUM_WIDTH, MAXIMUM_HEIGHT);
        }

        if (getSkinnable().getPrefWidth() != PREFERRED_WIDTH || getSkinnable().getPrefHeight() != PREFERRED_HEIGHT) {
            aspectRatio = getSkinnable().getPrefHeight() / getSkinnable().getPrefWidth();
        }
    }

    private void initGraphics() {
        main = new Region();
        main.getStyleClass().setAll("main");
        main.setOpacity(1);
        
        mainFrameOut = new Region();
        
        mainFrame = new Region();
        mainFrameIn = new Region();
        setStyle();
        
        setDots();
        
        mainForeground = new Region();
        mainForeground.getStyleClass().setAll("frontFrame");
        mainForeground.setOpacity(1);
        
        pane = new Pane();
        pane.getChildren().setAll(main,mainFrameOut,mainFrame,mainFrameIn,dots,mainForeground);
        pane.setCache(true);

        pane.getChildren().stream().filter((n) -> n instanceof Pane).
                forEach((n) -> iDots=pane.getChildren().indexOf(n) );
        getChildren().setAll(pane);

        gradient();
        createContents();
        
    }
    
    private void setSize(){
        if (getSkinnable().getPrefWidth() > 0 && getSkinnable().getPrefHeight() > 0) {
            if(getSkinnable().getLedWidth()>0 && getSkinnable().getLedHeight()>0){
                double scale=Math.min(getSkinnable().getPrefWidth()/getSkinnable().getLedWidth(),
                                      getSkinnable().getPrefHeight()/getSkinnable().getLedHeight());
                /*
                ASPECT RATIO: DEPENDS ON LEDS ARRAY ASPECT RATIO
                */
                getSkinnable().setPrefSize(getSkinnable().getLedWidth()*scale,getSkinnable().getLedHeight()*scale);
            }
            else{
                getSkinnable().setPrefSize(getSkinnable().getPrefWidth(), getSkinnable().getPrefHeight());
            }
        } else {
            getSkinnable().setPrefSize(PREFERRED_WIDTH, PREFERRED_HEIGHT);
        }
    }
    
    private void setStyle(){
        if(!getSkinnable().isFrameVisible()){
            mainFrameOut.getStyleClass().removeAll("mainFrameOut");
            mainFrame.getStyleClass().removeAll("glossy-metal","dark-glossy","gradient");
            mainFrameIn.getStyleClass().setAll("gradient-in");
            return;
        }
        switch (getSkinnable().getFrameDesign()) {
            case GLOSSY_METAL:
                mainFrame.getStyleClass().setAll("glossy-metal");
                mainFrameOut.getStyleClass().setAll("mainFrameOut");
                mainFrameIn.getStyleClass().remove("gradient-in");
                break;
            case DARK_GLOSSY:
                mainFrame.getStyleClass().setAll("dark-glossy");
                mainFrameOut.getStyleClass().setAll("mainFrameOut");
                mainFrameIn.getStyleClass().remove("gradient-in");
                break;
            case CHROME:
                mainFrame.getStyleClass().setAll("gradient");
                mainFrameIn.getStyleClass().setAll("gradient-in");
                break;
            case BLACK_METAL:
                mainFrame.getStyleClass().setAll("gradient");
                mainFrameIn.getStyleClass().setAll("gradient-in");
                break;
            case SHINY_METAL:
                mainFrame.getStyleClass().setAll("gradient");
                mainFrameIn.getStyleClass().setAll("gradient-in");
                break;
            case CUSTOM_DESIGN:
                mainFrameIn.getStyleClass().setAll("gradient-in");
                break;
        }
    }
    
    private void setDots(){
        dots = new Pane();
        dotMap = new HashMap<>(getSkinnable().ledWidthProperty().intValue() * getSkinnable().ledHeightProperty().intValue());
        for (int i = 0; i < getSkinnable().ledHeightProperty().intValue(); i++) {
            for (int j = 0; j < getSkinnable().ledWidthProperty().intValue(); j++) {
                Circle circ = new Circle(radio,COLOR_OFF);
                dotMap.put(new Integer(j + i * getSkinnable().ledWidthProperty().intValue()), circ);
                dots.getChildren().add(circ);
            }
        }      
        dots.setCache(true);
        if(pane!=null && pane.getChildren().size()>0){
            pane.getChildren().remove(iDots);
            pane.getChildren().add(iDots, dots);
        }
    }

    private void registerListeners() {
        getSkinnable().widthProperty().addListener(o -> handleControlPropertyChanged("RESIZE") );
        getSkinnable().heightProperty().addListener(o -> handleControlPropertyChanged("RESIZE") );
        getSkinnable().prefWidthProperty().addListener(o -> handleControlPropertyChanged("PREF_SIZE") );
        getSkinnable().prefHeightProperty().addListener(o -> handleControlPropertyChanged("PREF_SIZE") );
        getSkinnable().ledWidthProperty().addListener(o -> handleControlPropertyChanged("UPDATE") );
        getSkinnable().ledHeightProperty().addListener(o -> handleControlPropertyChanged("UPDATE") );
        getSkinnable().frameVisibleProperty().addListener(o -> handleControlPropertyChanged("STYLE") );
        getSkinnable().frameDesignProperty().addListener(o -> handleControlPropertyChanged("STYLE") );
        getSkinnable().frameCustomPathProperty().addListener(o -> handleControlPropertyChanged("STYLE") );
        getSkinnable().frameBaseColorProperty().addListener(o -> handleControlPropertyChanged("STYLE") );
        
        getSkinnable().getStyleClass().addListener((ListChangeListener.Change change) -> {
            resize();
            createContents();
        });
        getSkinnable().getContents().addListener((ListChangeListener.Change change) -> {
            while(change.next()){
                if(change.wasAdded() || change.wasRemoved()){
                    createContents();
                }
            }
        });
        getSkinnable().getContents().stream().forEach((Content t)->{
            final int iContent=getSkinnable().getContents().indexOf(t);
            t.colorProperty().addListener((o)-> handleContentPropertyChanged("CREATE",iContent)); 
            t.originProperty().addListener((o)-> handleContentPropertyChanged("CREATE",iContent)); 
            t.areaProperty().addListener((o)-> handleContentPropertyChanged("CREATE",iContent)); 
            t.bmpNameProperty().addListener((o)-> {
                if(byteAreas!=null && byteAreas.size()>iContent && byteAreas.get(iContent)!=null){
                    byteAreas.set(iContent,null);
                }
                handleContentPropertyChanged("CREATE",iContent);
            }); 
            t.txtContentProperty().addListener((o)-> handleContentPropertyChanged("CREATE",iContent)); 
            t.matrixFontProperty().addListener((o)-> handleContentPropertyChanged("CREATE",iContent)); 
            t.fontGapProperty().addListener((o)-> handleContentPropertyChanged("CREATE",iContent)); 
            t.txtAlignProperty().addListener((o)-> handleContentPropertyChanged("CREATE",iContent)); 
            t.effectProperty().addListener((o)->handleContentPropertyChanged("ANIMATE",iContent)); 
            t.pauseProperty().addListener((o)->handleContentPropertyChanged("ANIMATE",iContent)); 
            t.postEffectProperty().addListener((o)->handleContentPropertyChanged("ANIMATE",iContent)); 
            t.lapseProperty().addListener((o)->handleContentPropertyChanged("ANIMATE",iContent)); 
            t.clearProperty().addListener((o)->handleContentPropertyChanged("ANIMATE",iContent)); 
            t.orderProperty().addListener((o)->handleContentPropertyChanged("PAIRS",iContent)); 
        });
    }

    protected void handleControlPropertyChanged(final String PROPERTY) {    
        switch (PROPERTY) {
            case "UPDATE":
                setDots();
                setSize();
                resize();
                createContents();  
                break;
            case "STYLE":
                setStyle();
                gradient();
                break;
            case "RESIZE":
                resize();
                break;
            case "PREF_SIZE":
                aspectRatio = getSkinnable().getPrefHeight() / getSkinnable().getPrefWidth();
                break;
        }
    }
    
    protected void handleContentPropertyChanged(final String PROPERTY, final int iContent) {    
        switch (PROPERTY) {
            case "CREATE":
                Platform.runLater(() -> {
                    initializeContent(iContent);
                    animContent(iContent);
                    Anim.get(iContent).start();
                });
                break;
            case "ANIMATE":
                Platform.runLater(() -> {
                    animContent(iContent);
                    Anim.get(iContent).start();
                });
                break;
            case "PAIRS":
                checkPairs();
                Anim.stream().forEach((a) -> a.stop() );
                Platform.runLater(() -> {
                    getSkinnable().getContents().stream().forEach(c->{
                        animContent(getSkinnable().getContents().indexOf(c));
                    });     
                    Anim.stream().forEach((a) -> a.start() );
                });
        }
    }
    
    @Override protected double computeMinWidth(final double HEIGHT, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        return super.computeMinWidth(Math.max(MINIMUM_HEIGHT, HEIGHT - TOP_INSET - BOTTOM_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }
    @Override protected double computeMinHeight(final double WIDTH, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        return super.computeMinHeight(Math.max(MINIMUM_WIDTH, WIDTH - LEFT_INSET - RIGHT_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }

    @Override protected double computeMaxWidth(final double HEIGHT, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        return super.computeMaxWidth(Math.min(MAXIMUM_HEIGHT, HEIGHT - TOP_INSET - BOTTOM_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }
    @Override protected double computeMaxHeight(final double WIDTH, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        return super.computeMaxHeight(Math.min(MAXIMUM_WIDTH, WIDTH - LEFT_INSET - RIGHT_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }

    @Override protected double computePrefWidth(final double HEIGHT, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        double prefHeight = PREFERRED_HEIGHT;
        if (HEIGHT != -1) {
            prefHeight = Math.max(0, HEIGHT - TOP_INSET - BOTTOM_INSET);
        }
        return super.computePrefWidth(prefHeight, TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }
    @Override protected double computePrefHeight(final double WIDTH, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        double prefWidth = PREFERRED_WIDTH;
        if (WIDTH != -1) {
            prefWidth = Math.max(0, WIDTH - LEFT_INSET - RIGHT_INSET);
        }
        return super.computePrefHeight(prefWidth, TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }
    
    private void gradient(){
        width  = getSkinnable().getWidth();
        height = getSkinnable().getHeight();
        if(getSkinnable().isFrameVisible()){
            Platform.runLater(()->{
                Image image = null;   
                switch (getSkinnable().getFrameDesign()) {
                    case BLACK_METAL:
                        ConicalGradient bmGradient = new ConicalGradient(new Point2D(width/2d,height/2d),
                                                                     new Stop(0.0000, Color.rgb(254, 254, 254)),
                                                                     new Stop(0.1250, Color.rgb(0, 0, 0)),
                                                                     new Stop(0.3472, Color.rgb(153, 153, 153)),
                                                                     new Stop(0.5000, Color.rgb(0, 0, 0)),
                                                                     new Stop(0.6805, Color.rgb(153, 153, 153)),
                                                                     new Stop(0.8750, Color.rgb(0, 0, 0)),
                                                                     new Stop(1.0000, Color.rgb(254, 254, 254)));
                        image = bmGradient.apply(new Rectangle(width,height)).getImage();
                        break;
                    case CHROME:
                        ConicalGradient cmGradient = new ConicalGradient(new Point2D(width/2d,height/2d),
                                                                     new Stop(0.00, Color.WHITE),
                                                                     new Stop(0.09, Color.WHITE),
                                                                     new Stop(0.12, Color.rgb(136, 136, 138)),
                                                                     new Stop(0.16, Color.rgb(164, 185, 190)),
                                                                     new Stop(0.25, Color.rgb(158, 179, 182)),
                                                                     new Stop(0.29, Color.rgb(112, 112, 112)),
                                                                     new Stop(0.33, Color.rgb(221, 227, 227)),
                                                                     new Stop(0.38, Color.rgb(155, 176, 179)),
                                                                     new Stop(0.48, Color.rgb(156, 176, 177)),
                                                                     new Stop(0.52, Color.rgb(254, 255, 255)),
                                                                     new Stop(0.63, Color.WHITE),
                                                                     new Stop(0.68, Color.rgb(156, 180, 180)),
                                                                     new Stop(0.80, Color.rgb(198, 209, 211)),
                                                                     new Stop(0.83, Color.rgb(246, 248, 247)),
                                                                     new Stop(0.87, Color.rgb(204, 216, 216)),
                                                                     new Stop(0.97, Color.rgb(164, 188, 190)),
                                                                     new Stop(1.00, Color.WHITE));
                        image = cmGradient.apply(new Rectangle(width,height)).getImage();
                        break;
                    case SHINY_METAL:
                        Color c=getSkinnable().getFrameBaseColor();
                        ConicalGradient smGradient = new ConicalGradient(new Point2D(width/2d,height/2d),
                                                                 new Stop(0.0000, Color.rgb(254, 254, 254)),
                                                                 new Stop(0.1250, Util.darker(c, 0.15)),
                                                                 new Stop(0.2500, c.darker()),
                                                                 new Stop(0.3472, c.brighter()),
                                                                 new Stop(0.5000, c.darker().darker()),
                                                                 new Stop(0.6527, c.brighter()),
                                                                 new Stop(0.7500, c.darker()),
                                                                 new Stop(0.8750, Util.darker(c, 0.15)),
                                                                 new Stop(1.0000, Color.rgb(254, 254, 254)));
                        image = smGradient.apply(new Rectangle(width,height)).getImage();
                        break;
                    case CUSTOM_DESIGN:
                        // load image
                        String pathJpg=getSkinnable().getFrameCustomPath();
                        // 1. from jar or url
                        if(pathJpg!=null && (pathJpg.contains("http") || pathJpg.startsWith("/"))){
                            image=new Image(pathJpg);
                        }
                        // 2. from local file
                        else if(pathJpg!=null){
                            File file = new File(pathJpg);
                            if(file.exists()){
                                image=new Image(file.toURI().toString());
                            } else {
                                //3. from resource
                                image=new Image(MatrixPanel.class.getResource(pathJpg).toExternalForm());
                            }
                        }
                        break;
                }
                if(image!=null){
                    fillFrame=new Background(new BackgroundImage(image, 
                                            BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, 
                                            BackgroundPosition.CENTER, new BackgroundSize(width,height,false,false,false,true)));
                }
                resize();
            });
        } else {
            resize();
        }
    }
    private void resize() {
        width  = getSkinnable().getWidth();
        height = getSkinnable().getHeight();
        if (aspectRatio * width > height) {
            width = 1 / (aspectRatio / height);
        } else if (1 / (aspectRatio / height) > width) {
            height = aspectRatio * width;
        }

        if (width > 0 && height > 0) {
            double size=Math.min(width,height);
            main.setPrefSize(width, height);            
            mainFrameOut.setPrefSize(width, height);
            mainFrame.setPrefSize(width, height);
            mainFrameIn.setPrefSize(width, height);
            if(getSkinnable().isFrameVisible()){
                mainFrameOut.setStyle("-fx-background-radius: "+(0.09333333333*size/2d)+";");
                switch (getSkinnable().getFrameDesign()) {
                    case GLOSSY_METAL:
                        mainFrame.setStyle("-fx-background-radius: "+(0.08*size/2d)+", "+(0.05*size/2d+1d)+", "+(0.05*size/2d)+";"+
                                           "-fx-background-insets: 1, "+(0.0841121495 *size-2d)+", "+(0.0841121495 *size)+";");
                        break;
                    case DARK_GLOSSY:
                        mainFrame.setStyle("-fx-background-radius: "+(0.08*size/2d)+", "+(0.08*size/2d)+", "+(0.05*size/2d+1d)+", "+(0.05*size/2d)+";"+
                                           "-fx-background-insets: 1, 1, "+(0.0841121495 *size-2d)+", "+(0.0841121495 *size)+";");
                        break;
                    case BLACK_METAL:
                    case CHROME:
                    case SHINY_METAL:
                    case CUSTOM_DESIGN:
                        Rectangle rectFrame=new Rectangle(width,height);
                        rectFrame.setArcHeight(0.08*size); rectFrame.setArcWidth(0.08*size);
                        if(fillFrame!=null){
                            Platform.runLater(()-> mainFrame.setBackground(fillFrame) );
                        }
                        mainFrame.setShape(rectFrame);
                        mainFrameIn.setStyle("-fx-background-radius: "+(0.06*size/2d+1d)+", "+(0.05*size/2d)+";"+
                                           "-fx-background-insets: "+(0.0841121495 *size-2d)+", "+(0.0841121495 *size)+";");
                        break;
                    default:
                        Rectangle rect=new Rectangle(width,height);
                        rect.setArcHeight(0.08*size); rect.setArcWidth(0.08*size);
                        mainFrame.setShape(rect);
                        mainFrame.setStyle("-fx-background-image: url(\""+jpgFrame+"\");\n" +
                                           "-fx-background-position: center center;\n" +
                                           "-fx-background-repeat: no-repeat;\n" +
                                           "-fx-background-size: cover");
                        mainFrameIn.setStyle("-fx-background-radius: "+(0.06*size/2d+1d)+", "+(0.05*size/2d)+";"+
                                           "-fx-background-insets: "+(0.0841121495 *size-2d)+", "+(0.0841121495 *size)+";");
                        break;
                }
            } else {
                mainFrame.setStyle("-fx-background-image: null; -fx-background-color: null;");
                mainFrameIn.setStyle("-fx-background-radius: "+(0.06*size/2d+1d)+", "+(0.05*size/2d)+";"+
                                     "-fx-background-insets: "+(0.0841121495 *size-2d)+", "+(0.0841121495 *size)+";");
            }
            radio=(width-2d*(0.0841121495*size+5d))/(3d*getSkinnable().ledWidthProperty().doubleValue()+1);
            double gapH = (height-2d*(0.0841121495*size+5d)-2d*radio*getSkinnable().ledHeightProperty().doubleValue())/(getSkinnable().ledHeightProperty().doubleValue()+1);
            for (int i = 0; i < getSkinnable().ledHeightProperty().intValue(); i++) {
                for (int j = 0; j < getSkinnable().ledWidthProperty().intValue(); j++) {
                    Circle c=(Circle)dots.getChildren().get(i*getSkinnable().ledWidthProperty().intValue()+j);
                    c.setTranslateX(0.0841121495 * size + 5d + 2d*radio + j * 3d * radio);
                    c.setTranslateY(0.0841121495 * size + 5d + gapH + radio + i * (gapH + 2d * radio));
                    c.setRadius(radio);
                }
            }
            
            mainForeground.setPrefSize(width-2d*(0.0841121495 * size + 2d), height-2d*(0.0841121495 * size + 2d));
            mainForeground.setTranslateX(0.0841121495 * size + 2d);
            mainForeground.setTranslateY(0.0841121495 * size + 2d);
            
        }
    }

    /***************
     *** CONTENT ***
     ***************/
    private final int LED_COLUMN    = 0;
    private final int LED_ROW       = 1;
    private final int LED_INTENSITY = 2;
    /*
     * full area required for each content, even not visible
     */
    private ArrayList fullAreas = null;
    /*
     * Contains collection of images bytes, to avoid reprocessing BMPs
     */
    private ArrayList byteAreas = null;
    /*
     * visible AREAS in the panel, one per content
     */
    private Rectangle[] visibleArea = null;
    /* 
     * PAIRS of contents in the same area
     */
    private ArrayList pairs=null;
    /*
     * ANIMATION of each content
     */
    private ArrayList Anim=null;
    
    public void createContents() {
        // stop previous animations, if any
        stop();
        // reset leds
        dotMap.values().stream().forEach((c) -> c.setFill(COLOR_OFF) );
        
        List contents=getSkinnable().getContents();
        
        if (0 == contents.size()) {
            return;
        }
        
        fullAreas = new ArrayList<>();
        byteAreas = new ArrayList<>();
        visibleArea = new Rectangle[contents.size()];
        Anim=new ArrayList<>();
        visibleContent=new SimpleBooleanProperty[contents.size()];
            
        checkPairs();
        
        /* Initialize and animate every Content */
        Platform.runLater(() -> {
            contents.stream().forEach(content -> {
                final int iContent=contents.indexOf(content);
                initializeContent(iContent);
                animContent(iContent);
            });
            Anim.stream().forEach((a) -> a.start());            
        });
    }
    
    private void initializeContent(int iContent){
        
        if(iContent();
        getSkinnable().getContents().stream().filter(c -> c.getOrder()==RotationOrder.FIRST)
        .forEach((Content content1) -> {
            getSkinnable().getContents().stream().filter(c -> c.getOrder()==RotationOrder.SECOND)
            .forEach((Content content2) -> {
                if(content1.getArea().getBoundsInLocal().equals(content2.getArea().getBoundsInLocal())){
                    pairs.add(new ContentPair(getSkinnable().getContents().indexOf(content1),
                                              getSkinnable().getContents().indexOf(content2)));
                }
            });
        });
    }
    private void stop(){
        if(Anim!=null){
            Anim.stream().forEach((a) -> a.stop() );
            Anim.clear();
            Anim=null;
        }
        if(fullAreas!=null){
            fullAreas.clear();
            fullAreas=null;
        }
    }
    
    private class Animation extends AnimationTimer{
        private long lastUpdate=0l;
        private boolean bBlink=false; // heartbit
        private int contBlink=0;
        private int iter=0;        
        private final int iContent;
        private Content content=null;
        private int oriX, oriY, endX, endY;
        private int areaWidth, areaHeight;
        private int contentWidth, contentHeight;
        private IntegerProperty posX, posY, posXIni, posYIni;
//        private int[][] contentArea=null;
        private int realLapse, advance, limX, limitBlink, iterLeds;
        private boolean isBlinkEffect;
        
        private LinkedHashMap brightLeds=null;
        private ArrayList arrBrightLeds=null;
        private IntegerProperty incrPos=null;
        
        private PauseTransition pTrans=null;
        
        public Animation(int iContent){
            
            this.iContent=iContent;
            this.content=getSkinnable().getContents().get(iContent);
            
            // bind posX/posY increment (1) to allow for pause time (0) for each content
            incrPos=new SimpleIntegerProperty(1);
            
            visibleContent[iContent]=new SimpleBooleanProperty(true); // SINGLE && FIRST
            if(content!=null && content.getOrder().equals(Content.RotationOrder.SECOND)){
                visibleContent[iContent].setValue(false);
            }
            
        }
        
        public void initAnimation(){
//            this.contentArea = fullAreas.get(iContent);            

            oriX = (int) visibleArea[iContent].getX();
            oriY = (int) visibleArea[iContent].getY();
            endX = (int) visibleArea[iContent].getWidth();
            endY = (int) visibleArea[iContent].getHeight();
            areaWidth = endX-oriX;
            areaHeight = endY-oriY;

            /*
            * Total dimensions of area of the content
            */
            if(fullAreas!=null && fullAreas.get(iContent)!=null){
                contentWidth =fullAreas.get(iContent)[0].length;
                contentHeight = fullAreas.get(iContent).length;
            }
            /*
            * START LOCATION OF CONTENT
            */
            posXIni= new SimpleIntegerProperty(0);
            posYIni = new SimpleIntegerProperty(0);
            if(content!=null && !content.getEffect().equals(Content.Effect.NONE)){
                // content at its final position
                posYIni.set(0);
                if(content.getTxtAlign().equals(Content.Align.LEFT)){
                    posXIni.set(0);
                    // SCROLL_RIGHT: +cW-cW, SCROLL_LEFT: -aW+aW=0, MIRROR: -cW/2+cW/2
                    limX=0; 
                } else if(content.getTxtAlign().equals(Content.Align.CENTER)){
                    posXIni.set(contentWidth/2-areaWidth/2);
                    //SCROLL_RIGHT: +cW-(aW/2+cW/2) SCROLL_LEFT: -aW+(aW/2+fW/2), MIRROR: -aW/2+cW/2
                    limX=-areaWidth/2+contentWidth/2; 
                } else if(content.getTxtAlign().equals(Content.Align.RIGHT)){
                    posXIni.set(contentWidth-areaWidth);
                    //SCROLL_RIGHT: +cW-aW, SCROLL_LEFT: -aW+cW=0, MIRROR: cW/2-aW + cW/2
                    limX=contentWidth-areaWidth; 
                }

                // moved first if neccessary to start the scrolling effect
                if (content.getEffect().equals(Content.Effect.SCROLL_RIGHT)){
                    // content to the left of the visible area
                    posXIni.set(contentWidth);
                } else if (content.getEffect().equals(Content.Effect.SCROLL_LEFT)){
                    // content to the right of the visible area
                    posXIni.set(-areaWidth);
                } else if (content.getEffect().equals(Content.Effect.SCROLL_UP)){
                    // content to the bottom of the visible area
                    posYIni.set(-areaHeight);
                } else if (content.getEffect().equals(Content.Effect.SCROLL_DOWN)){
                    // content to the top of the visible area
                    posYIni.set(contentHeight);
                } else if (content.getEffect().equals(Content.Effect.MIRROR)){
                    // content to the center of the visible area
                    if(content.getTxtAlign().equals(Content.Align.LEFT)){
                        posXIni.set(-contentWidth/2);
                    } else if(content.getTxtAlign().equals(Content.Align.CENTER)){
                        posXIni.set(0-areaWidth/2);
                    } else if(content.getTxtAlign().equals(Content.Align.RIGHT)){
                        posXIni.set(contentWidth/2-areaWidth);
                    }
                } 
            }
            // +1,-1 to make the translation ot the content, 0 to pause it
            posX = new SimpleIntegerProperty(posXIni.get());
            posY = new SimpleIntegerProperty(posYIni.get());
            
            // speed = gap of ms to refresh the matrixPanel
            realLapse = (content!=null && content.getLapse() >= 250)?content.getLapse():250;
            
            if(content!=null && content.getLapse()>0){
                // leds advance per step (int), lower lapse, bigger advance:
                advance=realLapse/content.getLapse(); 
                realLapse=advance*content.getLapse();
            }
            else{
                advance=10;
            }
            if(content!=null && !content.getEffect().equals(Content.Effect.NONE)){
            
                isBlinkEffect=(content.getEffect().equals(Content.Effect.BLINK) || 
                                content.getEffect().equals(Content.Effect.BLINK_4) ||
                                content.getEffect().equals(Content.Effect.BLINK_10));
                limitBlink=(content.getEffect().equals(Content.Effect.BLINK)?10000: 
                            (content.getEffect().equals(Content.Effect.BLINK_4)?7:
                             (content.getEffect().equals(Content.Effect.BLINK_10)?19:0)));

                /*
                 * Effect.SPRAY
                 */
                if(content.getEffect().equals(Content.Effect.SPRAY)){
                    brightLeds = new LinkedHashMap<>();
                    arrBrightLeds=new ArrayList<>();
                    int[][] contentArea= fullAreas.get(iContent); 
                    // list of brighting LEDs: column j, row i, intensity val
                    for (int i = oriY; i < endY; i++) {
                        for (int j = oriX; j < endX; j++) {
                            Integer dot = new Integer(j + i * getSkinnable().ledWidthProperty().intValue());
                            if (dotMap.get(dot) != null) {
                                int val;
                                if (j + posX.intValue() >= oriX && j + posX.intValue() < contentWidth + oriX &&
                                    i + posY.intValue() >= oriY && i + posY.intValue() < contentHeight + oriY) {
                                    val = contentArea[i + posY.intValue() - oriY][j + posX.intValue() - oriX];
                                    if(val>0){
                                        int[] led={j,i,val};
                                        arrBrightLeds.add(led);
                                    }
                                } 
                            }
                        }
                    }

                    // RANDOMIZE ArrayList 
                    Collections.shuffle(arrBrightLeds);

                    // Create map with shuffled list
                    final Iterator vIter = arrBrightLeds.iterator();
                    for (int k=0; k0){
                        if(content!=null && content.getLapse()>0){
                            iterLeds=brightLeds.size()/content.getLapse();
                        } else {
                            iterLeds=brightLeds.size()/10;
                        }
                    } else {
                        iterLeds=0;
                    }
                }
            }
        }
                
        @Override
        public void handle(long now) {
            /*
            *  only make one frame step animation IF enough fps, 
            *  the content is visible and it isn't in pause
            */
            if (now > lastUpdate + realLapse*1000000 && 
                content!=null && iContentcontentWidth/2){
                                        pos=-pos;
                                    } else if(content.getTxtAlign().equals(Content.Align.CENTER) && j-oriX>areaWidth/2d){
                                        pos=-pos-areaWidth+contentWidth;                                                
                                    } else if(content.getTxtAlign().equals(Content.Align.RIGHT) && j-oriX>-contentWidth/2+areaWidth){
                                        pos=-pos+2*(contentWidth-areaWidth);
                                    }                                            
                                }

                                int val = 0;
                                if (j + pos >= oriX && j + pos < contentWidth + oriX &&
                                    i + posY.intValue() >= oriY && i + posY.intValue() < contentHeight + oriY) {
                                    val = contentArea[i + posY.intValue() - oriY][j + pos - oriX];
                                } 
                                if ((val > 0 && !isBlinkEffect) || (val>0 && isBlinkEffect && bBlink)) {
                                    final int toneB=val/100;
                                    final int toneG=(val-toneB*100)/10;
                                    final int toneR=(val-toneB*100-toneG*10);
                                    ((Circle)dotMap.get(dot)).setFill(Color.rgb(toneScale*toneR, toneScale*toneG, toneScale*toneB));
                                } else { 
                                    ((Circle)dotMap.get(dot)).setFill(COLOR_OFF);
                                }
                            }
                        }
                    }
                    contentArea=null;
                }
                /*
                 * INCREMENT TRASLATION OF CONTENT 
                 * CHECK END OF MOVEMENT
                 */
                boolean endRotation=false;

                if (content.getEffect().equals(Content.Effect.NONE)) {
                    endRotation=true;
                } else if (content.getEffect().equals(Content.Effect.SCROLL_RIGHT)) {
                    endRotation=(posX.intValue() <= limX); 
                    if(posX.intValue() - advance*incrPos.getValue() <= limX){ 
                        posX.set(limX);
                    }
                    else{
                        posX.set(posX.intValue() - advance*incrPos.getValue());
                    } 
                } else if (content.getEffect().equals(Content.Effect.SCROLL_LEFT) || 
                           content.getEffect().equals(Content.Effect.MIRROR)) {
                    endRotation=(posX.intValue() >= limX); 
                    if(posX.intValue() + advance*incrPos.getValue() >= limX){ 
                        posX.set(limX);
                    }
                    else{
                        posX.set(posX.intValue() + advance*incrPos.getValue());
                    }                    
                } else if (content.getEffect().equals(Content.Effect.SCROLL_DOWN)) {
                    endRotation = (posY.intValue() <= 0); // fullHeight-fullHeight
                    if(posY.intValue() - advance*incrPos.getValue() <= 0){ 
                        posY.set(0);
                    }
                    else{
                        posY.set(posY.intValue() - advance*incrPos.getValue());
                    }
                } else if (content.getEffect().equals(Content.Effect.SCROLL_UP)) {
                    endRotation = (posY.intValue() >= 0); // -areaHeight+areaHeight
                    if(posY.intValue() + advance*incrPos.getValue() >= 0){ 
                        posY.set(0);
                    }
                    else{
                        posY.set(posY.intValue() + advance*incrPos.getValue());
                    } 
                } else if (isBlinkEffect){
                    if(contBlink==limitBlink){
                        endRotation=true;
                        contBlink=-1;
                    } else if(incrPos.getValue()==1){ // not in pause time
                        endRotation=false;
                        contBlink+=1;
                        bBlink=!bBlink;                                
                    }
                } else if (content.getEffect().equals(Content.Effect.SPRAY)) {             
                    if(iter>=brightLeds.size()-1){
                        endRotation=true;
                        iter=0;
                    }
                    else{
                        endRotation=false;
                    }
                }

                /*
                * POST EFFECT
                */
                if(endRotation) {

                    if(content.getPostEffect().equals(Content.PostEffect.STOP)) { 
//                        System.out.println("stop content "+iContent);
                        this.stop();
                    } else if(content.getPostEffect().equals(Content.PostEffect.REPEAT) || 
                              content.getPostEffect().equals(Content.PostEffect.PAUSE)){
                        posX.set(posXIni.get());
                        posY.set(posYIni.get());                               

                        incrPos.setValue(0);                               

                        /*
                        * PAUSE BETWEEN ROTATIONS
                        */
                        pTrans=new PauseTransition();
                        if(content.getPostEffect().equals(Content.PostEffect.REPEAT)){
                            pTrans.setDuration(Duration.millis(10));
                        } else{
                            pTrans.setDuration(Duration.millis(content.getPause()));
                        }
                        pTrans.setOnFinished((ActionEvent event) -> {
                            incrPos.setValue(1);
                            
                            // clear screen
                            if(content!=null && (content.getClear() || content.getEffect().equals(Content.Effect.SPRAY))){
                                for (int i = oriY; i < endY; i++) {
                                    for (int j = oriX; j < endX; j++) {
                                        Integer dot = new Integer(j + i * getSkinnable().ledWidthProperty().intValue());
                                        ((Circle)dotMap.get(dot)).setFill(COLOR_OFF);
                                    }
                                }
                            }
                            
                            if(content!=null && !content.getOrder().equals(Content.RotationOrder.SINGLE)){
                                // at the end of the content display, allow paired content to be displayed
                                for(ContentPair pair: pairs){
                                    if(pair.isInPair(iContent)){
                                        visibleContent[pair.getFirstIndex()].setValue(!pair.isVisibleFirst());
                                        visibleContent[pair.getSecondIndex()].setValue(!pair.isVisibleSecond());
                                        pairs.get(pairs.indexOf(pair)).changeIndex();
                                        break;
                                    }                                
                                }
                            }
                        });                                        
                        pTrans.playFromStart();                                
                    }

                }
                //System.out.println((now-lastUpdate)/1000000);
                lastUpdate = now;
            }
        }
        
        @Override
        public void stop(){
            if(brightLeds!=null){
                brightLeds.clear();
                brightLeds=null;
            }
            if(arrBrightLeds!=null){
                arrBrightLeds.clear();
                arrBrightLeds=null;
            }
            if(pTrans!=null){
                pTrans.stop();
                pTrans=null;
            }
            content=null;
        }
        
    }
    private static class ContentPair {

        private int indexFirst;
        private int indexSecond;
        private boolean bVisibleFirst;
        
        public ContentPair(int index1, int index2) {
            indexFirst=index1;
            bVisibleFirst=true;
            indexSecond=index2; 
        }
        
        public void setFirstIndex(int index){
            indexFirst=index;
            bVisibleFirst=true;
        }
        public void setSecondIndex(int index){
            indexSecond=index;
        }
        
        public void changeIndex(){
            bVisibleFirst=!bVisibleFirst;
        }
        public int getFirstIndex() {
            return indexFirst;
        }
        public int getSecondIndex() {
            return indexSecond;
        }

        public boolean isVisibleFirst(){
            return bVisibleFirst;
        }
        
        public boolean isVisibleSecond(){
            return !bVisibleFirst;
        }
        private boolean isInPair(int iContent) {
            return (indexFirst==iContent || indexSecond==iContent);
        }
        
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy