/**********************************************
* Copyright (C) 2011 Lukas Laag
* This file is part of svgreal.
*
* svgreal 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 3 of the License, or
* (at your option) any later version.
*
* svgreal is distributed in the hope that it will be useful,
* but 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.
*
* You should have received a copy of the GNU General Public License
* along with svgreal. If not, see http://www.gnu.org/licenses/
**********************************************/
package org.vectomatic.svg.edit.client.command.path;
import org.vectomatic.dom.svg.OMSVGCircleElement;
import org.vectomatic.dom.svg.OMSVGDocument;
import org.vectomatic.dom.svg.OMSVGLineElement;
import org.vectomatic.dom.svg.OMSVGMatrix;
import org.vectomatic.dom.svg.OMSVGPathSeg;
import org.vectomatic.dom.svg.OMSVGPathSegCurvetoCubicAbs;
import org.vectomatic.dom.svg.OMSVGPoint;
import org.vectomatic.svg.edit.client.command.path.IPathRepOwner.Mode;
import com.google.gwt.dom.client.Element;
/**
* Class to represent a path cubic segment
* @author laaglu
*/
public class SVGCubicSegRep extends SVGSegRep {
protected OMSVGPathSegCurvetoCubicAbs cubicToSeg;
protected OMSVGCircleElement cp1;
protected OMSVGCircleElement cp2;
protected OMSVGLineElement tg1;
protected OMSVGLineElement tg2;
public SVGCubicSegRep(IPathRepOwner owner, OMSVGPathSegCurvetoCubicAbs cubicToSeg) {
super(owner);
this.cubicToSeg = cubicToSeg;
// Create the tangents
OMSVGDocument document = (OMSVGDocument) owner.getSvg().getOwnerDocument();
cp1 = document.createSVGCircleElement();
cp2 = document.createSVGCircleElement();
tg1 = document.createSVGLineElement();
tg2 = document.createSVGLineElement();
tangents.appendChild(tg1);
tangents.appendChild(tg2);
tangents.appendChild(cp1);
tangents.appendChild(cp2);
}
@Override
public OMSVGPathSeg getElement() {
return cubicToSeg;
}
@Override
public float getX() {
return cubicToSeg.getX();
}
@Override
public void setX(float x) {
cubicToSeg.setX(x);
}
@Override
public float getY() {
return cubicToSeg.getY();
}
@Override
public void setY(float y) {
cubicToSeg.setY(y);
}
@Override
public float getX1() {
return cubicToSeg.getX1();
}
@Override
public float getY1() {
return cubicToSeg.getY1();
}
@Override
public float getX2() {
return cubicToSeg.getX2();
}
@Override
public float getY2() {
return cubicToSeg.getY2();
}
@Override
public Element getCp1() {
return cp1.getElement();
}
@Override
public void setCp1(OMSVGPoint p, float hs) {
cubicToSeg.setX1(p.getX());
cubicToSeg.setY1(p.getY());
update(hs);
}
@Override
public Element getCp2() {
return cp2.getElement();
}
@Override
public void setCp2(OMSVGPoint p, float hs) {
cubicToSeg.setX2(p.getX());
cubicToSeg.setY2(p.getY());
update(hs);
}
@Override
public void update(float hs) {
float x = cubicToSeg.getX();
float y = cubicToSeg.getY();
vertex.getX().getBaseVal().setValue(x - hs);
vertex.getY().getBaseVal().setValue(y - hs);
vertex.getWidth().getBaseVal().setValue(hs * 2);
vertex.getHeight().getBaseVal().setValue(hs * 2);
if (owner.getMode() == Mode.TANGENT) {
float px = previous != null ? previous.getX() : 0;
float py = previous != null ? previous.getY() : 0;
float x1 = cubicToSeg.getX1();
float y1 = cubicToSeg.getY1();
float x2 = cubicToSeg.getX2();
float y2 = cubicToSeg.getY2();
tg1.getX1().getBaseVal().setValue(px);
tg1.getY1().getBaseVal().setValue(py);
tg1.getX2().getBaseVal().setValue(x1);
tg1.getY2().getBaseVal().setValue(y1);
tg2.getX1().getBaseVal().setValue(x);
tg2.getY1().getBaseVal().setValue(y);
tg2.getX2().getBaseVal().setValue(x2);
tg2.getY2().getBaseVal().setValue(y2);
cp1.getCx().getBaseVal().setValue(x1);
cp1.getCy().getBaseVal().setValue(y1);
cp1.getR().getBaseVal().setValue(hs);
cp2.getCx().getBaseVal().setValue(x2);
cp2.getCy().getBaseVal().setValue(y2);
cp2.getR().getBaseVal().setValue(hs);
}
}
@Override
public void updateStart(OMSVGPoint delta, float hs) {
cubicToSeg.setX1(cubicToSeg.getX1() + delta.getX());
cubicToSeg.setY1(cubicToSeg.getY1() + delta.getY());
update(hs);
}
@Override
public void updateEnd(OMSVGPoint delta, float hs) {
cubicToSeg.setX2(cubicToSeg.getX2() + delta.getX());
cubicToSeg.setY2(cubicToSeg.getY2() + delta.getY());
cubicToSeg.setX(cubicToSeg.getX() + delta.getX());
cubicToSeg.setY(cubicToSeg.getY() + delta.getY());
update(hs);
}
@Override
public void processMouseMove(OMSVGPoint delta, Element target, float hs, boolean isCtrlKeyDown) {
if (target == null || tg2.getElement() == target) {
updateEnd(delta, hs);
if (next != null) {
next.updateStart(delta, hs);
}
} else if (tg1.getElement() == target) {
updateStart(delta, hs);
if (previous != null) {
previous.updateEnd(delta, hs);
}
} else if (cp1.getElement() == target) {
SVGSegRep prevSeg = getPreviousSplineSeg();
Float angle = null;
if (isCtrlKeyDown && prevSeg != null) {
// Compute the angle between the tangent and the updated tangent
OMSVGPoint v1 = owner.getSvg().createSVGPoint(
cubicToSeg.getX1() - prevSeg.getX(),
cubicToSeg.getY1() - prevSeg.getY());
OMSVGPoint v2 = owner.getSvg().createSVGPoint(
cubicToSeg.getX1() + delta.getX() - prevSeg.getX(),
cubicToSeg.getY1() + delta.getY() - prevSeg.getY());
float d = v1.length() * v2.length();
if (d != 0) {
angle = (float)(Math.acos(v1.dotProduct(v2) / d) * 180 / Math.PI);
if (v1.crossProduct(v2) < 0) {
angle = 360 - angle;
}
}
}
cubicToSeg.setX1(cubicToSeg.getX1() + delta.getX());
cubicToSeg.setY1(cubicToSeg.getY1() + delta.getY());
update(hs);
if (angle != null) {
// Apply the same rotation to the next spline tangent
OMSVGMatrix m = owner.getSvg().createSVGMatrix();
m = m.translate(prevSeg.getX(), prevSeg.getY());
m = m.rotate(angle);
m = m.translate(-prevSeg.getX(), -prevSeg.getY());
OMSVGPoint p0 = owner.getSvg().createSVGPoint(
prevSeg.getX2(),
prevSeg.getY2());
OMSVGPoint p1 = p0.matrixTransform(m).substract(p0);
prevSeg.processMouseMove(p1, prevSeg.getCp2(), hs, false);
}
} else if (cp2.getElement() == target) {
SVGSegRep nextSeg = getNextSplineSeg();
Float angle = null;
if (isCtrlKeyDown && next != null) {
// Compute the angle between the tangent and the updated tangent
OMSVGPoint v1 = owner.getSvg().createSVGPoint(
cubicToSeg.getX2() - cubicToSeg.getX(),
cubicToSeg.getY2() - cubicToSeg.getY());
OMSVGPoint v2 = owner.getSvg().createSVGPoint(
cubicToSeg.getX2() + delta.getX() - cubicToSeg.getX(),
cubicToSeg.getY2() + delta.getY() - cubicToSeg.getY());
float d = v1.length() * v2.length();
if (d != 0) {
angle = (float)(Math.acos(v1.dotProduct(v2) / d) * 180 / Math.PI);
if (v1.crossProduct(v2) < 0) {
angle = 360 - angle;
}
}
}
cubicToSeg.setX2(cubicToSeg.getX2() + delta.getX());
cubicToSeg.setY2(cubicToSeg.getY2() + delta.getY());
update(hs);
if (angle != null) {
// Apply the same rotation to the next spline tangent
OMSVGMatrix m = owner.getSvg().createSVGMatrix();
m = m.translate(cubicToSeg.getX(), cubicToSeg.getY());
m = m.rotate(angle);
m = m.translate(-cubicToSeg.getX(), -cubicToSeg.getY());
OMSVGPoint p0 = owner.getSvg().createSVGPoint(
nextSeg.getX1(),
nextSeg.getY1());
OMSVGPoint p1 = p0.matrixTransform(m).substract(p0);
nextSeg.processMouseMove(p1, nextSeg.getCp1(), hs, false);
}
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("C ");
builder.append(cubicToSeg.getX1());
builder.append(",");
builder.append(cubicToSeg.getY1());
builder.append(" ");
builder.append(cubicToSeg.getX2());
builder.append(",");
builder.append(cubicToSeg.getY2());
builder.append(" ");
builder.append(cubicToSeg.getX());
builder.append(",");
builder.append(cubicToSeg.getY());
return builder.toString();
}
}