From 2860cd0dfa7b000ddccbb3a308b14dade52531c6 Mon Sep 17 00:00:00 2001 From: mwienand Date: Thu, 21 Jun 2012 17:27:36 +0200 Subject: [PATCH] Adds cubic Bezier interpolation. --- .../examples/demos/CubicInterpolationExample.java | 81 +++++++++++++++++++ .../eclipse/gef4/geometry/planar/PolyBezier.java | 83 ++++++++++++++++++++ 2 files changed, 164 insertions(+), 0 deletions(-) create mode 100644 org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/CubicInterpolationExample.java diff --git a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/CubicInterpolationExample.java b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/CubicInterpolationExample.java new file mode 100644 index 0000000..b7aa35a --- /dev/null +++ b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/CubicInterpolationExample.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2011 itemis AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthias Wienand (itemis AG) - initial API and implementation + * + *******************************************************************************/ +package org.eclipse.gef4.geometry.examples.demos; + +import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample; +import org.eclipse.gef4.geometry.planar.IGeometry; +import org.eclipse.gef4.geometry.planar.Line; +import org.eclipse.gef4.geometry.planar.Path; +import org.eclipse.gef4.geometry.planar.Point; +import org.eclipse.gef4.geometry.planar.PolyBezier; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Display; + +public class CubicInterpolationExample extends AbstractIntersectionExample { + public static void main(String[] args) { + new CubicInterpolationExample("Cubic Interpolation Example"); + } + + public CubicInterpolationExample(String title) { + super(title); + } + + protected AbstractControllableShape createControllableShape1(Canvas canvas) { + return new AbstractControllableShape(canvas) { + @Override + public void createControlPoints() { + addControlPoint(new Point(100, 200)); + addControlPoint(new Point(150, 250)); + addControlPoint(new Point(200, 150)); + addControlPoint(new Point(250, 250)); + addControlPoint(new Point(300, 150)); + addControlPoint(new Point(350, 250)); + addControlPoint(new Point(400, 200)); + } + + @Override + public PolyBezier createGeometry() { + return PolyBezier.interpolateCubic(getControlPoints()); + } + + @Override + public void drawShape(GC gc) { + Path curve = createGeometry().toPath(); + gc.drawPath(new org.eclipse.swt.graphics.Path(Display + .getCurrent(), curve.toSWTPathData())); + } + }; + } + + protected AbstractControllableShape createControllableShape2(Canvas canvas) { + return new AbstractControllableShape(canvas) { + @Override + public void createControlPoints() { + } + + @Override + public IGeometry createGeometry() { + return new Line(new Point(), new Point(1, 1)); + } + + @Override + public void drawShape(GC gc) { + } + }; + } + + @Override + protected Point[] computeIntersections(IGeometry g1, IGeometry g2) { + return new Point[] {}; + } +} diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java index 77fd8d1..c91f632 100644 --- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java +++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.Arrays; import org.eclipse.gef4.geometry.euclidean.Angle; +import org.eclipse.gef4.geometry.utils.PointListUtils; /** * A {@link PolyBezier} is an {@link IPolyCurve} which consists of one or more @@ -24,6 +25,7 @@ public class PolyBezier extends AbstractGeometry implements IPolyCurve, ITranslatable, IScalable, IRotatable { + private static final double INTERPOLATION_CURVE_WIDTH_COEFFICIENT = 1d; private static final long serialVersionUID = 1L; private static BezierCurve[] copy(BezierCurve... beziers) { @@ -50,6 +52,87 @@ public class PolyBezier extends AbstractGeometry implements IPolyCurve, this.beziers = copy(beziers); } + /** + * @see #interpolateCubic(double, Point...) + * @param points + * @return {@link PolyBezier} with continuous {@link CubicCurve} segments + * through the given {@link Point}s. + */ + public static PolyBezier interpolateCubic(Point... points) { + return interpolateCubic(INTERPOLATION_CURVE_WIDTH_COEFFICIENT, points); + } + + /** + * Creates a {@link PolyBezier} with continuous {@link CubicCurve} segments + * through the given {@link Point}s. + * + * @param curveWidthCoefficient + * value in the range ]0;+Inf[ that adjusts the + * width of the curve. A value smaller than one sharpens the + * curve and a value greater than one thickens the curve. + * @param points + * @return {@link PolyBezier} with continuous {@link CubicCurve} segments + * through the given {@link Point}s. + */ + public static PolyBezier interpolateCubic(double curveWidthCoefficient, + Point... points) { + if (points == null || points.length < 2) { + // System.out.println(" interpolateQuadratic() => ()"); + // TODO: throw exception instead? + return new PolyBezier(); + } else if (points.length == 2) { + Point mid = points[0].getTranslated(points[1]).getScaled(0.5); + return new PolyBezier( + new CubicCurve(points[0], mid, mid, points[1])); + } + + /* + * Computes the control points for the cubic bezier curve. The algorithm + * is based on what has been published by Maxim Shemanarev for Polygons + * (http://www.antigrain.com/research/bezier_interpolation/index.html) + * with a modified calculation of the first and the last control points, + * so that it can be applied to Polylines. + */ + Point[] mids = new Point[points.length - 1]; + for (int i = 0; i < mids.length; i++) { + mids[i] = points[i].getTranslated(points[i + 1]).getScaled(0.5); + } + + Line[] lines = PointListUtils.toSegmentsArray(points, true); + Line[] handleLines = PointListUtils.toSegmentsArray(mids, true); + + Point[] handleAnchors = new Point[handleLines.length]; + for (int i = 0; i < handleLines.length; i++) { + double d0 = lines[i].getP1().getDistance(lines[i].getP2()); + double d1 = lines[i + 1].getP1().getDistance(lines[i + 1].getP2()); + handleAnchors[i] = handleLines[i].get(d0 / (d0 + d1)); + } + + for (int i = 0; i < handleLines.length; i++) { + handleLines[i].scale(curveWidthCoefficient, handleAnchors[i]); + handleLines[i].translate(points[i + 1].x - handleAnchors[i].x, + points[i + 1].y - handleAnchors[i].y); + } + + CubicCurve[] interpolation = new CubicCurve[handleLines.length]; + + interpolation[0] = new CubicCurve(points[0], handleLines[0].getP1(), + points[1], points[1]); + + interpolation[interpolation.length - 1] = new CubicCurve( + points[points.length - 2], + handleLines[handleLines.length - 2].getP2(), + points[points.length - 1], points[points.length - 1]); + + for (int i = 1; i < interpolation.length - 1; i++) { + interpolation[i] = new CubicCurve(points[i], + handleLines[i - 1].getP2(), handleLines[i].getP1(), + points[i + 1]); + } + + return new PolyBezier(interpolation); + } + public boolean contains(Point p) { for (BezierCurve c : beziers) { if (c.contains(p)) { -- 1.7.4.1