geom-pathstroke.cpp revision 2d04ccee15df9fd4fdf02f32c0f050b1b6ba83f4
/* Authors:
* Liam P. White
* Tavmjong Bah
*
* Copyright (C) 2014-2015 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include <iomanip>
#include <math.h>
#include "helper/geom-pathstroke.h"
namespace Geom {
{
}
}
/**
* Find circle that touches inside of the curve, with radius matching the curvature, at time value \c t.
* Because this method internally uses unitTangentAt, t should be smaller than 1.0 (see unitTangentAt).
*/
{
}
}
double curv = k(t); // note that this value is signed
}
// Area of triangle given three corner points
using Geom::X;
using Geom::Y;
return( 0.5 * fabs( ( a[X]*(b[Y]-c[Y]) + b[X]*(c[Y]-a[Y]) + c[X]*(a[Y]-b[Y]) ) ) );
}
// Alternative touching circle routine directly using Beziers. Works only at end points.
double k = 0;
if ( start ) {
k = -k;
}
p = curve[0];
// std::cout << "Start k: " << k << " d: " << distance << " normal: " << normal << std::endl;
} else {
k = -k;
}
p = curve[3];
// std::cout << "End k: " << k << " d: " << distance << " normal: " << normal << std::endl;
}
if( k == 0 ) {
} else {
double radius = 1/k;
}
}
}
namespace {
// Internal data structure
struct join_data {
join_data(Geom::Path &_res, Geom::Path const&_outgoing, Geom::Point _in_tang, Geom::Point _out_tang, double _miter, double _width)
: res(_res), outgoing(_outgoing), in_tang(_in_tang), out_tang(_out_tang), miter(_miter), width(_width) {};
// contains the current path that is being built on
// contains the next curve to append
// input tangents
// line parameters
double miter;
double width; // half stroke width
};
// Join functions must append the outgoing path
{
}
{
jd.res.appendNew<Geom::EllipticalArc>(jd.width, jd.width, 0, false, jd.width <= 0, jd.outgoing.initialPoint());
}
{
using namespace Geom;
bool satisfied = false;
if (p.isFinite()) {
// check size of miter
if (satisfied) {
// miter OK, check to see if we can do a relocation
if (inc_ls) {
} else {
}
} else if (clip) {
// std::cout << " Clipping ------------ " << std::endl;
// miter needs clipping, find two points
// std::cout << " bisector_versor: " << bisector_versor << std::endl;
// std::cout << " point_limit: " << point_limit << std::endl;
// std::cout << " p1: " << p1 << std::endl;
// std::cout << " p2: " << p2 << std::endl;
if (inc_ls) {
} else {
}
}
}
// check if we can do another relocation
} else {
}
// either way, add the rest of the path
}
Geom::Point pick_solution(std::vector<Geom::ShapeIntersection> points, Geom::Point tang2, Geom::Point endPt)
{
// points[0] is bad, choose points[1]
} else if ( dot(tang2, points[1].point() - endPt) > 0 ) { // points[0] could be good, now check points[1]
// points[1] is bad, choose points[0]
} else {
// both points are good, choose nearest
}
return sol;
}
// Arcs line join. If two circles don't intersect, expand inner circle.
Geom::Point expand_circle( Geom::Circle &inner_circle, Geom::Circle const &outer_circle, Geom::Point const &start_pt, Geom::Point const &start_tangent ) {
// std::cout << "expand_circle:" << std::endl;
// std::cout << " outer_circle: radius: " << outer_circle.radius() << " center: " << outer_circle.center() << std::endl;
// std::cout << " start: point: " << start_pt << " tangent: " << start_tangent << std::endl;
// std::cout << " WARNING: Outer circle does not contain starting point!" << std::endl;
}
// std::cout << " chord1: " << chord1_pts[0].point() << ", " << chord1_pts[1].point() << std::endl;
// std::cout << " chord2: " << chord2_pts[0].point() << ", " << chord2_pts[1].point() << std::endl;
// Find D, point on chord2 and on circle closest to start point
// std::cout << " d0: " << d0 << " d1: " << d1 << std::endl;
// Chord through start point and point D
// std::cout << " chord3: " << chord3_pts[0].point() << ", " << chord3_pts[1].point() << std::endl;
// Find farthest point on chord3 and on circle (could be more robust)
// std::cout << " d2: " << d2 << " d3: " << d3 << std::endl;
// Find point P, the intersection of outer circle and new inner circle
// Find center of new circle: it is at the intersection of the bisector
// of the chord defined by the start point and point P and a line through
// the start point and parallel to the first bisector.
// std::cout << " center_new: " << center_new[0].point() << std::endl;
// std::cout << " r_new: " << r_new << std::endl;
return p;
}
// Arcs line join. If two circles don't intersect, adjust both circles so they just touch.
// Increase (decrease) the radius of circle 1 and decrease (increase) of circle 2 by the same amount keeping the given points and tangents fixed.
Geom::Point adjust_circles( Geom::Circle &circle1, Geom::Circle &circle2, Geom::Point const &point1, Geom::Point const &point2, Geom::Point const &tan1, Geom::Point const &tan2 ) {
// std::cout << "adjust_circles:" << std::endl;
// std::cout << " norm: " << n1 << "; " << n2 << std::endl;
// std::cout << " sum_n: " << sum_n << std::endl;
// std::cout << " delta_r: " << delta_r << std::endl;
// std::cout << " delta_c: " << delta_c << std::endl;
// Quadratic equation
double s1 = 0;
double s2 = 0;
if( fabs(A) < 0.01 ) {
// std::cout << " A near zero! $$$$$$$$$$$$$$$$$$" << std::endl;
if( B != 0 ) {
s1 = -C/B;
}
} else {
}
// std::cout << " A: " << A << std::endl;
// std::cout << " B: " << B << std::endl;
// std::cout << " C: " << C << std::endl;
// std::cout << " s1: " << s1 << " s2: " << s2 << " dr: " << dr << std::endl;
// std::cout << " C1: " << circle1 << std::endl;
// std::cout << " C2: " << circle2 << std::endl;
// std::cout << " d': " << Geom::Point( circle1.center() - circle2.center() ).length() << std::endl;
// std::cout << " points: " << p0 << "; " << p1 << std::endl;
return p0;
} else {
return p1;
}
}
{
// std::cout << "\nextrapolate_join: entrance: alternative: " << alternative << std::endl;
using namespace Geom;
// width is half stroke-width
// std::cout << " startPt: " << startPt << " endPt: " << endPt << std::endl;
// std::cout << " tang1: " << tang1 << " tang2: " << tang2 << std::endl;
// std::cout << " dot product: " << Geom::dot( tang1, tang2 ) << std::endl;
// std::cout << " width: " << width << std::endl;
// CIRCLE CALCULATION TESTING
// std::cout << " circle1: " << circle1 << std::endl;
// std::cout << " circle2: " << circle2 << std::endl;
// std::cout << " Circle 1 error!!!!!!!!!!!!!!!!!" << std::endl;
// std::cout << " " << circle_test1 << std::endl;
}
}
// std::cout << " Circle 2 error!!!!!!!!!!!!!!!!!" << std::endl;
// std::cout << " " << circle_test2 << std::endl;
}
}
// END TESTING
double side1 = tang1[Geom::X]*(startPt[Geom::Y]-center1[Geom::Y]) - tang1[Geom::Y]*(startPt[Geom::X]-center1[Geom::X]);
double side2 = tang2[Geom::X]*( endPt[Geom::Y]-center2[Geom::Y]) - tang2[Geom::Y]*( endPt[Geom::X]-center2[Geom::X]);
// std::cout << " side1: " << side1 << " side2: " << side2 << std::endl;
// std::cout << " two circles" << std::endl;
// See if tangent is backwards (radius < width/2 and circle is inside stroke).
// std::cout << " node_on_path: " << node_on_path << " -------------- " << std::endl;
bool b1 = false;
bool b2 = false;
b1 = true;
}
b2 = true;
}
// std::cout << " b1: " << (b1?"true":"false")
// << " b2: " << (b2?"true":"false") << std::endl;
// Two circles
// std::cout << " Circles do not intersect, do backup" << std::endl;
switch (alternative) {
case 1:
{
// Fallback to round if one path has radius smaller than half line width.
// std::cout << "At one least path has radius smaller than half line width." << std::endl;
return( round_join(jd) );
}
Point p;
// std::cout << "Expand circle1" << std::endl;
// std::cout << "Expand circle2" << std::endl;
} else {
// std::cout << "Either both points inside or both outside" << std::endl;
return( miter_clip_join(jd) );
}
break;
}
case 2:
{
// Fallback to round if one path has radius smaller than half line width.
// std::cout << "At one least path has radius smaller than half line width." << std::endl;
return( round_join(jd) );
}
} else {
// std::cout << "Either both points inside or both outside" << std::endl;
return( miter_clip_join(jd) );
}
break;
}
case 3:
if( side1 > 0 ) {
} else {
}
break;
case 0:
default:
// Do nothing
break;
}
}
} else {
}
} else {
}
}
// Line and circle
// std::cout << " line circle" << std::endl;
}
// Circle and line
// std::cout << " circle line" << std::endl;
}
}
// std::cout << " no solutions" << std::endl;
// no solutions available, fall back to miter
return miter_join(jd);
}
// We have a solution, thus sol is defined.
// See if we need to clip. Miter length is measured along a circular arc that is tangent to the
// bisector of the incoming and out going angles and passes through the end point (sol) of the
// line join.
// Center of circle is intersection of a line orthogonal to bisector and a line bisecting
// a chord connecting the path end point (point_on_path) and the join end point (sol).
bool clipped = false;
// No intersection (can happen if curvatures are equal but opposite)
clipped = true;
}
} else {
} else {
}
// We need to clip
clipped = true;
if (!inc_ls) {
// Incoming circular
delete arc1;
}
} else {
}
if (!out_ls) {
// Outgoing circular
delete arc2;
}
} else {
}
}
}
// Add initial
if (arc1) {
} else if (seg1 ) {
} else {
// Straight line segment: move last point
}
if (clipped) {
}
// Add outgoing
if (arc2) {
} else if (seg2 ) {
} else {
// Straight line segment:
}
// add the rest of the path
delete arc1;
delete arc2;
delete seg1;
delete seg2;
}
{
// I am not sure how well this will work -- we pick the join node closest
// to the cross point of the paths
/*Geom::Point original = res.finalPoint()+Geom::rot90(jd.in_tang)*jd.width;
Geom::Coord trial = Geom::L2(res.pointAt(cross[0].ta)-original);
solution = 0;
for (size_t i = 1; i < cross.size(); ++i) {
//printf("Trying %d\n", i);
Geom::Coord test = Geom::L2(res.pointAt(cross[i].ta)-original);
if (test < trial) {
trial = test;
solution = i;
//printf("Found improved solution: %f\n", trial);
}
}*/
}
if (solution != -1) {
// Watch for bugs in 2geom crossing regarding severe inflection points
} else {
}
}
{
}
// Offsetting a line segment is mathematically stable and quick to do
{
}
{
// get derivatives
// TODO: we might want to consider using Geom::touching_circle to determine the
// curvature radius here. Less code duplication, but slower
return; // this isn't a segment...
}
rad = 1e8;
} else {
}
} else {
}
len = l;
}
void offset_cubic(Geom::Path& p, Geom::CubicBezier const& bez, double width, double tol, size_t levels)
{
using Geom::X;
using Geom::Y;
// offset the start and end control points out by the width
// --------
// correction of the lengths of the tangent to the offset
// --------
// create the estimate curve
// reached maximum recursive depth
// don't bother with any more correction
if (levels == 0) {
p.append(c);
return;
}
// check the tolerance for our estimate to be a parallel curve
}
// we're good, curve is accurate enough
p.append(c);
return;
} else {
// split the curve in two
}
}
void offset_quadratic(Geom::Path& p, Geom::QuadraticBezier const& bez, double width, double tol, size_t levels)
{
// cheat
// it's faster
// seriously
}
{
double const tolerance = 0.0025;
// TODO: we can handle SVGEllipticalArc here as well, do that!
switch (order) {
case 1:
break;
case 2: {
break;
}
case 3: {
break;
}
default: {
break;
}
}
} else {
}
}
typedef void cap_func(Geom::PathBuilder& res, Geom::Path const& with_dir, Geom::Path const& against_dir, double width);
{
}
void round_cap(Geom::PathBuilder& res, Geom::Path const&, Geom::Path const& against_dir, double width)
{
}
void square_cap(Geom::PathBuilder& res, Geom::Path const& with_dir, Geom::Path const& against_dir, double width)
{
width /= 2.;
}
void peak_cap(Geom::PathBuilder& res, Geom::Path const& with_dir, Geom::Path const& against_dir, double width)
{
width /= 2.;
Geom::Point midpoint = ((with_dir.finalPoint() + normal_1*width) + (against_dir.initialPoint() + normal_2*width)) * 0.5;
}
} // namespace
namespace Inkscape {
Geom::PathVector outline(Geom::Path const& input, double width, double miter, LineJoinType join, LineCapType butt)
{
switch (butt) {
case BUTT_ROUND:
break;
case BUTT_SQUARE:
cf = &square_cap;
break;
case BUTT_PEAK:
break;
default:
}
// glue caps
} else {
}
}
}
{
res.setStitching(true);
temp.setStitching(true);
// Do two curves at a time for efficiency, since the join function needs to know the outgoing curve as well
for (size_t u = 0; u < k; u += 2) {
// on the first run through, there isn't a join
if (u == 0) {
} else {
}
// odd number of paths
if (u < k - 1) {
}
}
res.erase_last();
//
}
return res;
}
void outline_join(Geom::Path &res, Geom::Path const& temp, Geom::Point in_tang, Geom::Point out_tang, double width, double miter, Inkscape::LineJoinType join)
{
return;
// if the points are /that/ close, just ignore this one
return;
}
if (on_outside) {
switch (join) {
case Inkscape::JOIN_BEVEL:
jf = &bevel_join;
break;
case Inkscape::JOIN_ROUND:
jf = &round_join;
break;
case Inkscape::JOIN_EXTRAPOLATE:
jf = &extrapolate_join;
break;
case Inkscape::JOIN_EXTRAPOLATE1:
break;
case Inkscape::JOIN_EXTRAPOLATE2:
break;
case Inkscape::JOIN_EXTRAPOLATE3:
break;
case Inkscape::JOIN_MITER_CLIP:
jf = &miter_clip_join;
break;
default:
jf = &miter_join;
}
} else {
}
}
} // namespace Inkscape
/*
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 :