Skip to content

Commit

Permalink
Add polyline getter and example program
Browse files Browse the repository at this point in the history
  • Loading branch information
stribor14 committed Mar 19, 2019
1 parent 2e9ef77 commit 6972068
Show file tree
Hide file tree
Showing 14 changed files with 956 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Don't track local Doxygen docs
html/
latex/
build*/
14 changes: 12 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
dist: xenial

sudo: false

git:
quiet: true

branches:
only:
- master

addons:
apt:
update: true
packages:
- doxygen
- qt5-default
- libeigen3-dev

script:
- doxygen Doxyfile
script: >-
qmake example/bezier_example.pro &&
make &&
doxygen Doxyfile
deploy:
provider: pages
Expand Down
40 changes: 36 additions & 4 deletions bezier.cpp → BezierCpp/bezier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ void Curve::resetCache()
cached_ext_points_.reset();
cached_bounding_box_tight_.reset();
cached_bounding_box_relaxed_.reset();
cached_polyline_.reset();
}

Curve::Coeffs Curve::bernsteinCoeffs() const
Expand Down Expand Up @@ -132,6 +133,39 @@ PointVector Curve::getControlPoints() const
return points;
}

PointVector Curve::getPolyline(double smoothness) const
{
if(!cached_polyline_ || smoothness != cached_polyline_smootheness_)
{
PointVector *polyline = new PointVector;
std::vector<std::shared_ptr<Curve>> subcurves;
subcurves.push_back(std::make_shared<Bezier::Curve>(this->getControlPoints()));
polyline->push_back(control_points_.row(0));
while(!subcurves.empty())
{
auto c = subcurves.back();
subcurves.pop_back();
auto cp = c->getControlPoints();
double hull = 0;
for(int k = 1; k < cp.size(); k++)
hull += (cp.at(k-1) - cp.at(k)).norm();
if (hull < smoothness * (cp.front() - cp.back()).norm())
{
polyline->push_back(cp.back());
}
else
{
auto split = c->splitCurve();
subcurves.push_back(std::make_shared<Bezier::Curve>(split.second));
subcurves.push_back(std::make_shared<Bezier::Curve>(split.first));
}
}
(const_cast<Curve*>(this))->cached_polyline_smootheness_ = smoothness;
(const_cast<Curve*>(this))->cached_polyline_.reset(polyline);
}
return *cached_polyline_;
}

void Curve::manipulateControlPoint(int index, Point point)
{
control_points_.row(index) = point;
Expand All @@ -140,10 +174,8 @@ void Curve::manipulateControlPoint(int index, Point point)

void Curve::manipulateCurvature(double t, Point point)
{
if (N_ > 4)
throw "Order of curve too big.";
if (N_ < 3)
throw "Not a curve.";
if (N_ < 3 || N_ > 4)
throw "Only quadratic and cubic curves can be manipulated";

double r = fabs((pow(t, N_ - 1) + pow(1 - t, N_ - 1) - 1) / (pow(t, N_ - 1) + pow(1 - t, N_ - 1)));
double u = pow(1 - t, N_ - 1) / (pow(t, N_ - 1) + pow(1 - t, N_ - 1));
Expand Down
63 changes: 36 additions & 27 deletions bezier.h → BezierCpp/bezier.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@
#include <memory>

/*!
* Nominal namespace
* Nominal namespace containing class definition and typedefs
*/
namespace Bezier
{
/*!
* \brief Point Point in xy plane
* \brief Point in xy plane
*/
typedef Eigen::Vector2d Point;
/*!
* \brief Vector in xy plane
* \brief A Vector in xy plane
*/
typedef Eigen::Vector2d Vec2;
/*!
Expand Down Expand Up @@ -76,6 +76,8 @@ class Curve : public std::enable_shared_from_this<Curve>
cached_bounding_box_tight_; /*! If generated, stores bounding box (use_roots = true) for later use */
std::shared_ptr<BBox>
cached_bounding_box_relaxed_; /*! If generated, stores bounding box (use_roots = false) for later use */
std::shared_ptr<PointVector> cached_polyline_; /*! If generated, stores polyline for later use */
double cached_polyline_smootheness_ = 0; /*! Smootheness of cached polyline */

/// Reset all privately cached data
inline void resetCache();
Expand All @@ -100,94 +102,101 @@ class Curve : public std::enable_shared_from_this<Curve>

public:
/*!
* \brief Curve Create Bezier curve
* \brief Create the Bezier curve
* \param points Nx2 matrix where each row is one of N control points that define the curve
*/
Curve(const Eigen::MatrixX2d& points);

/*!
* \brief Curve Create Bezier curve
* \brief Create the Bezier curve
* \param points A vector of control points that define the curve
*/
Curve(const PointVector& points);

/*!
* \brief getControlPoints Get control points
* \return a Vector of control points
* \brief Get the control points
* \return A vector of control points
*/
PointVector getControlPoints() const;

/*!
* \brief manipulateControlPoint Set new coordinates to a control point
* \brief Get a polyline representation of curve as a vector of points on curve
* \param smoothness smoothness factor > 1 (more resulting points when closer to 1)
* \return A vector of polyline vertices
*/
PointVector getPolyline(double smoothness = 1.001) const;

/*!
* \brief Set the new coordinates to a control point
* \param index Index of chosen control point
* \param point New control point
*/
void manipulateControlPoint(int index, Point point);

/*!
* \brief manipulateCurvature Manipulate curve so that it passes through wanted point for given 't'
* \brief Manipulate the curve so that it passes through wanted point for given 't'
* \param t Curve parameter
* \param point Point where curve should pass for given t
* \param point Point where curve should pass for a given t
*
* Only works for quadratic and cubic curves
* \warning Resets cached data
*/
void manipulateCurvature(double t, Point point);

/*!
* \brief elevateOrder Raise curve order by 1
* \brief Raise the curve order by 1
*
* Curve will always retain its shape
* \warning Resets cached data
*/
void elevateOrder();

/*!
* \brief lowerOrder Lower curve order by 1
* \brief Lower the curve order by 1
*
* If current shape cannot be described by lower order, it will be best aproximation
* \warning Resets cached data
*/
void lowerOrder();

/*!
* \brief valueAt Get point on curve for given t
* \brief Get the point on curve for a given t
* \param t Curve parameter
* \return Point on a curve for given t
* \return Point on a curve for a given t
*/
Point valueAt(double t) const;

/*!
* \brief curvatureAt Get curvature of curve for given t
* \brief Get curvature of curve for a given t
* \param t Curve parameter
* \return Curvature of a curve for given t
* \return Curvature of a curve for a given t
*/
double curvatureAt(double t) const;

/*!
* \brief tangentAt Get tangent of curve for given t
* \brief Get the tangent of curve for a given t
* \param t Curve parameter
* \param normalize If resulting tangent should be normalized
* \return Tangent of a curve for given t
* \param normalize If the resulting tangent should be normalized
* \return Tangent of a curve for a given t
*/
Vec2 tangentAt(double t, bool normalize = true) const;

/*!
* \brief normalAt Get normal of curve for given t
* \brief Get the normal of curve for a given t
* \param t Curve parameter
* \param normalize If resulting normal should be normalized
* \param normalize If the resulting normal should be normalized
* \return Normal of a curve for given t
*/
Vec2 normalAt(double t, bool normalize = true) const;

/*!
* \brief getDerivative Get derivative of a curve
* \brief Get the derivative of a curve
* \return Derivative curve
*/
Curve getDerivative() const;

/*!
* \brief getRoots Get extreme points of curve
* \brief Get the extreme points of curve
* \param step Size of step in coarse search
* \param epsilon Precision of resulting t
* \param max_iter Maximum number of iterations for Newton-Rhapson
Expand All @@ -196,21 +205,21 @@ class Curve : public std::enable_shared_from_this<Curve>
std::vector<Point> getRoots(double step = 0.1, double epsilon = 0.001, std::size_t max_iter = 15) const;

/*!
* \brief getBBox Get bounding box of curve
* \brief Get the bounding box of curve
* \param use_roots If algorithm should use extreme points
* \return Bounding box (if use_roots is false, returns the bounding box of control points)
*/
BBox getBBox(bool use_roots = true) const;

/*!
* \brief splitCurve Split curve into two subcurves
* \brief Split the curve into two subcurves
* \param z Parameter t at which to split the curve
* \return Pair of two subcurves
*/
std::pair<Curve, Curve> splitCurve(double z = 0.5) const;

/*!
* \brief getPointsOfIntersection Get points of intersection with another curve
* \brief Get the points of intersection with another curve
* \param curve Curve to intersect with
* \param stop_at_first If first point of intersection is enough
* \param epsilon Precision of resulting intersection
Expand All @@ -220,7 +229,7 @@ class Curve : public std::enable_shared_from_this<Curve>
double epsilon = 0.001) const;

/*!
* \brief projectPointOnCurve Get parameter t where curve is closes to given point
* \brief Get the parameter t where curve is closes to given point
* \param point Point to project on curve
* \param step Size of step in coarse search
* \return Parameter t
Expand Down
2 changes: 1 addition & 1 deletion Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.

INPUT = .
INPUT = ./BezierCpp/

# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
Expand Down
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Bezier-Cpp
[![Build Status](https://travis-ci.com/stribor14/Bezier-cpp.svg?branch=master)](https://travis-ci.com/stribor14/Bezier-cpp)

Fast and lightweight class for using the Bezier curves of any order in C++

*Algorithm implementations are based on [A Primer on Bezier Curves](https://pomax.github.io/bezierinfo/) by Pomax*
Expand All @@ -17,13 +19,28 @@ Fast and lightweight class for using the Bezier curves of any order in C++
- find points of intersection with another curve
- elevate/lower order
- manipulate control points
- manipulate dot on curve
- manipulate dot on curve (only for quadratic and cubic curves)

## Dependencies
- c++11
- Eigen3

*Add compile flag* `-march=native` *when compiling to use vectorization with Eigen.*

## Example program
A small Qt5 based program written as a playground for manipulating Bezier curves.
### Usage
- starts with two Bezier curves (with 4 and 5 control points respectively)
- Zoom in/out: *__Ctrl + mouse wheel__*
- Manipulate control point or point on curve: *__Left mouse buttom__*
- Project mouse pointer on all curves and show tangent: *__Right mouse buttom__*
- Split curve at mouse point: *__Middle mouse buttom__*
- Raise order of the curve: *__Double left click__*
- Lower order of the curve *__Double right click__*
- Toggle bounding boxes and curve intersections: *__Double middle click__*

### Additional dependencies
- qt5-default

## Licence
Apache License Version 2.0
48 changes: 48 additions & 0 deletions example/bezier_example.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#-------------------------------------------------
#
# Project created by QtCreator 2019-03-15T10:50:10
#
#-------------------------------------------------

QT += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = bezier_example
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0

CONFIG += c++11
QMAKE_CXXFLAGS += -march=native

INCLUDEPATH += /usr/include/eigen3 \
../BezierCpp

SOURCES += \
main.cpp \
mainwindow.cpp \
../BezierCpp/bezier.cpp \
qgraphicsviewzoom.cpp

HEADERS += \
mainwindow.h \
../BezierCpp/bezier.h \
qgraphicsviewzoom.h

FORMS += \
mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
Loading

0 comments on commit 6972068

Please sign in to comment.