curve.cpp revision 29f466534f28f3e06f68e44ba8d11281baa1e312
#define __CURVE_C__
/** \file
* Routines for SPCurve and for NArtBpath arrays / Geom::PathVector in general.
*/
/*
* Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
* Johan Engelen
*
* Copyright (C) 2000 Lauris Kaplinski
* Copyright (C) 2000-2001 Ximian, Inc.
* Copyright (C) 2002 Lauris Kaplinski
* Copyright (C) 2008 Johan Engelen
*
* Released under GNU GPL
*/
#include <string.h>
#include "libnr/nr-point.h"
#include <libnr/n-art-bpath.h>
#include <libnr/nr-point-matrix-ops.h>
#include <libnr/nr-translate-ops.h>
#include <libnr/n-art-bpath-2geom.h>
#include <libnr/nr-convert2geom.h>
#include <cstring>
#include <string>
#define NO_CHECKS // define this to disable the checking for unequal paths in SPCurve, improves performance by a lot!
#ifndef NO_CHECKS
}
#endif
#ifndef NO_CHECKS
}
#endif
#ifndef NO_CHECKS
}
#else
#endif
}
#ifndef NO_CHECKS
if ( !a ) {
}
#else
void SPCurve::debug_check( char const *, bool) {
#endif
}
/* Constructors */
/**
* The returned curve's state is as if SPCurve::reset has just been called on it.
* \param length Initial number of NArtBpath elements allocated for bpath (including NR_END
* element).
* 2GEOMproof
*/
: _refcount(1),
_pathv(),
_end(0),
_substart(0),
_hascpt(false),
_posSet(false),
_moving(false),
_closed(false)
{
if (length <= 0) {
g_error("SPCurve::SPCurve called with invalid length parameter");
throw;
}
debug_check("SPCurve::SPCurve(guint length)", this);
}
: _refcount(1),
_end(0),
_length(0),
_substart(0),
_hascpt(false),
_posSet(false),
_moving(false),
_closed(false)
{
// temporary code to convert to _bpath as well:
for (; i > 0; i--)
break;
_substart = i;
debug_check("SPCurve::SPCurve(Geom::PathVector const& pathv)", this);
}
// * 2GEOMproof
SPCurve *
{
for (; i > 0; i--)
break;
return curve;
}
/**
* Convert NArtBpath object to SPCurve object.
*
* \return new SPCurve, or NULL if the curve was not created for some reason.
* 2GEOMproof
*/
SPCurve *
{
return curve;
}
// * 2GEOMproof
SPCurve *
{
c->moveto(p);
for (int i=3; i>=0; i--) {
}
c->closepath_current();
debug_check("SPCurve::new_from_rect", c);
return c;
}
// * 2GEOMproof
{
if (_bpath) {
}
}
/* Methods */
void
{
_hascpt = false;
_posSet = false;
_moving = false;
// temporary code to convert to _bpath as well:
if (_bpath) {
}
for (; i > 0; i--)
break;
_substart = i;
debug_check("SPCurve::set_pathvector", this);
}
/**
* Get pointer to bpath data. Don't keep this reference too long, because the path might change by another function.
*/
NArtBpath const *
{
debug_check("SPCurve::get_bpath", this);
return _bpath;
};
Geom::PathVector const &
SPCurve::get_pathvector() const
{
debug_check("SPCurve::get_pathvector", this);
return _pathv;
}
/**
*Returns index in bpath[] of NR_END element.
* remove for 2geom
*/
SPCurve::get_length() const
{
// g_message("SPCurve::get_length must be removed");
return _end;
}
/*
* Returns the number of segments of all paths summed
*/
SPCurve::get_segment_count() const
{
}
return nr;
}
/**
* Increase _refcount of curve.
*
* \todo should this be shared with other refcounting code?
*/
SPCurve *
{
_refcount += 1;
return this;
}
/**
* Decrease refcount of curve, with possible destruction.
*
* \todo should this be shared with other refcounting code?
*/
SPCurve *
{
_refcount -= 1;
if (_refcount < 1) {
delete this;
}
return NULL;
}
/**
* Add space for more paths in curve.
* This function has no meaning for 2geom representation, other than maybe for optimization issues (enlargening the vector for what is to come)
* 2GEOMproof
*/
void
{
g_return_if_fail(this != NULL);
g_return_if_fail(space > 0);
return;
if (space < SP_CURVE_LENSTEP)
}
/**
* Create new curve from its own bpath array.
* 2GEOMproof
*/
SPCurve *
{
}
/**
* Return new curve that is the concatenation of all curves in list.
* 2GEOMified
*/
SPCurve *
{
}
}
gint i;
break;
}
new_curve->_pathv.insert( new_curve->_pathv.end(), c->get_pathvector().begin(), c->get_pathvector().end() );
}
return new_curve;
}
/**
* Returns a list of new curves corresponding to the subpaths in \a curve.
* 2geomified
*/
GSList *
{
guint p = 0;
while (p < _end) {
gint i = 1;
i++;
l = g_slist_prepend(l, new_curve);
p += i;
pathnr++;
}
return l;
}
/**
* Transform all paths in curve, template helper.
*/
template<class M>
static void
{
switch (p->code) {
case NR_MOVETO:
case NR_MOVETO_OPEN:
case NR_LINETO: {
break;
}
case NR_CURVETO:
for (unsigned i = 1; i <= 3; ++i) {
p->setC(i, p->c(i) * m);
}
break;
default:
break;
}
}
}
/**
* Transform all paths in curve using matrix.
* 2GEOMified, can be deleted when completely 2geom
*/
void
{
debug_check("SPCurve::transform(NR::Matrix const &m)", this);
}
/**
* Transform all paths in curve using matrix.
*/
void
{
debug_check("SPCurve::transform(Geom::Matrix const &m)", this);
}
/**
* Transform all paths in curve using NR::translate.
* 2GEOMified, can be deleted when completely 2geom
*/
void
{
debug_check("SPCurve::transform(NR::translate const &m)", this);
}
/**
* Set curve to empty curve.
* 2GEOMified
*/
void
{
_end = 0;
_substart = 0;
_hascpt = false;
_posSet = false;
_moving = false;
_closed = false;
debug_check("SPCurve::reset", this);
}
/* Several consecutive movetos are ALLOWED */
/**
* Calls SPCurve::moveto() with point made of given coordinates.
*/
void
{
}
/**
* Calls SPCurve::moveto() with point made of given coordinates.
*/
void
{
moveto(from_2geom(p));
}
/**
* Perform a moveto to a point, thus starting a new subpath.
* 2GEOMified
*/
void
{
_hascpt = true;
_posSet = true;
_movePos = p;
// the output is not the same. This is because SPCurve *incorrectly* coaslesces multiple moveto's into one for NArtBpath.
// debug_check("SPCurve::moveto", this);
}
/**
* Calls SPCurve::lineto() with a point's coordinates.
*/
void
{
}
/**
* Calls SPCurve::lineto() with a point's coordinates.
*/
void
{
}
/**
* Adds a line to the current subpath.
* 2GEOMified
*/
void
{
if (_moving) {
/* fix endpoint */
_moving = false;
if ( Geom::LineSegment const *last_line_segment = dynamic_cast<Geom::LineSegment const *>( &(*it) )) {
}
} else if (_posSet) {
/* start a new segment */
ensure_space(2);
bp++;
bp++;
_end += 2;
_posSet = false;
_closed = false;
return;
} else {
/* add line */
ensure_space(1);
bp++;
_end++;
}
debug_check("SPCurve::lineto", this);
}
/**
* Calls SPCurve::curveto() with coordinates of three points.
*/
void
{
using Geom::X;
using Geom::Y;
}
/**
* Calls SPCurve::curveto() with coordinates of three points.
*/
void
{
using NR::X;
using NR::Y;
}
/**
* Adds a bezier segment to the current subpath.
* 2GEOMified
*/
void
{
if (_posSet) {
/* start a new segment */
ensure_space(2);
bp++;
bp++;
_end += 2;
_posSet = false;
_closed = false;
_pathv.back().appendNew<Geom::CubicBezier>( Geom::Point(x0,y0), Geom::Point(x1,y1), Geom::Point(x2,y2) );
} else {
/* add curve */
ensure_space(1);
bp++;
_end++;
else _pathv.back().appendNew<Geom::CubicBezier>( Geom::Point(x0,y0), Geom::Point(x1,y1), Geom::Point(x2,y2) );
}
debug_check("SPCurve::curveto", this);
}
/**
* Close current subpath by possibly adding a line between start and end.
* 2GEOMified
*/
void
{
/* We need at least moveto, curveto, end. */
{
}
}
// Inkscape always manually adds the closing line segment to SPCurve with a lineto.
// This lineto is removed in the writing function for NArtBpath,
// so when path is closed and the last segment is a lineto, the closing line segment must really be removed first!
// TODO: fix behavior in Inkscape!
if ( /*Geom::LineSegment const *line_segment = */ dynamic_cast<Geom::LineSegment const *>(&_pathv.back().back())) {
}
_closed = true;
_closed = false;
break;
}
}
/** \todo
* effic: Maintain a count of NR_MOVETO_OPEN's (e.g. instead of
* the closed boolean).
*/
_closed = false;
break;
}
}
_hascpt = false;
debug_check("SPCurve::closepath", this);
}
/** Like SPCurve::closepath() but sets the end point of the current
command to the subpath start point instead of adding a new lineto.
Used for freehand drawing when the user draws back to the start point.
2GEOMified
**/
void
{
/* We need at least moveto, curveto, end. */
{
}
// Inkscape always manually adds the closing line segment to SPCurve with a lineto.
// This lineto is removed in the writing function for NArtBpath,
// so when path is closed and the last segment is a lineto, the closing line segment must really be removed first!
// TODO: fix behavior in Inkscape!
if ( /*Geom::LineSegment const *line_segment = */ dynamic_cast<Geom::LineSegment const *>(&_pathv.back().back())) {
}
_closed = true;
_closed = false;
break;
}
}
/** \todo
* effic: Maintain a count of NR_MOVETO_OPEN's (e.g. instead of
* the closed boolean).
*/
_closed = false;
break;
}
}
_hascpt = false;
_moving = false;
debug_check("SPCurve::closepath_current", this);
}
/**
* True if no paths are in curve. If it only contains a path with only a moveto, the path is considered NON-empty
*/
bool
{
return empty;
}
/**
* True iff all subpaths are closed.
*/
bool
{
bool closed = true;
closed = false;
break;
}
}
return closed;
}
/**
* Return last subpath or NULL.
*/
NArtBpath const *
SPCurve::last_bpath() const
{
if (_end == 0) {
return NULL;
}
}
/**
* Return last pathsegment (possibly the closing path segment) in PathVector or NULL.
* equal in functionality to SPCurve::last_bpath()
*/
SPCurve::last_segment() const
{
if (is_empty()) {
return NULL;
}
return NULL;
}
}
/**
* Return last path in PathVector or NULL.
*/
{
if (is_empty()) {
return NULL;
}
}
/**
* Return first pathsegment in PathVector or NULL.
* equal in functionality to SPCurve::first_bpath()
*/
SPCurve::first_segment() const
{
if (is_empty()) {
return NULL;
}
return NULL;
}
}
/**
* Return first path in PathVector or NULL.
*/
SPCurve::first_path() const
{
if (is_empty()) {
return NULL;
}
}
/**
* Return first point of first subpath or (0,0). TODO: shouldn't this be (NR_HUGE, NR_HUGE) to be able to tell it apart from normal (0,0) ?
*/
SPCurve::first_point() const
{
if (is_empty())
}
/**
* Return the second point of first subpath or _movePos if curve too short.
* If the pathvector is empty, this returns (0,0). If the first path is only a moveto, this method
* returns the first point of the second path, if it exists. If there is no 2nd path, it returns the
* first point of the first path.
*
* FIXME: for empty paths shouldn't this return (NR_HUGE,NR_HUGE)
*/
SPCurve::second_point() const
{
if (is_empty()) {
}
// first path is only a moveto
// check if there is second path
} else {
return _pathv[0].initialPoint();
}
}
else
}
/**
* Return the second-last point of last subpath or _movePos if curve too short.
*/
SPCurve::penultimate_point() const
{
if (_end < 2) {
return _movePos;
}
return from_2geom(p);
}
/**
* Return last point of last subpath or (0,0). TODO: shouldn't this be (NR_HUGE, NR_HUGE) to be able to tell it apart from normal (0,0) ?
* If the last path is only a moveto, then return that point.
*/
SPCurve::last_point() const
{
if (is_empty())
}
inline static bool
is_moveto(NRPathcode const c)
{
return c == NR_MOVETO || c == NR_MOVETO_OPEN;
}
/**
* Returns a *new* \a curve but drawn in the opposite direction.
* Should result in the same shape, but
* with all its markers drawn facing the other direction.
* Reverses the order of subpaths as well
* 2GEOMified
**/
SPCurve *
SPCurve::create_reverse() const
{
/* We need at least moveto, curveto, end. */
case NR_MOVETO:
/* FALL-THROUGH */
case NR_MOVETO_OPEN:
return new_curve;
}
break;
case NR_LINETO:
break;
case NR_CURVETO:
break;
default:
}
}
}
/**
* Append \a curve2 to \a this.
* If \a use_lineto is false, simply add all paths in \a curve2 to \a this;
* if \a use_lineto is true, combine \a this's last path and \a curve2's first path and add the rest of the paths in \a curve2 to \a this.
* 2GEOMified
*/
void
bool use_lineto)
{
return;
return;
case NR_MOVETO_OPEN:
if (use_lineto && _hascpt) {
use_lineto = false;
} else {
}
closed = false;
break;
case NR_MOVETO:
if (use_lineto && _hascpt) {
use_lineto = FALSE;
} else {
}
closed = true;
break;
case NR_LINETO:
break;
case NR_CURVETO:
break;
case NR_END:
}
}
if (closed) {
closepath();
}
debug_check("SPCurve::append", this);
/* 2GEOM code when code above is removed:
if (use_lineto) {
Geom::PathVector::const_iterator it = curve2->_pathv.begin();
if ( ! _pathv.empty() ) {
Geom::Path & lastpath = _pathv.back();
lastpath.appendNew<Geom::LineSegment>( (*it).initialPoint() );
lastpath.append( (*it) );
} else {
_pathv.push_back( (*it) );
}
for (it++; it != curve2->_pathv.end(); it++) {
_pathv.push_back( (*it) );
}
} else {
for (Geom::PathVector::const_iterator it = curve2->_pathv.begin(); it != curve2->_pathv.end(); it++) {
_pathv.push_back( (*it) );
}
}
*/
}
/**
* Append \a c1 to \a this with possible fusing of close endpoints.
* 2GEOMproof. Needs to be recoded when NArtBpath is no longer there. Right now, it applies the same changes to bpath and pathv depending on bpath
*/
SPCurve *
{
return this;
}
debug_check("SPCurve::append_continuous 11", this);
if (be) {
if ( bs
{
/** \todo
* fixme: Strictly we mess in case of multisegment mixed
*/
bool closed = false;
case NR_MOVETO_OPEN:
closed = false;
break;
case NR_MOVETO:
closed = true;
break;
case NR_LINETO:
break;
case NR_CURVETO:
break;
case NR_END:
}
}
} else {
}
} else {
}
debug_check("SPCurve::append_continuous", this);
return this;
}
/**
* Remove last segment of curve.
* (Only used once in /src/pen-context.cpp)
* 2GEOMified
*/
void
{
if ( is_empty() )
return;
if (_end > 0) {
_end -= 1;
if (_end > 0) {
{
_hascpt = true;
_posSet = true;
_closed = false;
_end -= 1;
}
}
}
}
debug_check("SPCurve::backspace", this);
}
/* Private methods */
/**
* Returns index of first NR_END bpath in array.
*/
{
unsigned ret = 0;
++ret;
}
++ret;
return ret;
}
/**
* \brief
*
* \todo
* fixme: this is bogus -- it doesn't check for nr_moveto, which will indicate
* a closing of the subpath it's nonsense to talk about a path as a whole
* being closed, although maybe someone would want that for some other reason?
* Oh, also, if the bpath just ends, then it's *open*. I hope nobody is using
* this code for anything.
*/
{
return false;
}
}
return true;
}
/**
* Returns length of bezier segment.
*/
static double
double const threshold)
{
/** \todo
* The SVG spec claims that a closed form exists, but for the moment I'll
* use a stupid algorithm.
*/
double ret;
} else {
ret = bezier_len(c0, a1, a2, midpoint, rec_threshold) + bezier_len(midpoint, b1, b2, c3, rec_threshold);
g_warning("ret=%f outside of expected bounds [%f, %f] for {(%.0f %.0f) (%.0f %.0f) (%.0f %.0f) (%.0f %.0f)}",
}
}
return ret;
}
/**
* Returns total length of curve, excluding length of closepath segments.
*/
double
{
double ret = 0.0;
return ret;
}
double seg_len = 0;
switch (p.code) {
case NR_MOVETO_OPEN:
case NR_MOVETO:
case NR_LINETO:
break;
case NR_CURVETO:
break;
case NR_END:
return ret;
}
prev = p.c(3);
}
return ret;
}
/**
* Like sp_curve_distance_including_space(), but ensures that the
* result >= 1e-18: uses 1 per segment if necessary.
*/
double
{
if (real_dist >= 1e-18) {
return real_dist;
} else {
for (unsigned i = 0; i < nSegs; ++i) {
seg2len[i] = 1.;
}
return (double) nSegs;
}
}
/**
* 2GEOMified
*/
void
{
if (is_empty()) {
return;
}
double begin_dist = 0.;
switch (p.code) {
case NR_LINETO:
case NR_MOVETO:
case NR_MOVETO_OPEN:
break;
case NR_CURVETO:
}
break;
default:
}
}
/* Explicit set for better numerical properties. */
delete [] seg2len;
g_error("SPCurve::stretch_endpoints - arclength <= 0");
throw;
}
Geom::Piecewise<Geom::D2<Geom::SBasis> > offsetpath = Geom::sectionize( Geom::D2<Geom::Piecewise<Geom::SBasis> >(offsetx, offsety) );
pwd2 += offsetpath;
debug_check("SPCurve::stretch_endpoints", this);
}
/**
* sets start of first path to new_p0, and end of first path to new_p1
* 2GEOMified
*/
void
{
if (is_empty()) {
return;
}
debug_check("SPCurve::move_endpoints", this);
}
/**
* returns the number of nodes in a path, used for statusbar text when selecting an spcurve.
* 2GEOMified
*/
SPCurve::nodes_in_path() const
{
if (i > r) i = r; // sometimes after switching from node editor length is wrong, e.g. f6 - draw - f2 - tab - f1, this fixes it
for (; i >= 0; i --)
r --;
nr++; // count last node (this works also for closed paths because although they don't have a 'last node', they do have an extra segment
}
return r;
}
/**
* Adds p to the last point (and last handle if present) of the last path
* 2GEOMified
*/
void
{
if (is_empty()) {
return;
}
if (_end == 0) {
return;
}
}
// Move handle as well when the last segment is a cubic bezier segment:
// TODO: what to do for quadratic beziers?
if ( Geom::CubicBezier const *lastcube = dynamic_cast<Geom::CubicBezier const *>(&_pathv.back().back()) ) {
}
debug_check("SPCurve::last_point_additive_move", this);
}
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :