Newer
Older
/*
* Copyright (c) 2015 Tricoire Sebastien 3dsman@free.fr
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
*/
#include "OE_curve.h"
#include "OE_utils.h"
#include <iostream>
#include <GL/gl.h>
#include <cstdlib>
#include <math.h>
#include <cstdio>
#include <cstring>
unsigned char OE_curve::lineColor[] = {0,160,192,255};
unsigned char OE_curve::controlLineColor[] = {100,60,92,255};
unsigned char OE_curve::controlPointColor[] = {100,60,92,255};
unsigned char OE_curve::controlEndPointColor[] = {100,60,92,255};
OE_curve::OE_curve(std::vector<vector_2d> points, bool closed)
{
pts = points;
this->closed = closed; // Flag indicating if shapes should be treated as closed.
}
OE_curve::~OE_curve()
{
}
bool OE_curve::getPoint(uint16_t nb, float* x, float* y)
{
return true;
}
return false;
}
bool OE_curve::getPoint(uint16_t nb, vector_2d* pt)
{
return true;
}
return false;
}
bool OE_curve::setPoint(uint16_t nb, float x, float y)
{
return true;
}
return false;
}
bool OE_curve::addPoint( float x, float y)
{
bool OE_curve::addPoint( vector_2d pt)
}
void OE_curve::lineTo(float x, float y)
{
float px,py, dx,dy;
dx = x - px;
dy = y - py;
addPoint(px + dx/3.0f, py + dy/3.0f);
addPoint(x - dx/3.0f, y - dy/3.0f);
addPoint(x, y);
}
}
void OE_curve::lineTo(vector_2d pt)
{
vector_2d p,d;
d = pt-p;
addPoint(p + d/3.0f);
addPoint(pt - d/3.0f);
addPoint(pt);
}
}
void OE_curve::cubicBezTo(float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
{
addPoint(cpx1, cpy1);
addPoint(cpx2, cpy2);
addPoint(x, y);
}
#define EPSILON (1e-12)
bool OE_curve::ptInBounds( vector_2d pt, float* bounds)
return pt.x >= bounds[0] && pt.x <= bounds[2] && pt.y >= bounds[1] && pt.y <= bounds[3];
}
double OE_curve::evalBezier(double t, double p0, double p1, double p2, double p3)
{
double it = 1.0-t;
return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3;
}
std::vector<vector_2d> OE_curve::interPoint(vector_2d pt1, vector_2d pt2, vector_2d pt3, vector_2d pt4, float t)
{
std::vector<vector_2d> out;
vector_2d pt12 = (pt2-pt1)*t+pt1;
vector_2d pt23 = (pt3-pt2)*t+pt2;
vector_2d pt34 = (pt4-pt3)*t+pt3;
vector_2d pt123 = (pt23-pt12)*t+pt12;
vector_2d pt234 = (pt34-pt23)*t+pt23;
vector_2d pt1234 = (pt234-pt123)*t+pt123;
out.push_back(pt123);
out.push_back(pt1234);
out.push_back(pt234);
return out;
}
void OE_curve::segmentBounds(float* bounds, vector_2d* segment)
vector_2d* v0 = &segment[0];
vector_2d* v1 = &segment[1];
vector_2d* v2 = &segment[2];
vector_2d* v3 = &segment[3];
bounds[0] = minf(v0->x, v3->x);
bounds[1] = minf(v0->y, v3->y);
bounds[2] = maxf(v0->x, v3->x);
bounds[3] = maxf(v0->y, v3->y);
// Bezier segment fits inside the convex hull of it's control points.
if (ptInBounds(*v1, bounds) && ptInBounds(*v2, bounds))
// Add bezier segment inflection points in X and Y.
a = -3.0 * v0->y+ 9.0 * v1->y - 9.0 * v2->y + 3.0 * v3->y;
b = 6.0 * v0->y - 12.0 * v1->y + 6.0 * v2->y;
c = 3.0 * v1->y - 3.0 * v0->y;
a = -3.0 * v0->x + 9.0 * v1->x - 9.0 * v2->x + 3.0 * v3->x;
b = 6.0 * v0->x - 12.0 * v1->x + 6.0 * v2->x;
c = 3.0 * v1->x - 3.0 * v0->x;
count = 0;
if (fabs(a) < EPSILON) {
if (fabs(b) > EPSILON) {
t = -c / b;
if (t > EPSILON && t < 1.0-EPSILON)
roots[count++] = t;
}
} else {
b2ac = b*b - 4.0*c*a;
if (b2ac > EPSILON) {
t = (-b + sqrt(b2ac)) / (2.0 * a);
if (t > EPSILON && t < 1.0-EPSILON)
roots[count++] = t;
t = (-b - sqrt(b2ac)) / (2.0 * a);
if (t > EPSILON && t < 1.0-EPSILON)
roots[count++] = t;
}
}
for (j = 0; j < count; j++) {
v = evalBezier(roots[j], v0->y, v1->y, v2->y, v3->y);
v = evalBezier(roots[j], v0->x, v1->x, v2->x, v3->x);
}
bounds[0+i] = minf(bounds[0+i], (float)v);
bounds[2+i] = maxf(bounds[2+i], (float)v);
}
}
}
void OE_curve::getBound(float* xMin, float* yMin, float* xMax, float* yMax)
{
float tmpbounds[4];
vector_2d* curve;
// Find bounds
if (i == 0) {
bounds[0] = tmpbounds[0];
bounds[1] = tmpbounds[1];
bounds[2] = tmpbounds[2];
bounds[3] = tmpbounds[3];
} else {
bounds[0] = minf(bounds[0], tmpbounds[0]);
bounds[1] = minf(bounds[1], tmpbounds[1]);
bounds[2] = maxf(bounds[2], tmpbounds[2]);
bounds[3] = maxf(bounds[3], tmpbounds[3]);
}
}
*xMin = bounds[0];
*yMin = bounds[1];
*xMax = bounds[2];
*yMax = bounds[3];
}
std::vector<vector_2d> OE_curve::subCurve( float start, float end, bool rev)
{
std::vector<vector_2d> out;
if (start>end)
{
if(start==(pts.size()-1)/3)
start = 0;
else if(end==0)
end = (pts.size()-1)/3;
}
if (((start>end)&&(!closed))||(start==end)||(start<0)||(end<0)||(start>(pts.size()-1.0)/3.0)||(end>(pts.size()-1.0)/3.0))
return out;
std::vector<vector_2d> tangeant;
if (start>end)
loop = true;
float scaleVect = 1;
float nearPt;
nearPt = floor(start);
vector_2d* ps = &pts[nearPt*3];
tangeant = interPoint(ps[0],ps[1],ps[2],ps[3], start-nearPt);
out.push_back(tangeant[1]);
out.push_back(tangeant[2]);
scaleVect = 1-(start-nearPt);
{
nearPt++;
ps = &pts[nearPt*3];
{
ps2 = &pts[0];
nearPt = 0;
loop =false;
}
out.push_back((ps[-1]-ps2[0])*scaleVect + ps2[0]);
out.push_back(ps2[0]);
out.push_back(ps2[1]);
scaleVect = 1;
out[out.size()] = (out[out.size()]-out[out.size()-1])*(end-nearPt)+out[out.size()];
ps = &pts[nearPt*3];
tangeant = interPoint(ps[0],ps[1],ps[2],ps[3], end-nearPt);
out.push_back(tangeant[0]);
out.push_back(tangeant[1]);
if (rev) std::reverse(out.begin(),out.end());
bool OE_curve::setNext(OE_curve* next)
{
this->next = next;
return true;
}
OE_curve* OE_curve::getNext()
{
return next;
}
bool OE_curve::getClosed(){return closed;}
void OE_curve::setClosed(bool closed){this->closed = closed; needRefresh = true;}
bool OE_curve::check(){return ((pts.size()>0)&&((pts.size())%3==1)) ;}
bool OE_curve::refresh(float dpi)
discPts = discretizeFast(dpi);
float OE_curve::distPtSeg(vector_2d pt, vector_2d seg1, vector_2d seg2)
{
float d2, t;
vector_2d pq, d;
pq = seg2-seg1;
d = pt-seg1;
d2 = pq.x*pq.x + pq.y*pq.y;
t = pq.x*d.x + pq.y*d.y;
if (d2 > 0) t /= d2;
if (t < 0) t = 0;
else if (t > 1) t = 1;
d = seg1 + t*pq - pt;
return d.x*d.x + d.y*d.y;
}
std::vector<vector_2d> OE_curve::discretizeCubicBez(vector_2d pt1, vector_2d pt2, vector_2d pt3, vector_2d pt4, float tol, int level)
vector_2d pt12 = (pt1+pt2)*0.5f;
vector_2d pt23 = (pt2+pt3)*0.5f;
vector_2d pt34 = (pt3+pt4)*0.5f;
vector_2d pt123 = (pt12+pt23)*0.5f;
vector_2d pt234 = (pt23+pt34)*0.5f;
vector_2d pt1234 = (pt123+pt234)*0.5f;
d = distPtSeg(pt1234, pt1, pt4);
out = discretizeCubicBez(pt1, pt12, pt123, pt1234, tol, level+1);
std::vector<vector_2d> tmpdisc = discretizeCubicBez(pt1234, pt234, pt34, pt4, tol, level+1);
out.insert(out.end(), tmpdisc.begin(), tmpdisc.end());
} else {
std::vector<vector_2d> OE_curve::discretizeFast(float maxDist)
for (unsigned i = 0; i < pts.size()-3; i += 3) {
std::vector<vector_2d> tmpdisk = discretizeCubicBez(pts.at(i), pts.at(i+1), pts.at(i+2), pts.at(i+3), maxDist, 0);
out.insert(out.end(), tmpdisk.begin(), tmpdisk.end());
}
std::vector<vector_2d> OE_curve::discretizeRegular(float dist)
std::vector<vector_2d> out;
if (check())
{
float vectlen, tmplen = 0;
vector_2d vect;
std::vector<vector_2d> disc, tmpdisc ;
tmpdisc.push_back(pts[0]);
for (unsigned i = 0; i < pts.size()-3; i += 3) {
std::vector<vector_2d> tmpdisk = discretizeCubicBez(pts[i], pts[i+1], pts[i+2], pts[i+3], dist/100.0, 0);
tmpdisc.insert(tmpdisc.end(), tmpdisk.begin(), tmpdisk.end());
}
tmplen =0;
out.push_back(tmpdisc[0]);
for (unsigned i = 0; i < tmpdisc.size()-1; i += 1) {
vect = tmpdisc[i]-tmpdisc[i+1];
out.push_back(tmpdisc.at(tmpdisc.size()-1));
return out;