/*
* This file is part of LaTeXDraw.
* Copyright (c) 2005-2017 Arnaud BLOUIN
* LaTeXDraw is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later version.
* LaTeXDraw is distributed without any warranty; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package net.sf.latexdraw.models.impl;
import java.awt.geom.Rectangle2D;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import net.sf.latexdraw.models.ShapeFactory;
import net.sf.latexdraw.models.interfaces.shape.BorderPos;
import net.sf.latexdraw.models.interfaces.shape.Color;
import net.sf.latexdraw.models.interfaces.shape.FillingStyle;
import net.sf.latexdraw.models.interfaces.shape.IDot;
import net.sf.latexdraw.models.interfaces.shape.IGroup;
import net.sf.latexdraw.models.interfaces.shape.IPoint;
import net.sf.latexdraw.models.interfaces.shape.IShape;
import net.sf.latexdraw.models.interfaces.shape.ISquaredShape;
import net.sf.latexdraw.models.interfaces.shape.IStandardGrid;
import net.sf.latexdraw.models.interfaces.shape.LineStyle;
import net.sf.latexdraw.models.interfaces.shape.Position;
import net.sf.latexdraw.view.pst.PSTricksConstants;
/**
* This trait encapsulates the code of the group related to the support of the general shape's properties.
* @author Arnaud Blouin
*/
interface LGroupShape extends IGroup {
@Override
default void copy(final IShape sh) {
//TODO
}
@Override
default double getBorderGap() {
return isEmpty() ? 0d : getShapes().get(0).getBorderGap();
}
@Override
default double getDashSepBlack() {
return getShapes().stream().filter(sh -> sh.isLineStylable()).findFirst().map(sh -> sh.getDashSepBlack()).orElse(Double.NaN);
}
@Override
default double getDashSepWhite() {
return getShapes().stream().filter(sh -> sh.isLineStylable()).findFirst().map(sh -> sh.getDashSepWhite()).orElse(Double.NaN);
}
@Override
default double getDotSep() {
return getShapes().stream().filter(sh -> sh.isLineStylable()).findFirst().map(sh -> sh.getDotSep()).orElse(Double.NaN);
}
@Override
default IPoint getFullBottomRightPoint() {
final double gap = getBorderGap();
final IPoint br = getBottomRightPoint();
br.translate(gap, gap);
return br;
}
@Override
default IPoint getFullTopLeftPoint() {
final double gap = getBorderGap();
final IPoint tl = getTopLeftPoint();
tl.translate(-gap, -gap);
return tl;
}
@Override
default double getHeight() {
return isEmpty() ? 0d : getShapes().get(0).getHeight();
}
@Override
default int getNbPoints() {
return 0;
}
@Override
default ObservableList<IPoint> getPoints() {
return FXCollections.emptyObservableList();
}
@Override
default IPoint getPtAt(final int index) {
return null;
}
@Override
default double getWidth() {
return isEmpty() ? 0d : getShapes().get(0).getWidth();
}
@Override
default void scaleWithRatio(final double x, final double x2, Position x3, Rectangle2D x4) {
//TODO ?
}
@Override
default void setDashSepBlack(final double dash) {
getShapes().stream().filter(sh -> sh.isLineStylable()).forEach(sh -> sh.setDashSepBlack(dash));
}
@Override
default void setDashSepWhite(final double dash) {
getShapes().stream().filter(sh -> sh.isLineStylable()).forEach(sh -> sh.setDashSepWhite(dash));
}
@Override
default void setDotSep(final double dot) {
getShapes().stream().filter(sh -> sh.isLineStylable()).forEach(sh -> sh.setDotSep(dot));
}
@Override
default boolean shadowFillsShape() {
return getShapes().stream().anyMatch(sh -> sh.shadowFillsShape());
}
@Override
default void mirrorHorizontal(final IPoint origin) {
getShapes().forEach(sh -> sh.mirrorHorizontal(origin));
}
@Override
default void mirrorVertical(final IPoint origin) {
getShapes().forEach(sh -> sh.mirrorVertical(origin));
}
@Override
default void setThickness(final double thickness) {
getShapes().forEach(sh -> sh.setThickness(thickness));
}
@Override
default void scale(final double prevWidth, final double prevHeight, final Position pos, final Rectangle2D bound) {
if(getShapes().stream().anyMatch(sh -> sh instanceof ISquaredShape || sh instanceof IStandardGrid || sh instanceof IDot)) {
getShapes().forEach(sh -> sh.scaleWithRatio(prevWidth, prevHeight, pos, bound));
}else {
getShapes().forEach(sh -> sh.scale(prevWidth, prevHeight, pos, bound));
}
}
@Override
default double getThickness() {
return getShapes().stream().filter(sh -> sh.isThicknessable()).map(sh -> sh.getThickness()).findFirst().orElse(Double.NaN);
}
@Override
default double getFullThickness() {
return getShapes().stream().filter(sh -> sh.isThicknessable()).map(sh -> sh.getFullThickness()).findFirst().orElse(Double.NaN);
}
@Override
default boolean isColourable() {
return getShapes().stream().anyMatch(sh -> sh.isColourable());
}
@Override
default boolean isThicknessable() {
return getShapes().stream().anyMatch(sh -> sh.isThicknessable());
}
@Override
default boolean isShowPtsable() {
return getShapes().stream().anyMatch(sh -> sh.isShowPtsable());
}
@Override
default boolean isShowPts() {
return getShapes().stream().anyMatch(sh -> sh.isShowPtsable() && sh.isShowPts());
}
@Override
default void setShowPts(final boolean show) {
getShapes().stream().filter(sh -> sh.isShowPtsable()).forEach(sh -> sh.setShowPts(show));
}
@Override
default Color getLineColour() {
return isEmpty() ? PSTricksConstants.DEFAULT_LINE_COLOR : getShapes().get(0).getLineColour();
}
@Override
default boolean isLineStylable() {
return getShapes().stream().anyMatch(sh -> sh.isLineStylable());
}
@Override
default LineStyle getLineStyle() {
return getShapes().stream().filter(sh -> sh.isLineStylable()).map(sh -> sh.getLineStyle()).findFirst().orElse(LineStyle.SOLID);
}
@Override
default void setLineStyle(final LineStyle style) {
getShapes().stream().filter(sh -> sh.isLineStylable()).forEach(sh -> sh.setLineStyle(style));
}
@Override
default boolean isBordersMovable() {
return getShapes().stream().anyMatch(sh -> sh.isBordersMovable());
}
@Override
default BorderPos getBordersPosition() {
return getShapes().stream().filter(sh -> sh.isBordersMovable()).map(sh -> sh.getBordersPosition()).findFirst().orElse(BorderPos.INTO);
}
@Override
default void setBordersPosition(final BorderPos position) {
getShapes().stream().filter(sh -> sh.isLineStylable()).forEach(sh -> sh.setBordersPosition(position));
}
@Override
default void setLineColour(final Color lineColour) {
getShapes().stream().filter(sh -> sh.isLineStylable()).forEach(sh -> sh.setLineColour(lineColour));
}
@Override
default void setDbleBordCol(final Color colour) {
getShapes().stream().filter(sh -> sh.isLineStylable()).forEach(sh -> sh.setDbleBordCol(colour));
}
@Override
default Color getDbleBordCol() {
return getShapes().stream().filter(sh -> sh.hasDbleBord()).map(sh -> sh.getDbleBordCol()).findFirst().orElse(PSTricksConstants.DEFAULT_DOUBLE_COLOR);
}
@Override
default boolean hasDbleBord() {
return getShapes().stream().anyMatch(sh -> sh.isDbleBorderable() && sh.hasDbleBord());
}
@Override
default void setHasDbleBord(final boolean dbleBorders) {
getShapes().stream().filter(sh -> sh.isLineStylable()).forEach(sh -> sh.setHasDbleBord(dbleBorders));
}
@Override
default boolean isDbleBorderable() {
return getShapes().stream().anyMatch(sh -> sh.isDbleBorderable());
}
@Override
default void setDbleBordSep(final double dbleBorderSep) {
getShapes().stream().filter(sh -> sh.isLineStylable()).forEach(sh -> sh.setDbleBordSep(dbleBorderSep));
}
@Override
default double getDbleBordSep() {
return getShapes().stream().filter(sh -> sh.isDbleBorderable() && sh.hasDbleBord()).map(sh -> sh.getDbleBordSep()).findFirst().orElse(Double.NaN);
}
@Override
default boolean isShadowable() {
return getShapes().stream().anyMatch(sh -> sh.isShadowable());
}
@Override
default boolean hasShadow() {
return getShapes().stream().anyMatch(sh -> sh.isShadowable() && sh.hasShadow());
}
@Override
default void setHasShadow(final boolean shadow) {
getShapes().stream().filter(sh -> sh.isShadowable()).forEach(sh -> sh.setHasShadow(shadow));
}
@Override
default void setShadowSize(final double shadSize) {
getShapes().stream().filter(sh -> sh.isShadowable()).forEach(sh -> sh.setShadowSize(shadSize));
}
@Override
default double getShadowSize() {
return getShapes().stream().filter(sh -> sh.isShadowable() && sh.hasShadow()).map(sh -> sh.getShadowSize()).findFirst().orElse(Double.NaN);
}
@Override
default void setShadowAngle(final double shadAngle) {
getShapes().stream().filter(sh -> sh.isShadowable()).forEach(sh -> sh.setShadowAngle(shadAngle));
}
@Override
default double getShadowAngle() {
return getShapes().stream().filter(sh -> sh.isShadowable() && sh.hasShadow()).map(sh -> sh.getShadowAngle()).findFirst().orElse(Double.NaN);
}
@Override
default void setShadowCol(final Color colour) {
getShapes().stream().filter(sh -> sh.isShadowable()).forEach(sh -> sh.setShadowCol(colour));
}
@Override
default Color getShadowCol() {
return getShapes().stream().filter(sh -> sh.isShadowable() && sh.hasShadow()).
map(sh -> sh.getShadowCol()).findFirst().orElse(PSTricksConstants.DEFAULT_SHADOW_COLOR);
}
@Override
default FillingStyle getFillingStyle() {
return getShapes().stream().filter(sh -> sh.isInteriorStylable()).map(sh -> sh.getFillingStyle()).findFirst().orElse(FillingStyle.NONE);
}
@Override
default void setFillingStyle(final FillingStyle style) {
getShapes().stream().filter(sh -> sh.isInteriorStylable()).forEach(sh -> sh.setFillingStyle(style));
}
@Override
default void setFilled(final boolean filled) {
getShapes().stream().filter(sh -> sh.isFillable()).forEach(sh -> sh.setFilled(filled));
}
@Override
default boolean isFilled() {
return getShapes().stream().anyMatch(sh -> sh.isFillable() && sh.isFilled());
}
@Override
default boolean isFillable() {
return getShapes().stream().anyMatch(sh -> sh.isFillable());
}
@Override
default boolean isInteriorStylable() {
return getShapes().stream().anyMatch(sh -> sh.isInteriorStylable());
}
@Override
default void setFillingCol(final Color colour) {
getShapes().stream().filter(sh -> sh.isFillable()).forEach(sh -> sh.setFillingCol(colour));
}
@Override
default Color getFillingCol() {
return getShapes().stream().filter(sh -> sh.isFillable() && sh.isFilled()).
map(sh -> sh.getFillingCol()).findFirst().orElse(PSTricksConstants.DEFAULT_FILL_COLOR);
}
@Override
default void setHatchingsCol(final Color colour) {
getShapes().stream().filter(sh -> sh.isInteriorStylable()).forEach(sh -> sh.setHatchingsCol(colour));
}
@Override
default Color getHatchingsCol() {
return getShapes().stream().filter(sh -> sh.isInteriorStylable() && sh.getFillingStyle().isHatchings()).
map(sh -> sh.getHatchingsCol()).findFirst().orElse(PSTricksConstants.DEFAULT_HATCHING_COLOR);
}
@Override
default void setGradColStart(final Color colour) {
getShapes().stream().filter(sh -> sh.isInteriorStylable()).forEach(sh -> sh.setGradColStart(colour));
}
@Override
default Color getGradColStart() {
return getShapes().stream().filter(sh -> sh.isInteriorStylable() && sh.getFillingStyle().isGradient()).
map(sh -> sh.getGradColStart()).findFirst().orElse(PSTricksConstants.DEFAULT_GRADIENT_START_COLOR);
}
@Override
default void setGradColEnd(final Color colour) {
getShapes().stream().filter(sh -> sh.isInteriorStylable()).forEach(sh -> sh.setGradColEnd(colour));
}
@Override
default Color getGradColEnd() {
return getShapes().stream().filter(sh -> sh.isInteriorStylable() && sh.getFillingStyle().isGradient()).
map(sh -> sh.getGradColEnd()).findFirst().orElse(PSTricksConstants.DEFAULT_GRADIENT_END_COLOR);
}
@Override
default void setGradAngle(final double gradAngle) {
getShapes().stream().filter(sh -> sh.isInteriorStylable()).forEach(sh -> sh.setGradAngle(gradAngle));
}
@Override
default double getGradAngle() {
return getShapes().stream().filter(sh -> sh.isInteriorStylable() && sh.getFillingStyle().isGradient()).
map(sh -> sh.getGradAngle()).findFirst().orElse(Double.NaN);
}
@Override
default void setGradMidPt(final double gradMidPoint) {
getShapes().stream().filter(sh -> sh.isInteriorStylable()).forEach(sh -> sh.setGradMidPt(gradMidPoint));
}
@Override
default double getGradMidPt() {
return getShapes().stream().filter(sh -> sh.isInteriorStylable() && sh.getFillingStyle().isGradient()).
map(sh -> sh.getGradMidPt()).findFirst().orElse(Double.NaN);
}
@Override
default double getHatchingsAngle() {
return getShapes().stream().filter(sh -> sh.isInteriorStylable()).map(sh -> sh.getHatchingsAngle()).findFirst().orElse(Double.NaN);
}
@Override
default double getHatchingsSep() {
return getShapes().stream().filter(sh -> sh.isInteriorStylable()).map(sh -> sh.getHatchingsSep()).findFirst().orElse(Double.NaN);
}
@Override
default double getHatchingsWidth() {
return getShapes().stream().filter(sh -> sh.isInteriorStylable()).map(sh -> sh.getHatchingsWidth()).findFirst().orElse(Double.NaN);
}
@Override
default void setHatchingsAngle(final double hatchingsAngle) {
getShapes().stream().filter(sh -> sh.isInteriorStylable()).forEach(sh -> sh.setHatchingsAngle(hatchingsAngle));
}
@Override
default void setHatchingsSep(final double hatchingsSep) {
getShapes().stream().filter(sh -> sh.isInteriorStylable()).forEach(sh -> sh.setHatchingsSep(hatchingsSep));
}
@Override
default void setHatchingsWidth(final double hatchingsWidth) {
getShapes().stream().filter(sh -> sh.isInteriorStylable()).forEach(sh -> sh.setHatchingsWidth(hatchingsWidth));
}
@Override
default void translate(final double tx, final double ty) {
getShapes().forEach(sh -> sh.translate(tx, ty));
}
@Override
default void addToRotationAngle(final IPoint gravCentre, final double angle) {
final IPoint gc = getGravityCentre();
getShapes().forEach(sh -> sh.addToRotationAngle(gc, angle));
}
@Override
default void setRotationAngle(final double rotationAngle) {
getShapes().forEach(sh -> sh.setRotationAngle(rotationAngle));
}
@Override
default void rotate(final IPoint point, final double angle) {
getShapes().forEach(sh -> sh.rotate(point, angle));
}
@Override
default double getRotationAngle() {
return isEmpty() ? 0d : getShapes().get(0).getRotationAngle();
}
@Override
default IPoint getGravityCentre() {
return isEmpty() ? ShapeFactory.INST.createPoint() : getTopLeftPoint().getMiddlePoint(getBottomRightPoint());
}
@Override
default IPoint getBottomRightPoint() {
return getShapes().parallelStream().map(sh -> sh.getBottomRightPoint()).
reduce((p1, p2) -> ShapeFactory.INST.createPoint(Math.max(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()))).
orElse(ShapeFactory.INST.createPoint(Double.NaN, Double.NaN));
}
@Override
default IPoint getBottomLeftPoint() {
return getShapes().parallelStream().map(sh -> sh.getBottomLeftPoint()).
reduce((p1, p2) -> ShapeFactory.INST.createPoint(Math.min(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()))).
orElse(ShapeFactory.INST.createPoint(Double.NaN, Double.NaN));
}
@Override
default IPoint getTopLeftPoint() {
return getShapes().parallelStream().map(sh -> sh.getTopLeftPoint()).
reduce((p1, p2) -> ShapeFactory.INST.createPoint(Math.min(p1.getX(), p2.getX()), Math.min(p1.getY(), p2.getY()))).
orElse(ShapeFactory.INST.createPoint(Double.NaN, Double.NaN));
}
@Override
default IPoint getTopRightPoint() {
return getShapes().parallelStream().map(sh -> sh.getTopRightPoint()).
reduce((p1, p2) -> ShapeFactory.INST.createPoint(Math.max(p1.getX(), p2.getX()), Math.min(p1.getY(), p2.getY()))).
orElse(ShapeFactory.INST.createPoint(Double.NaN, Double.NaN));
}
@Override
default boolean hasHatchings() {
return getShapes().stream().anyMatch(sh -> sh.hasHatchings());
}
@Override
default boolean hasGradient() {
return getShapes().stream().anyMatch(sh -> sh.hasGradient());
}
}