PDA

View Full Version : Fill area above baseline and below the curve



jarno
18th November 2010, 13:05
Hello. Is it possible to fill area above the baseline and below the curve (something like this (http://www.halliburton.com/public/landmark/contents/Overview/images/promaxvsp3.jpg))?

FelixB
19th November 2010, 07:51
have you tried QwtPlotCurve::setBrush() ?

jarno
19th November 2010, 11:17
Yes, but it fills whole area under the curve. Please, look at this image (http://www.halliburton.com/public/landmark/contents/Overview/images/promaxvsp3.jpg). It is exactly what I'm trying to achieve. This kind of fill is a requirement for seismic profile data visualization.

FelixB
19th November 2010, 11:23
no, setBrush() fills the area between the curve and the baseline. Have you set the CurveType to Xfy?

jarno
19th November 2010, 12:23
For example, sin() alternates from positive to negative and back again. Baseline is 0. With the brush set, the whole area between baseline and sin() values (both positive and negative) will be filled. I need only area between positive values and baseline to be filled. My code looks something like:


class SimpleData : public QwtData
{
public:
SimpleData(double (*f)(double), size_t size):
d_size(size),
d_f(f)
{
}

virtual QwtData *copy() const
{
return new SimpleData(*this);
}

virtual size_t size() const
{
return d_size;
}

virtual double x(size_t i) const
{
return d_f(y(i));
}

virtual double y(size_t i) const
{
return 0.1 * i;
}

private:
size_t d_size;
double (*d_f)(double);
};

int
main(int argc, char** argv)
{
QApplication application(argc, argv);
QwtPlot plot;

plot.show();

QwtPlotCurve curve;
SimpleData data(std::sin, 100);

curve.attach(&plot);
curve.setBrush(QBrush(Qt::blue, Qt::SolidPattern));
curve.setCurveType(QwtPlotCurve::Xfy);
curve.setData(data);

return application.exec();
}

Is overriding the QwtPlotCurve::drawCurve with extra curve style is the only way?

FelixB
19th November 2010, 13:00
ok, I understand the problem now. what about this (pseudocode):

you have curve "c1". create a second curve "c2". set c2.yValues = c1.yValues and c2.xValue[i] = max(c1.xValue[i], baseline) - now you can add the brush to c2 and the area between baseline and higher values will be painted.

I hope, you understand what I suggest...

Uwe
19th November 2010, 13:08
Setting brush and baseline to your curve is almost what you need - beside clipping away the rectangle above your baseline before the curve is filled. But unfortunately QwtPlotCurve::fillCurve() is not virtual.

So what you try do is something like this:


virtual void YourCurve::drawLines(QPainter *painter,
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
int from, int to) const
{
painter->save();

painter->setClipRect(...);

const QPen pen = this->pen();
setPen(Qt::transparent);

QwtPlotCurve::drawLines(painter, xMap, yMap, from, to);

setPen(pen);

painter->restore();

const QBrush brush = this->brush();
setBrush(Qt::NoBrush);

QwtPlotCurve::drawLines(painter, xMap, yMap, from, to);

setBrush(brush);
}

Uwe

jarno
19th November 2010, 17:08
Thanks. I've came to the following code, but it seems that I have to understand QPainter and it's logical coordinates (looks like it clips everything).


virtual void
drawCurve(QPainter *painter,
int style,
const QwtScaleMap &xMap,
const QwtScaleMap &yMap,
int from,
int to) const
{
if (style < QwtPlotCurve::UserCurve) {
QwtPlotCurve::drawCurve(painter, style, xMap, yMap, from, to);
return;
}

QPoint topLeft;
QPoint bottomRight;

switch (curveType()) {
case Xfy :
topLeft.rx() = xMap.transform(baseline());
topLeft.ry() = yMap.transform(y(from));
bottomRight.rx() = xMap.transform(maxXValue());
bottomRight.ry() = yMap.transform(y(to));
break;
case Yfx :
topLeft.rx() = xMap.transform(x(from));
topLeft.ry() = yMap.transform(baseline());
bottomRight.rx() = xMap.transform(x(to));
bottomRight.ry() = yMap.transform(maxYValue());
break;
}

painter->save();

painter->drawRect(QRect(topLeft, bottomRight)); // Draw clip rectangle
painter->setClipRect(QRect(topLeft, bottomRight));

const QPen savedPen = painter->pen();

painter->setPen(Qt::transparent);
QwtPlotCurve::drawLines(painter, xMap, yMap, from, to);
painter->setPen(savedPen);

const QBrush savedBrush = painter->brush();

painter->setBrush(Qt::NoBrush);
QwtPlotCurve::drawLines(painter, xMap, yMap, from, to);
painter->setBrush(savedBrush);

painter->restore();
}

Uwe
19th November 2010, 18:11
Your clip rectangle can be built from the baseline ( mapped into widget coordinates ) and the contents rect of the canvas - the coordinates of your curve are of no importance.

As you don't have the canvas rectangle here you could also use any huge coordinate far outside like -100000 ( or 100000, when your y axis is inverted and you want to clip below the baseline ).

Uwe

chinalski
28th February 2012, 17:38
Hi Uwe,
I have exactly the same problem as jarno: filling only the positive part of a curve, and leave transparent the negative part.
You said in a previous reply of this post:

Setting brush and baseline to your curve is almost what you need - beside clipping away the rectangle above your baseline before the curve is filled. But unfortunately QwtPlotCurve::fillCurve() is not virtual.
But I can see now that in qwt-6.0.1 fillCurve is "virtual", probably you made a change in one of the last versions.
May I ask you to spend some words to explain to me which is the right way to use fillCurve() to achieve what I need?

Thank you very much
c

Added after 22 minutes:

Sorry, I asked Uwe, but obviously anyone has an answer will be very appreciated.
c

chinalski
7th March 2012, 18:22
Hi all,
nobody answered to my question, probably because it was a trivial question, in any case I resolved my problem, and I'm writing just to close the thread and to post the solution, if anyone will have the same problem in the future.
This is the code I used to define the member fillCurve of the virtual class QwtPlotCurve.
Thank you, bye
c


void QwtPlotCurveAnteo::fillCurve(QPainter *painter,
const QwtScaleMap &xMap,
const QwtScaleMap &yMap,
const QRectF & canvasRect,
QPolygonF & polygon) const
{
painter->save();
painter->setClipRect(xMap.transform(baseline()),0,100,1000,
Qt::ReplaceClip);

QwtPlotCurve::fillCurve(painter, xMap, yMap, canvasRect, polygon);

painter->setClipRect(xMap.transform(baseline()),0,100,1000,
Qt::NoClip);
painter->restore();

return;
}