/*
* 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.view.jfx;
import java.util.List;
import javafx.beans.value.ChangeListener;
import javafx.collections.ListChangeListener;
import net.sf.latexdraw.models.interfaces.shape.IFreehand;
import net.sf.latexdraw.models.interfaces.shape.IPoint;
import org.eclipse.jdt.annotation.NonNull;
/**
* The JFX view of a freehand model.
* @author Arnaud Blouin
*/
public class ViewFreeHand extends ViewPathShape<IFreehand> {
private final ChangeListener<Object> update = (observable, oldValue, newValue) -> setPath();
/**
* Creates the view.
* @param sh The model.
*/
ViewFreeHand(final @NonNull IFreehand sh) {
super(sh);
model.getPoints().addListener((ListChangeListener.Change<? extends IPoint> c) -> setPath());
// To update on translation. To improve.
if(!model.getPoints().isEmpty()) {
model.getPoints().get(model.getPoints().size() - 1).xProperty().addListener(update);
model.getPoints().get(model.getPoints().size() - 1).yProperty().addListener(update);
}
model.intervalProperty().addListener(update);
model.typeProperty().addListener(update);
model.openProperty().addListener(update);
setPath();
}
private void setPath() {
border.getElements().clear();
shadow.getElements().clear();
if(model.getNbPoints() > 1) {
switch(model.getType()) {
case CURVES:
setPathCurves();
break;
case LINES:
setPathLines();
break;
}
if(!model.isOpen()) {
border.getElements().add(ViewFactory.INSTANCE.createClosePath());
}
shadow.getElements().addAll(border.getElements());
}
}
/**
* Fills the path of curves.
*/
private void setPathCurves() {
final int interval = model.getInterval();
final List<IPoint> pts = model.getPoints();
final int size = pts.size();
double prevx = pts.get(size - 1).getX();
double prevy = pts.get(size - 1).getY();
double curx = pts.get(0).getX();
double cury = pts.get(0).getY();
double midx = (curx + prevx) / 2d;
double midy = (cury + prevy) / 2d;
int i;
double x1;
double x2;
double y1;
double y2;
border.getElements().add(ViewFactory.INSTANCE.createMoveTo(curx, cury));
// Starting the drawing of the shape with a line.
if(size > interval) {
prevx = curx;
prevy = cury;
curx = pts.get(interval).getX();
cury = pts.get(interval).getY();
midx = (curx + prevx) / 2d;
midy = (cury + prevy) / 2d;
border.getElements().add(ViewFactory.INSTANCE.createLineTo(midx, midy));
}
// Adding curves
for(i = interval * 2; i < size; i += interval) {
x1 = (midx + curx) / 2d;
y1 = (midy + cury) / 2d;
prevx = curx;
prevy = cury;
curx = pts.get(i).getX();
cury = pts.get(i).getY();
midx = (curx + prevx) / 2d;
midy = (cury + prevy) / 2d;
x2 = (prevx + midx) / 2d;
y2 = (prevy + midy) / 2d;
border.getElements().add(ViewFactory.INSTANCE.createCubicCurveTo(x1, y1, x2, y2, midx, midy));
}
// If it remains not used points.
if(i - interval + 1 < size) {
x1 = (midx + curx) / 2d;
y1 = (midy + cury) / 2d;
prevx = curx;
prevy = cury;
curx = pts.get(size - 1).getX();
cury = pts.get(size - 1).getY();
midx = (curx + prevx) / 2d;
midy = (cury + prevy) / 2d;
x2 = (prevx + midx) / 2d;
y2 = (prevy + midy) / 2d;
border.getElements().add(ViewFactory.INSTANCE.createCubicCurveTo(x1, y1, x2, y2, pts.get(size - 1).getX(), pts.get(size - 1).getY()));
}
}
/**
* Fills the path of lines.
*/
private void setPathLines() {
final int interval = model.getInterval();
final List<IPoint> pts = model.getPoints();
final int size = pts.size();
IPoint pt = pts.get(0);
int i;
border.getElements().add(ViewFactory.INSTANCE.createMoveTo(pt.getX(), pt.getY()));
for(i = interval; i < size; i += interval) {
pt = pts.get(i);
border.getElements().add(ViewFactory.INSTANCE.createLineTo(pt.getX(), pt.getY()));
}
if(i - interval < size) {
border.getElements().add(ViewFactory.INSTANCE.createLineTo(pts.get(size - 1).getX(), pts.get(size - 1).getY()));
}
}
@Override
public void flush() {
if(!border.getElements().isEmpty()) {
model.getPoints().get(model.getPoints().size() - 1).xProperty().removeListener(update);
model.getPoints().get(model.getPoints().size() - 1).yProperty().removeListener(update);
}
model.intervalProperty().removeListener(update);
model.typeProperty().removeListener(update);
model.openProperty().removeListener(update);
super.flush();
}
}