You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/chalk/ui/kcurve.cpp

451 lines
11 KiB

/* ============================================================
* Copyright 2004-2005 by Gilles Caulier
* Copyright 2005 by Casper Boemann (reworked to be generic)
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* ============================================================ */
// C++ includes.
#include <cmath>
#include <cstdlib>
// TQt includes.
#include <tqpixmap.h>
#include <tqpainter.h>
#include <tqpoint.h>
#include <tqpen.h>
#include <tqevent.h>
#include <tqtimer.h>
#include <tqrect.h>
#include <tqfont.h>
#include <tqfontmetrics.h>
// KDE includes.
#include <kdebug.h>
#include <kcursor.h>
#include <tdelocale.h>
// Local includes.
#include "kcurve.h"
KCurve::KCurve(TQWidget *parent, const char *name, WFlags f)
: TQWidget(parent, name, f)
{
m_grab_point = NULL;
m_readOnlyMode = false;
m_guideVisible = false;
m_dragging = false;
m_pix = NULL;
setMouseTracking(true);
setPaletteBackgroundColor(TQt::NoBackground);
setMinimumSize(150, 50);
TQPair<double,double> *p = new TQPair<double,double>;
p->first = 0.0; p->second=0.0;
m_points.append(p);
p = new TQPair<double,double>;
p->first = 1.0; p->second=1.0;
m_points.append(p);
m_points.setAutoDelete(true);
setFocusPolicy(TQWidget::StrongFocus);
}
KCurve::~KCurve()
{
if (m_pix) delete m_pix;
}
void KCurve::reset(void)
{
m_grab_point = NULL;
m_guideVisible = false;
repaint(false);
}
void KCurve::setCurveGuide(TQColor color)
{
m_guideVisible = true;
m_colorGuide = color;
repaint(false);
}
void KCurve::setPixmap(TQPixmap pix)
{
if (m_pix) delete m_pix;
m_pix = new TQPixmap(pix);
repaint(false);
}
void KCurve::keyPressEvent(TQKeyEvent *e)
{
if(e->key() == TQt::Key_Delete || e->key() == TQt::Key_Backspace)
{
TQPair<double,double> *closest_point=NULL;
if(m_grab_point)
{
//first find closest point to get focus afterwards
TQPair<double,double> *p = m_points.first();
double distance = 1000; // just a big number
while(p)
{
if(p!=m_grab_point)
if (fabs (m_grab_point->first - p->first) < distance)
{
distance = fabs(m_grab_point->first - p->first);
closest_point = p;
}
p = m_points.next();
}
m_points.remove(m_grab_point);
}
m_grab_point = closest_point;
repaint(false);
}
else
TQWidget::keyPressEvent(e);
}
void KCurve::paintEvent(TQPaintEvent *)
{
int x, y;
int wWidth = width();
int wHeight = height();
x = 0;
y = 0;
// Drawing selection or all histogram values.
// A TQPixmap is used for enable the double buffering.
TQPixmap pm(size());
TQPainter p1;
p1.begin(&pm, this);
// draw background
if(m_pix)
{
p1.scale(1.0*wWidth/m_pix->width(), 1.0*wHeight/m_pix->height());
p1.drawPixmap(0, 0, *m_pix);
p1.resetXForm();
}
else
pm.fill();
// Draw grid separators.
p1.setPen(TQPen(TQt::gray, 1, TQt::SolidLine));
p1.drawLine(wWidth/3, 0, wWidth/3, wHeight);
p1.drawLine(2*wWidth/3, 0, 2*wWidth/3, wHeight);
p1.drawLine(0, wHeight/3, wWidth, wHeight/3);
p1.drawLine(0, 2*wHeight/3, wWidth, 2*wHeight/3);
// Draw curve.
double curvePrevVal = getCurveValue(0.0);
p1.setPen(TQPen(TQt::black, 1, TQt::SolidLine));
for (x = 0 ; x < wWidth ; x++)
{
double curveX;
double curveVal;
curveX = (x + 0.5) / wWidth;
curveVal = getCurveValue(curveX);
p1.drawLine(x - 1, wHeight - int(curvePrevVal * wHeight),
x, wHeight - int(curveVal * wHeight));
curvePrevVal = curveVal;
}
p1.drawLine(x - 1, wHeight - int(curvePrevVal * wHeight),
x, wHeight - int(getCurveValue(1.0) * wHeight));
// Drawing curve handles.
if ( !m_readOnlyMode )
{
TQPair<double,double> *p = m_points.first();
while(p)
{
double curveX = p->first;
double curveY = p->second;
if(p == m_grab_point)
{
p1.setPen(TQPen(TQt::red, 3, TQt::SolidLine));
p1.drawEllipse( int(curveX * wWidth) - 2,
wHeight - 2 - int(curveY * wHeight), 4, 4 );
}
else
{
p1.setPen(TQPen(TQt::red, 1, TQt::SolidLine));
p1.drawEllipse( int(curveX * wWidth) - 3,
wHeight - 3 - int(curveY * wHeight), 6, 6 );
}
p = m_points.next();
}
}
p1.end();
bitBlt(this, 0, 0, &pm);
}
void KCurve::mousePressEvent ( TQMouseEvent * e )
{
if (m_readOnlyMode) return;
TQPair<double,double> *closest_point=NULL;
double distance;
if (e->button() != TQt::LeftButton)
return;
double x = e->pos().x() / (float)width();
double y = 1.0 - e->pos().y() / (float)height();
distance = 1000; // just a big number
TQPair<double,double> *p = m_points.first();
int insert_pos,pos=0;
while(p)
{
if (fabs (x - p->first) < distance)
{
distance = fabs(x - p->first);
closest_point = p;
if(x < p->first)
insert_pos = pos;
else
insert_pos = pos + 1;
}
p = m_points.next();
pos++;
}
if(closest_point == NULL)
{
closest_point = new TQPair<double,double>;
closest_point->first = x;
closest_point->second = y;
m_points.append(closest_point);
}
else if(distance * width() > 5)
{
closest_point = new TQPair<double,double>;
closest_point->first = x;
closest_point->second = y;
m_points.insert(insert_pos, closest_point);
}
else
if(fabs(y - closest_point->second) * width() > 5)
return;
m_grab_point = closest_point;
m_grabOffsetX = m_grab_point->first - x;
m_grabOffsetY = m_grab_point->second - y;
m_grab_point->first = x + m_grabOffsetX;
m_grab_point->second = y + m_grabOffsetY;
m_dragging = true;
setCursor( KCursor::crossCursor() );
// Determine the leftmost and rightmost points.
m_leftmost = 0;
m_rightmost = 1;
p = m_points.first();
while(p)
{
if (p != m_grab_point)
{
if(p->first> m_leftmost && p->first < x)
m_leftmost = p->first;
if(p->first < m_rightmost && p->first > x)
m_rightmost = p->first;
}
p = m_points.next();
}
repaint(false);
}
void KCurve::mouseReleaseEvent ( TQMouseEvent * e )
{
if (m_readOnlyMode) return;
if (e->button() != TQt::LeftButton)
return;
setCursor( KCursor::arrowCursor() );
m_dragging = false;
repaint(false);
emit modified();
}
void KCurve::mouseMoveEvent ( TQMouseEvent * e )
{
if (m_readOnlyMode) return;
double x = e->pos().x() / (float)width();
double y = 1.0 - e->pos().y() / (float)height();
if (m_dragging == false) // If no point is selected set the the cursor shape if on top
{
double distance = 1000;
double ydistance = 1000;
TQPair<double,double> *p = m_points.first();
while(p)
{
if (fabs (x - p->first) < distance)
{
distance = fabs(x - p->first);
ydistance = fabs(y - p->second);
}
p = m_points.next();
}
if (distance * width() > 5 || ydistance * height() > 5)
setCursor( KCursor::arrowCursor() );
else
setCursor( KCursor::crossCursor() );
}
else // Else, drag the selected point
{
setCursor( KCursor::crossCursor() );
x += m_grabOffsetX;
y += m_grabOffsetY;
if (x <= m_leftmost)
x = m_leftmost + 1E-4; // the addition so we can grab the dot later.
if(x >= m_rightmost)
x = m_rightmost - 1E-4;
if(y > 1.0)
y = 1.0;
if(y < 0.0)
y = 0.0;
m_grab_point->first = x;
m_grab_point->second = y;
emit modified();
}
repaint(false);
}
double KCurve::getCurveValue(double x)
{
return getCurveValue(m_points, x);
}
double KCurve::getCurveValue(TQPtrList<TQPair<double,double> > &curve, double x)
{
double t;
TQPair<double,double> *p;
TQPair<double,double> *p0,*p1,*p2,*p3;
double c0,c1,c2,c3;
double val;
if(curve.count() == 0)
return 0.5;
// First find curve segment
p = curve.first();
if(x < p->first)
return p->second;
p = curve.last();
if(x >= p->first)
return p->second;
// Find the four control points (two on each side of x)
p = curve.first();
while(x >= p->first)
{
p = curve.next();
}
curve.prev();
if((p0 = curve.prev()) == NULL)
p1 = p0 = curve.first();
else
p1 = curve.next();
p2 = curve.next();
if( (p = curve.next()) )
p3 = p;
else
p3 = p2;
// Calculate the value
t = (x - p1->first) / (p2->first - p1->first);
c2 = (p2->second - p0->second) * (p2->first-p1->first) / (p2->first-p0->first);
c3 = p1->second;
c0 = -2*p2->second + 2*c3 + c2 + (p3->second - p1->second) * (p2->first - p1->first) / (p3->first - p1->first);
c1 = p2->second - c3 - c2 - c0;
val = ((c0*t + c1)*t + c2)*t + c3;
if(val < 0.0)
val = 0.0;
if(val > 1.0)
val = 1.0;
return val;
}
TQPtrList<TQPair<double,double> > KCurve::getCurve()
{
TQPtrList<TQPair<double,double> > outlist;
TQPair<double,double> *p;
TQPair<double,double> *outpoint;
p = m_points.first();
while(p)
{
outpoint = new TQPair<double,double>(p->first, p->second);
outlist.append(outpoint);
p = m_points.next();
}
return outlist;
}
void KCurve::setCurve(TQPtrList<TQPair<double,double> >inlist)
{
TQPair<double,double> *p;
TQPair<double,double> *inpoint;
m_points.clear();
inpoint = inlist.first();
while(inpoint)
{
p = new TQPair<double,double>(inpoint->first, inpoint->second);
m_points.append(p);
inpoint = inlist.next();
}
}
void KCurve::leaveEvent( TQEvent * )
{
}
#include "kcurve.moc"