1909 lines
63 KiB
C++
1909 lines
63 KiB
C++
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
*
|
|
* RawTherapee 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* RawTherapee 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "myflatcurve.h"
|
|
#include "../rtengine/curves.h"
|
|
#include <cstring>
|
|
#include <gdkmm/types.h>
|
|
|
|
MyFlatCurve::MyFlatCurve ()
|
|
{
|
|
|
|
graphW = get_allocation().get_width() - RADIUS * 2;
|
|
graphH = get_allocation().get_height() - RADIUS * 2;
|
|
prevGraphW = graphW;
|
|
prevGraphH = graphH;
|
|
lit_point = -1;
|
|
closest_point = 0;
|
|
buttonPressed = false;
|
|
editedHandle = FCT_EditedHandle_None;
|
|
area = FCT_Area_None;
|
|
tanHandlesDisplayed = false;
|
|
periodic = true;
|
|
|
|
//bghist = new unsigned int[256];
|
|
|
|
editedPos.resize(4);
|
|
editedPos.at(0) = editedPos.at(1) = editedPos.at(2) = editedPos.at(3) = 0.0;
|
|
|
|
Glib::RefPtr<Gtk::StyleContext> style = get_style_context();
|
|
style->add_class(GTK_STYLE_CLASS_FRAME);
|
|
//style->add_class(GTK_STYLE_CLASS_NOTEBOOK);
|
|
//style->add_class(GTK_STYLE_CLASS_FLAT);
|
|
|
|
signal_event().connect( sigc::mem_fun(*this, &MyFlatCurve::handleEvents) );
|
|
|
|
// By default, we create a curve with 8 control points
|
|
curve.type = FCT_MinMaxCPoints;
|
|
|
|
defaultCurve();
|
|
}
|
|
|
|
/*MyFlatCurve::~MyFlatCurve () {
|
|
}*/
|
|
|
|
std::vector<double> MyFlatCurve::get_vector (int veclen)
|
|
{
|
|
|
|
// Create the output variable
|
|
std::vector<double> convertedValues;
|
|
|
|
// Get the curve control points
|
|
std::vector<double> curveDescr = getPoints ();
|
|
rtengine::FlatCurve rtcurve(curveDescr, periodic, veclen * 1.2 > 5000 ? 5000 : veclen * 1.2);
|
|
|
|
// Create the sample values that will be converted
|
|
std::vector<double> samples;
|
|
samples.resize (veclen);
|
|
|
|
for (int i = 0; i < veclen; i++) {
|
|
samples.at(i) = (double) i / (veclen - 1.0);
|
|
}
|
|
|
|
// Converting the values
|
|
rtcurve.getVal (samples, convertedValues);
|
|
|
|
// Cleanup and return
|
|
return convertedValues;
|
|
}
|
|
|
|
void MyFlatCurve::get_LUT (LUTf &lut)
|
|
{
|
|
|
|
int size = lut.getSize();
|
|
|
|
// Get the curve control points
|
|
std::vector<double> curveDescr = getPoints ();
|
|
rtengine::FlatCurve rtcurve(curveDescr, periodic, lut.getUpperBound() * 1.2 > 5000 ? 5000 : lut.getUpperBound() * 1.2);
|
|
|
|
double t;
|
|
double maxVal = double(lut.getUpperBound());
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
t = double(i) / maxVal;
|
|
lut[i] = rtcurve.getVal (t);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void MyFlatCurve::interpolate ()
|
|
{
|
|
|
|
prevGraphW = graphW;
|
|
prevGraphH = graphH;
|
|
int nbPoints = graphW - 2;
|
|
point(nbPoints);
|
|
get_LUT (point);
|
|
upoint.reset ();
|
|
lpoint.reset ();
|
|
|
|
curveIsDirty = false;
|
|
}
|
|
|
|
void MyFlatCurve::draw ()
|
|
{
|
|
if (!isDirty()) {
|
|
return;
|
|
}
|
|
|
|
Glib::RefPtr<Gdk::Window> win = get_window();
|
|
|
|
if (!surfaceCreated() || !win) {
|
|
return;
|
|
}
|
|
|
|
// re-calculate curve if dimensions changed
|
|
int currPointSize = point.getUpperBound();
|
|
|
|
if (curveIsDirty || /*prevGraphW != graphW || prevGraphH != graphH ||*/ (currPointSize == GRAPH_SIZE && (graphW - 3 > GRAPH_SIZE)) || (currPointSize > GRAPH_SIZE && (graphW - 2 <= GRAPH_SIZE || graphW - 3 != currPointSize))) {
|
|
interpolate ();
|
|
}
|
|
|
|
double innerW = double(graphW - 2);
|
|
double innerH = double(graphH - 2);
|
|
|
|
Gtk::StateFlags state = !is_sensitive() ? Gtk::STATE_FLAG_INSENSITIVE : Gtk::STATE_FLAG_NORMAL;
|
|
|
|
Glib::RefPtr<Gtk::StyleContext> style = get_style_context();
|
|
Cairo::RefPtr<Cairo::Context> cr = getContext();
|
|
cr->set_line_cap(Cairo::LINE_CAP_SQUARE);
|
|
|
|
// clear background
|
|
style->render_background(cr, 0., 0., double(getWidth()), double(getHeight()));
|
|
|
|
Gdk::RGBA c;
|
|
|
|
cr->set_line_width (1.0);
|
|
|
|
// draw f(x)=0.5 line
|
|
c = style->get_border_color(state);
|
|
cr->set_source_rgb (c.get_red(), c.get_green(), c.get_blue());
|
|
std::valarray<double> ds (1);
|
|
ds[0] = 4;
|
|
cr->set_dash (ds, 0);
|
|
cr->move_to (double(graphX) + 1.5, double(graphY - graphH / 2) - 0.5);
|
|
cr->rel_line_to (double(graphW - 3), 0.);
|
|
cr->stroke ();
|
|
|
|
cr->unset_dash ();
|
|
|
|
cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL);
|
|
|
|
cr->set_line_width (1.0);
|
|
|
|
// draw the left colored bar
|
|
if (leftBar) {
|
|
// first the background
|
|
int bWidth = getBarWidth();
|
|
BackBuffer *bb = this;
|
|
leftBar->setDrawRectangle(win, 1, graphY - graphH + 1, bWidth - 2, graphH - 2);
|
|
leftBar->expose(bb);
|
|
|
|
// now the border
|
|
c = style->get_border_color(state);
|
|
cr->set_source_rgb (c.get_red(), c.get_green(), c.get_blue());
|
|
cr->rectangle(0.5, graphY - graphH + 0.5, bWidth - 1, graphH - 1);
|
|
cr->stroke();
|
|
}
|
|
|
|
// draw the bottom colored bar
|
|
if (bottomBar) {
|
|
// first the background
|
|
int bWidth = getBarWidth();
|
|
BackBuffer *bb = this;
|
|
bottomBar->setDrawRectangle(win, graphX + 1, graphY + CBAR_MARGIN + 1, graphW - 2, bWidth - 2);
|
|
bottomBar->expose(bb);
|
|
|
|
// now the border
|
|
c = style->get_border_color(state);
|
|
cr->set_source_rgb (c.get_red(), c.get_green(), c.get_blue());
|
|
cr->rectangle(graphX + 0.5, graphY + CBAR_MARGIN + 0.5, graphW - 1, bWidth - 1 );
|
|
cr->stroke();
|
|
}
|
|
|
|
cr->set_line_cap(Cairo::LINE_CAP_BUTT);
|
|
|
|
// draw the pipette values
|
|
if (pipetteR > -1.f || pipetteG > -1.f || pipetteB > -1.f) {
|
|
cr->set_line_width (0.75);
|
|
cr->set_source_rgb (c.get_red(), c.get_green(), c.get_blue());
|
|
int n = 0;
|
|
|
|
if (pipetteR > -1.f) {
|
|
++n;
|
|
}
|
|
|
|
if (pipetteG > -1.f) {
|
|
++n;
|
|
}
|
|
|
|
if (pipetteB > -1.f) {
|
|
++n;
|
|
}
|
|
|
|
if (n > 1) {
|
|
if (pipetteR > -1.f) {
|
|
cr->set_source_rgba (1., 0., 0., 0.5); // WARNING: assuming that red values are stored in pipetteR, which might not be the case!
|
|
cr->move_to (double(graphX) + 1.5 + double(graphW - 3)*pipetteR, double(graphY) - 1.5);
|
|
cr->rel_line_to (0, double(-graphH + 3));
|
|
cr->stroke ();
|
|
}
|
|
|
|
if (pipetteG > -1.f) {
|
|
cr->set_source_rgba (0., 1., 0., 0.5); // WARNING: assuming that green values are stored in pipetteG, which might not be the case!
|
|
cr->move_to (double(graphX) + 1.5 + double(graphW - 3)*pipetteG, double(graphY) - 1.5);
|
|
cr->rel_line_to (0, double(-graphH + 3));
|
|
cr->stroke ();
|
|
}
|
|
|
|
if (pipetteB > -1.f) {
|
|
cr->set_source_rgba (0., 0., 1., 0.5); // WARNING: assuming that blue values are stored in pipetteB, which might not be the case!
|
|
cr->move_to (double(graphX) + 1.5 + double(graphW - 3)*pipetteB, double(graphY) - 1.5);
|
|
cr->rel_line_to (0, double(-graphH + 3));
|
|
cr->stroke ();
|
|
}
|
|
}
|
|
|
|
if (pipetteVal > -1.f) {
|
|
cr->set_line_width (2.);
|
|
c = style->get_color (state);
|
|
cr->set_source_rgb (c.get_red(), c.get_green(), c.get_blue());
|
|
cr->move_to (double(graphX) + 1.5 + double(graphW - 3)*pipetteVal, double(graphY) - 1.5);
|
|
cr->rel_line_to (0, double(-graphH + 3));
|
|
cr->stroke ();
|
|
cr->set_line_width (1.);
|
|
}
|
|
}
|
|
|
|
// draw the color feedback of the control points
|
|
if (colorProvider) {
|
|
|
|
//if (curve.type!=FCT_Parametric)
|
|
for (int i = 0; i < (int)curve.x.size(); ++i) {
|
|
|
|
if (curve.x.at(i) != -1.) {
|
|
int coloredLineWidth = min( max(75, graphW) / 75, 8 );
|
|
|
|
cr->set_line_width (coloredLineWidth);
|
|
colorProvider->colorForValue(curve.x.at(i), curve.y.at(i), CCET_VERTICAL_BAR, colorCallerId, this);
|
|
cr->set_source_rgb (ccRed, ccGreen, ccBlue);
|
|
|
|
if ( i == lit_point && (editedHandle & (FCT_EditedHandle_CPointUD | FCT_EditedHandle_CPoint | FCT_EditedHandle_CPointX)) ) {
|
|
cr->set_line_width (2 * coloredLineWidth);
|
|
}
|
|
|
|
cr->move_to (double(graphX) + 1 + innerW * curve.x.at(i), double(graphY - 1));
|
|
cr->rel_line_to (0., -innerH);
|
|
cr->stroke ();
|
|
cr->set_line_width (coloredLineWidth);
|
|
|
|
// draw the lit_point's horizontal line
|
|
bool drawHLine = false;
|
|
|
|
if (edited_point > -1) {
|
|
if (i == edited_point) {
|
|
cr->set_line_width (2 * coloredLineWidth);
|
|
drawHLine = true;
|
|
}
|
|
} else if (i == lit_point) {
|
|
if ( (area & (FCT_Area_H | FCT_Area_V | FCT_Area_Point)) || editedHandle == FCT_EditedHandle_CPointUD) {
|
|
if (editedHandle & (FCT_EditedHandle_CPointUD | FCT_EditedHandle_CPoint | FCT_EditedHandle_CPointY)) {
|
|
cr->set_line_width (2 * coloredLineWidth);
|
|
drawHLine = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (drawHLine) {
|
|
int point = edited_point > -1 ? edited_point : lit_point;
|
|
colorProvider->colorForValue(curve.x.at(i), curve.y.at(i), CCET_HORIZONTAL_BAR, colorCallerId, this);
|
|
cr->set_source_rgb (ccRed, ccGreen, ccBlue);
|
|
|
|
cr->move_to (double(graphX + 1) , double(graphY - 1) - innerH * curve.y.at(point));
|
|
cr->rel_line_to (innerW, 0.);
|
|
cr->stroke ();
|
|
}
|
|
}
|
|
}
|
|
|
|
// endif
|
|
cr->set_line_width (1.0);
|
|
} else {
|
|
cr->set_source_rgb (0.5, 0.0, 0.0);
|
|
|
|
if (edited_point > -1 || ((lit_point > -1) && ((area & (FCT_Area_H | FCT_Area_V | FCT_Area_Point)) || editedHandle == FCT_EditedHandle_CPointUD)) ) {
|
|
// draw the lit_point's vertical line
|
|
if (edited_point > -1 || (editedHandle & (FCT_EditedHandle_CPointUD | FCT_EditedHandle_CPoint | FCT_EditedHandle_CPointY))) {
|
|
cr->set_line_width (2.0);
|
|
}
|
|
|
|
int point = edited_point > -1 ? edited_point : lit_point;
|
|
cr->move_to (double(graphX) + 1 + innerW * curve.x.at(point), double(graphY - 1));
|
|
cr->rel_line_to (0., -innerH);
|
|
cr->stroke ();
|
|
cr->set_line_width (1.0);
|
|
|
|
// draw the lit_point's horizontal line
|
|
if (editedHandle & (FCT_EditedHandle_CPointUD | FCT_EditedHandle_CPoint | FCT_EditedHandle_CPointY)) {
|
|
cr->set_line_width (2.0);
|
|
}
|
|
|
|
cr->move_to (double(graphX + 1) , double(graphY - 1) - innerH * curve.y.at(point));
|
|
cr->rel_line_to (innerW, 0.);
|
|
cr->stroke ();
|
|
cr->set_line_width (1.0);
|
|
}
|
|
}
|
|
|
|
cr->set_line_cap(Cairo::LINE_CAP_SQUARE);
|
|
|
|
// draw the graph's borders:
|
|
c = style->get_border_color(state);
|
|
cr->set_source_rgb (c.get_red(), c.get_green(), c.get_blue());
|
|
cr->rectangle(double(graphX) + 0.5, double(graphY) - 0.5, double(graphW - 1), double(-graphH + 1));
|
|
cr->stroke ();
|
|
|
|
double lineMinLength = 1. / graphW * SQUARE * 0.9;
|
|
|
|
if (tanHandlesDisplayed && lit_point != -1 && getHandles(lit_point) && curve.x.at(lit_point) != -1.) {
|
|
double x = double(graphX + 1) + innerW * curve.x.at(lit_point);
|
|
double y = double(graphY) - innerH * curve.y.at(lit_point);
|
|
double x2;
|
|
double square;
|
|
bool crossingTheFrame;
|
|
|
|
// left handle is yellow
|
|
// TODO: finding a way to set the left handle color for flat curve editor
|
|
cr->set_source_rgb (1.0, 1.0, 0.0);
|
|
|
|
// draw tangential vectors
|
|
|
|
crossingTheFrame = false;
|
|
|
|
// We display the line only if it's longer than the handle knot half-size...
|
|
if (leftTanX < -0.00001) {
|
|
leftTanX += 1.0;
|
|
crossingTheFrame = true;
|
|
}
|
|
|
|
x2 = double(graphX + 1) + innerW * leftTanX;
|
|
|
|
if (curve.x.at(lit_point) - leftTanX > lineMinLength || crossingTheFrame) {
|
|
// The left tangential vector reappear on the right side
|
|
// draw the line
|
|
cr->move_to (x, y);
|
|
|
|
if (crossingTheFrame) {
|
|
cr->line_to (double(graphX + 1), y);
|
|
cr->stroke ();
|
|
cr->move_to (double(graphX) + innerW, y);
|
|
}
|
|
|
|
cr->line_to (x2, y);
|
|
cr->stroke ();
|
|
}
|
|
|
|
// draw tangential knot
|
|
square = area == FCT_Area_LeftTan ? SQUARE * 2. : SQUARE;
|
|
cr->rectangle(x2 - square, y - square, 2.*square, 2.*square);
|
|
cr->fill();
|
|
|
|
// right handle is blue
|
|
// TODO: finding a way to set the right handle color for flat curve editor
|
|
cr->set_source_rgb (0.0, 0.0, 1.0);
|
|
|
|
// draw tangential vectors
|
|
|
|
crossingTheFrame = false;
|
|
|
|
// We display the line only if it's longer than the handle knot half-size...
|
|
if (rightTanX > 1.00001) {
|
|
rightTanX -= 1.0;
|
|
crossingTheFrame = true;
|
|
}
|
|
|
|
x2 = double(graphX + 1) + innerW * rightTanX;
|
|
|
|
if (rightTanX - curve.x.at(lit_point) > lineMinLength || crossingTheFrame) {
|
|
// The left tangential vector reappear on the right side
|
|
// draw the line
|
|
cr->move_to (x, y);
|
|
|
|
if (crossingTheFrame) {
|
|
cr->line_to (double(graphX) + innerW, y);
|
|
cr->stroke ();
|
|
cr->move_to (double(graphX + 1), y);
|
|
}
|
|
|
|
cr->line_to (x2, y);
|
|
cr->stroke ();
|
|
}
|
|
|
|
// draw tangential knot
|
|
square = area == FCT_Area_RightTan ? SQUARE * 2. : SQUARE;
|
|
cr->rectangle(x2 - square, y - square, 2.*square, 2.*square);
|
|
cr->fill();
|
|
}
|
|
|
|
// draw curve
|
|
c = style->get_color(state);
|
|
cr->set_source_rgb (c.get_red(), c.get_green(), c.get_blue());
|
|
float graphH_ = float(graphH - 3);
|
|
float graphX_ = float(graphX) + 1.5;
|
|
float graphY_ = float(graphY) - 1.5;
|
|
cr->move_to (graphX_, getVal(point, 0) * -graphH_ + graphY_);
|
|
|
|
for (int i = 1; i < graphW - 2; ++i) {
|
|
cr->line_to (float(i) + graphX_, getVal(point, i) * -graphH_ + graphY_);
|
|
}
|
|
|
|
cr->stroke ();
|
|
|
|
// draw bullets
|
|
//if (curve.type!=FCT_Parametric)
|
|
for (int i = 0; i < (int)curve.x.size(); ++i) {
|
|
if (curve.x.at(i) != -1.) {
|
|
if (i == edited_point) {
|
|
cr->set_source_rgb (1.0, 0.0, 0.0);
|
|
} else if (i == lit_point) {
|
|
if (colorProvider && edited_point == -1) {
|
|
colorProvider->colorForValue(curve.x.at(i), curve.y.at(i), CCET_POINT, colorCallerId, this);
|
|
cr->set_source_rgb (ccRed, ccGreen, ccBlue);
|
|
} else {
|
|
cr->set_source_rgb (1.0, 0.0, 0.0);
|
|
}
|
|
} else if (i == snapToElmt || i == edited_point) {
|
|
cr->set_source_rgb (1.0, 0.0, 0.0);
|
|
} else if (curve.y.at(i) == 0.5) {
|
|
cr->set_source_rgb (0.0, 0.5, 0.0);
|
|
} else {
|
|
cr->set_source_rgb (c.get_red(), c.get_green(), c.get_blue());
|
|
}
|
|
|
|
double x = double(graphX + 1) + innerW * curve.x.at(i); // project (curve.x.at(i), 0, 1, graphW);
|
|
double y = double(graphY - 1) - innerH * curve.y.at(i); // project (curve.y.at(i), 0, 1, graphH);
|
|
|
|
cr->arc (x, y, (double)RADIUS, 0, 2 * M_PI);
|
|
cr->fill ();
|
|
|
|
if (i == edited_point) {
|
|
cr->set_source_rgb (1.0, 0.0, 0.0);
|
|
cr->set_line_width(2.);
|
|
cr->arc (x, y, RADIUS + 3.5, 0, 2 * M_PI);
|
|
cr->stroke();
|
|
cr->set_line_width(1.);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// endif
|
|
|
|
// draw the left and right tangent handles
|
|
if (tanHandlesDisplayed) {
|
|
double halfSquareSizeX = minDistanceX / 2.;
|
|
double halfSquareSizeY = minDistanceY / 2.;
|
|
|
|
// LEFT handle
|
|
|
|
// yellow
|
|
cr->set_source_rgb (1.0, 1.0, 0.0);
|
|
cr->rectangle(double(graphX + 1) + innerW * (leftTanHandle.centerX - halfSquareSizeX),
|
|
double(graphY - 1) - innerH * (leftTanHandle.centerY + halfSquareSizeY),
|
|
innerW * minDistanceX,
|
|
innerW * minDistanceY);
|
|
cr->fill();
|
|
|
|
// RIGHT handle
|
|
|
|
// blue
|
|
cr->set_source_rgb (0.0, 0.0, 1.0);
|
|
cr->rectangle(double(graphX + 1) + innerW * (rightTanHandle.centerX - halfSquareSizeX),
|
|
double(graphY - 1) - innerH * (rightTanHandle.centerY + halfSquareSizeY),
|
|
innerW * minDistanceX,
|
|
innerW * minDistanceY);
|
|
cr->fill();
|
|
}
|
|
|
|
setDirty(false);
|
|
queue_draw();
|
|
}
|
|
|
|
bool MyFlatCurve::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
|
|
{
|
|
Glib::RefPtr<Gdk::Window> win = get_window();
|
|
Gtk::Allocation allocation = get_allocation();
|
|
allocation.set_x(0);
|
|
allocation.set_y(0);
|
|
|
|
// setDrawRectangle will allocate the backbuffer Surface
|
|
if (setDrawRectangle(win, allocation)) {
|
|
setDirty(true);
|
|
|
|
if (prevGraphW > GRAPH_SIZE || graphW > GRAPH_SIZE) {
|
|
curveIsDirty = true;
|
|
}
|
|
}
|
|
|
|
draw ();
|
|
copySurface(cr);
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the X1, X2, Y position of the tangential handles.
|
|
*/
|
|
bool MyFlatCurve::getHandles(int n)
|
|
{
|
|
int N = curve.x.size();
|
|
double prevX, nextX;
|
|
double x, leftTan, rightTan;
|
|
|
|
if (n == -1) {
|
|
return false;
|
|
}
|
|
|
|
x = curve.x.at(n);
|
|
leftTan = curve.leftTangent.at(n);
|
|
rightTan = curve.rightTangent.at(n);
|
|
|
|
if (!n) {
|
|
// first point, the left handle is then computed with the last point's right handle
|
|
prevX = curve.x.at(N - 1) - 1.0;
|
|
nextX = curve.x.at(n + 1);
|
|
} else if (n == N - 1) {
|
|
// last point, the right handle is then computed with the first point's left handle
|
|
prevX = curve.x.at(n - 1);
|
|
nextX = curve.x.at(0) + 1.0;
|
|
} else {
|
|
// last point, the right handle is then computed with the first point's left handle
|
|
prevX = curve.x.at(n - 1);
|
|
nextX = curve.x.at(n + 1);
|
|
}
|
|
|
|
if (leftTan == 0.0) {
|
|
leftTanX = x;
|
|
} else if (leftTan == 1.0) {
|
|
leftTanX = prevX;
|
|
} else {
|
|
leftTanX = (prevX - x) * leftTan + x;
|
|
}
|
|
|
|
if (rightTan == 0.0) {
|
|
rightTanX = x;
|
|
} else if (rightTan == 1.0) {
|
|
rightTanX = nextX;
|
|
} else {
|
|
rightTanX = (nextX - x) * rightTan + x;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MyFlatCurve::handleEvents (GdkEvent* event)
|
|
{
|
|
|
|
CursorShape new_type = cursor_type;
|
|
std::vector<double>::iterator itx, ity, itlt, itrt;
|
|
|
|
snapToElmt = -100;
|
|
bool retval = false;
|
|
int num = (int)curve.x.size();
|
|
|
|
/* graphW and graphH are the size of the graph */
|
|
calcDimensions();
|
|
|
|
if ((graphW < 0) || (graphH < 0)) {
|
|
return false;
|
|
}
|
|
|
|
minDistanceX = double(MIN_DISTANCE) / double(graphW - 1);
|
|
minDistanceY = double(MIN_DISTANCE) / double(graphH - 1);
|
|
|
|
switch (event->type) {
|
|
|
|
case Gdk::BUTTON_PRESS:
|
|
if (edited_point == -1) { //curve.type!=FCT_Parametric) {
|
|
if (event->button.button == 1) {
|
|
buttonPressed = true;
|
|
add_modal_grab ();
|
|
|
|
// get the pointer position
|
|
getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint != 0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state));
|
|
getMouseOverArea();
|
|
|
|
// hide the tangent handles
|
|
tanHandlesDisplayed = false;
|
|
|
|
// Action on BUTTON_PRESS and no edited point
|
|
switch (area) {
|
|
|
|
case (FCT_Area_Insertion):
|
|
new_type = CSMove;
|
|
|
|
/* insert a new control point */
|
|
if (num > 0) {
|
|
if (clampedX > curve.x.at(closest_point)) {
|
|
++closest_point;
|
|
}
|
|
}
|
|
|
|
itx = curve.x.begin();
|
|
ity = curve.y.begin();
|
|
itlt = curve.leftTangent.begin();
|
|
itrt = curve.rightTangent.begin();
|
|
|
|
for (int i = 0; i < closest_point; i++) {
|
|
itx++;
|
|
ity++;
|
|
itlt++;
|
|
itrt++;
|
|
}
|
|
|
|
curve.x.insert (itx, 0);
|
|
curve.y.insert (ity, 0);
|
|
curve.leftTangent.insert (itlt, 0);
|
|
curve.rightTangent.insert (itrt, 0);
|
|
num++;
|
|
|
|
if (mod_type & GDK_CONTROL_MASK) {
|
|
clampedY = point.getVal01(clampedX);
|
|
}
|
|
|
|
// the graph is refreshed only if a new point is created
|
|
curve.x.at(closest_point) = clampedX;
|
|
curve.y.at(closest_point) = clampedY;
|
|
curve.leftTangent.at(closest_point) = 0.35;
|
|
curve.rightTangent.at(closest_point) = 0.35;
|
|
|
|
curveIsDirty = true;
|
|
setDirty(true);
|
|
draw ();
|
|
notifyListener ();
|
|
|
|
lit_point = closest_point;
|
|
|
|
// point automatically activated
|
|
editedHandle = FCT_EditedHandle_CPoint;
|
|
ugpX = curve.x.at(lit_point);
|
|
ugpY = curve.y.at(lit_point);
|
|
break;
|
|
|
|
case (FCT_Area_Point):
|
|
new_type = CSMove;
|
|
editedHandle = FCT_EditedHandle_CPoint;
|
|
ugpX = curve.x.at(lit_point);
|
|
ugpY = curve.y.at(lit_point);
|
|
break;
|
|
|
|
case (FCT_Area_H):
|
|
case (FCT_Area_V):
|
|
new_type = CSMove;
|
|
editedHandle = FCT_EditedHandle_CPointUD;
|
|
ugpX = curve.x.at(lit_point);
|
|
ugpY = curve.y.at(lit_point);
|
|
break;
|
|
|
|
case (FCT_Area_LeftTan):
|
|
new_type = CSEmpty;
|
|
editedHandle = FCT_EditedHandle_LeftTan;
|
|
ugpX = curve.leftTangent.at(lit_point);
|
|
break;
|
|
|
|
case (FCT_Area_RightTan):
|
|
new_type = CSEmpty;
|
|
editedHandle = FCT_EditedHandle_RightTan;
|
|
ugpX = curve.rightTangent.at(lit_point);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
} else if (event->button.button == 3) {
|
|
/* get the pointer position */
|
|
getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint != 0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state));
|
|
getMouseOverArea();
|
|
|
|
if (lit_point > -1 && lit_point != edited_point) {
|
|
if (editedHandle == FCT_EditedHandle_None) {
|
|
if (area == FCT_Area_Point || area == FCT_Area_V) {
|
|
// the cursor is close to an existing point
|
|
if (!coordinateAdjuster->is_visible()) {
|
|
coordinateAdjuster->showMe(this);
|
|
}
|
|
|
|
new_type = CSArrow;
|
|
tanHandlesDisplayed = false;
|
|
edited_point = lit_point;
|
|
setDirty(true);
|
|
draw ();
|
|
std::vector<CoordinateAdjuster::Boundaries> newBoundaries(4);
|
|
unsigned int size = curve.x.size();
|
|
|
|
if (edited_point == 0) {
|
|
newBoundaries.at(0).minVal = 0.;
|
|
newBoundaries.at(0).maxVal = curve.x.at(1);
|
|
} else if (edited_point == size - 1) {
|
|
newBoundaries.at(0).minVal = curve.x.at(edited_point - 1);
|
|
newBoundaries.at(0).maxVal = 1.;
|
|
} else if (curve.x.size() > 2) {
|
|
newBoundaries.at(0).minVal = curve.x.at(edited_point - 1);
|
|
newBoundaries.at(0).maxVal = curve.x.at(edited_point + 1);
|
|
}
|
|
|
|
newBoundaries.at(1).minVal = 0.;
|
|
newBoundaries.at(1).maxVal = 1.;
|
|
newBoundaries.at(2).minVal = 0.;
|
|
newBoundaries.at(2).maxVal = 1.;
|
|
newBoundaries.at(3).minVal = 0.;
|
|
newBoundaries.at(3).maxVal = 1.;
|
|
retval = true;
|
|
editedPos.at(0) = curve.x.at(edited_point);
|
|
editedPos.at(1) = curve.y.at(edited_point);
|
|
editedPos.at(2) = curve.leftTangent.at(edited_point);
|
|
editedPos.at(3) = curve.rightTangent.at(edited_point);
|
|
coordinateAdjuster->setPos(editedPos);
|
|
coordinateAdjuster->startNumericalAdjustment(newBoundaries);
|
|
}
|
|
}
|
|
}
|
|
|
|
retval = true;
|
|
}
|
|
|
|
if (buttonPressed) {
|
|
retval = true;
|
|
}
|
|
} else { // if (edited_point > -1)
|
|
if (event->button.button == 3) {
|
|
// do we edit another point?
|
|
/* get the pointer position */
|
|
getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint != 0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state));
|
|
getMouseOverArea();
|
|
|
|
if (area == FCT_Area_Point || area == FCT_Area_V) {
|
|
// the cursor is close to an existing point
|
|
if (lit_point != edited_point) {
|
|
edited_point = lit_point;
|
|
setDirty(true);
|
|
draw ();
|
|
std::vector<CoordinateAdjuster::Boundaries> newBoundaries(4);
|
|
unsigned int size = curve.x.size();
|
|
|
|
if (edited_point == 0) {
|
|
newBoundaries.at(0).minVal = 0.;
|
|
newBoundaries.at(0).maxVal = curve.x.at(1);
|
|
} else if (edited_point == size - 1) {
|
|
newBoundaries.at(0).minVal = curve.x.at(edited_point - 1);
|
|
newBoundaries.at(0).maxVal = 1.;
|
|
} else if (curve.x.size() > 2) {
|
|
newBoundaries.at(0).minVal = curve.x.at(edited_point - 1);
|
|
newBoundaries.at(0).maxVal = curve.x.at(edited_point + 1);
|
|
}
|
|
|
|
newBoundaries.at(1).minVal = 0.;
|
|
newBoundaries.at(1).maxVal = 1.;
|
|
newBoundaries.at(2).minVal = 0.;
|
|
newBoundaries.at(2).maxVal = 1.;
|
|
newBoundaries.at(3).minVal = 0.;
|
|
newBoundaries.at(3).maxVal = 1.;
|
|
editedPos.at(0) = curve.x.at(edited_point);
|
|
editedPos.at(1) = curve.y.at(edited_point);
|
|
editedPos.at(2) = curve.leftTangent.at(edited_point);
|
|
editedPos.at(3) = curve.rightTangent.at(edited_point);
|
|
coordinateAdjuster->switchAdjustedPoint(editedPos, newBoundaries);
|
|
retval = true;
|
|
}
|
|
} else if (area == FCT_Area_Insertion) {
|
|
// the cursor is inside the graph but away from existing points
|
|
new_type = CSPlus;
|
|
curveIsDirty = true;
|
|
stopNumericalAdjustment();
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case Gdk::BUTTON_RELEASE:
|
|
if (edited_point == -1) { //curve.type!=FCT_Parametric) {
|
|
if (buttonPressed && event->button.button == 1) {
|
|
int src, dst;
|
|
buttonPressed = false;
|
|
remove_modal_grab ();
|
|
|
|
// Removing any deleted point if we were previously modifying the point position
|
|
if (editedHandle & (FCT_EditedHandle_CPoint | FCT_EditedHandle_CPointX | FCT_EditedHandle_CPointY)) {
|
|
/* delete inactive points: */
|
|
itx = curve.x.begin();
|
|
ity = curve.y.begin();
|
|
itlt = curve.leftTangent.begin();
|
|
itrt = curve.rightTangent.begin();
|
|
|
|
for (src = dst = 0; src < num; ++src)
|
|
if (curve.x.at(src) >= 0.0) {
|
|
curve.x.at(dst) = curve.x.at(src);
|
|
curve.y.at(dst) = curve.y.at(src);
|
|
curve.leftTangent.at(dst) = curve.leftTangent.at(src);
|
|
curve.rightTangent.at(dst) = curve.rightTangent.at(src);
|
|
++dst;
|
|
++itx;
|
|
++ity;
|
|
++itlt;
|
|
++itrt;
|
|
}
|
|
|
|
if (dst < src) {
|
|
|
|
// curve cleanup
|
|
curve.x.erase (itx, curve.x.end());
|
|
curve.y.erase (ity, curve.y.end());
|
|
curve.leftTangent.erase (itlt, curve.leftTangent.end());
|
|
curve.rightTangent.erase (itrt, curve.rightTangent.end());
|
|
|
|
if (curve.x.empty()) {
|
|
curve.x.push_back (0.5);
|
|
curve.y.push_back (0.5);
|
|
curve.leftTangent.push_back (0.3);
|
|
curve.rightTangent.push_back (0.3);
|
|
curveIsDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
editedHandle = FCT_EditedHandle_None;
|
|
lit_point = -1;
|
|
|
|
// get the pointer position
|
|
getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint != 0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state));
|
|
getMouseOverArea();
|
|
|
|
switch (area) {
|
|
|
|
case (FCT_Area_Insertion):
|
|
new_type = CSArrow;
|
|
break;
|
|
|
|
case (FCT_Area_Point):
|
|
new_type = CSMove;
|
|
break;
|
|
|
|
case (FCT_Area_H):
|
|
new_type = CSResizeHeight;
|
|
break;
|
|
|
|
case (FCT_Area_V):
|
|
new_type = CSMove;
|
|
break;
|
|
|
|
case (FCT_Area_LeftTan):
|
|
new_type = CSEmpty;
|
|
break;
|
|
|
|
case (FCT_Area_RightTan):
|
|
new_type = CSEmpty;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
setDirty(true);
|
|
draw ();
|
|
retval = true;
|
|
//notifyListener ();
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case Gdk::MOTION_NOTIFY:
|
|
if (curve.type == FCT_Linear || curve.type == FCT_MinMaxCPoints) {
|
|
|
|
int previous_lit_point = lit_point;
|
|
enum MouseOverAreas prevArea = area;
|
|
|
|
snapToMinDistY = snapToMinDistX = 10.;
|
|
snapToValY = snapToValX = 0.;
|
|
snapToElmt = -100;
|
|
|
|
// get the pointer position
|
|
getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint != 0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state));
|
|
getMouseOverArea();
|
|
|
|
if (editedHandle == FCT_EditedHandle_CPointUD) {
|
|
double dX = deltaX;
|
|
double dY = deltaY;
|
|
|
|
if (dX < 0.) {
|
|
dX = -dX;
|
|
}
|
|
|
|
if (dY < 0.) {
|
|
dY = -dY;
|
|
}
|
|
|
|
if (dX > dY) {
|
|
editedHandle = FCT_EditedHandle_CPointX;
|
|
area = FCT_Area_V;
|
|
new_type = CSResizeWidth;
|
|
} else {
|
|
editedHandle = FCT_EditedHandle_CPointY;
|
|
area = FCT_Area_H;
|
|
new_type = CSResizeHeight;
|
|
}
|
|
}
|
|
|
|
switch (editedHandle) {
|
|
|
|
case (FCT_EditedHandle_None): {
|
|
|
|
if ((lit_point != -1 && previous_lit_point != lit_point) && (area & (FCT_Area_V | FCT_Area_Point)) && edited_point == -1) {
|
|
|
|
bool sameSide = false;
|
|
|
|
// display the handles
|
|
tanHandlesDisplayed = true;
|
|
|
|
if (curve.x.at(lit_point) < 3.*minDistanceX) {
|
|
// lit_point too near of the left border -> both handles are displayed on the right of the vertical line
|
|
rightTanHandle.centerX = leftTanHandle.centerX = curve.x.at(lit_point) + 2.*minDistanceX;
|
|
sameSide = true;
|
|
} else if (curve.x.at(lit_point) > 1. - 3.*minDistanceX) {
|
|
// lit_point too near of the left border -> both handles are displayed on the right of the vertical line
|
|
rightTanHandle.centerX = leftTanHandle.centerX = curve.x.at(lit_point) - 2.*minDistanceX;
|
|
sameSide = true;
|
|
} else {
|
|
leftTanHandle.centerX = curve.x.at(lit_point) - 2.*minDistanceX;
|
|
rightTanHandle.centerX = curve.x.at(lit_point) + 2.*minDistanceX;
|
|
}
|
|
|
|
if (sameSide) {
|
|
if (clampedY > 1. - 2.*minDistanceY) {
|
|
// lit_point too near of the top border
|
|
leftTanHandle.centerY = 1. - minDistanceY;
|
|
rightTanHandle.centerY = 1. - 3.*minDistanceY;
|
|
} else if (clampedY < 2.*minDistanceY) {
|
|
// lit_point too near of the bottom border
|
|
leftTanHandle.centerY = 3.*minDistanceY;
|
|
rightTanHandle.centerY = minDistanceY;
|
|
} else {
|
|
leftTanHandle.centerY = clampedY + minDistanceY;
|
|
rightTanHandle.centerY = clampedY - minDistanceY;
|
|
}
|
|
} else {
|
|
if (clampedY > 1. - minDistanceY) {
|
|
rightTanHandle.centerY = leftTanHandle.centerY = 1. - minDistanceY;
|
|
} else if (clampedY < minDistanceY) {
|
|
rightTanHandle.centerY = leftTanHandle.centerY = minDistanceY;
|
|
} else {
|
|
rightTanHandle.centerY = leftTanHandle.centerY = clampedY;
|
|
}
|
|
}
|
|
} else if (lit_point == -1 || edited_point > -1) {
|
|
tanHandlesDisplayed = false;
|
|
}
|
|
|
|
|
|
if (edited_point == -1) {
|
|
switch (area) {
|
|
|
|
case (FCT_Area_Insertion):
|
|
new_type = CSPlus;
|
|
break;
|
|
|
|
case (FCT_Area_Point):
|
|
|
|
//new_type = CSMove;
|
|
//break;
|
|
case (FCT_Area_V):
|
|
new_type = CSMove;
|
|
break;
|
|
|
|
case (FCT_Area_H):
|
|
new_type = CSResizeHeight;
|
|
break;
|
|
|
|
case (FCT_Area_LeftTan):
|
|
new_type = CSMoveLeft;
|
|
break;
|
|
|
|
case (FCT_Area_RightTan):
|
|
new_type = CSMoveRight;
|
|
break;
|
|
|
|
case (FCT_Area_None):
|
|
default:
|
|
new_type = CSArrow;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((lit_point != previous_lit_point) || (prevArea != area)) {
|
|
setDirty(true);
|
|
draw ();
|
|
}
|
|
|
|
if (coordinateAdjuster->is_visible() && edited_point == -1) {
|
|
if (lit_point > -1) {
|
|
if (lit_point != previous_lit_point) {
|
|
editedPos.at(0) = curve.x.at(lit_point);
|
|
editedPos.at(1) = curve.y.at(lit_point);
|
|
editedPos.at(2) = curve.leftTangent.at(lit_point);
|
|
editedPos.at(3) = curve.rightTangent.at(lit_point);
|
|
}
|
|
|
|
coordinateAdjuster->setPos(editedPos);
|
|
} else if (area == FCT_Area_Insertion) {
|
|
editedPos.at(0) = clampedX;
|
|
editedPos.at(1) = clampedY;
|
|
editedPos.at(2) = 0.;
|
|
editedPos.at(3) = 0.;
|
|
coordinateAdjuster->setPos(editedPos);
|
|
} else {
|
|
editedPos.at(0) = editedPos.at(1) = editedPos.at(2) = editedPos.at(3) = 0;
|
|
coordinateAdjuster->setPos(editedPos);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case (FCT_EditedHandle_CPoint):
|
|
movePoint(true, true);
|
|
|
|
if (coordinateAdjuster->is_visible()) {
|
|
editedPos.at(0) = curve.x.at(lit_point);
|
|
editedPos.at(1) = curve.y.at(lit_point);
|
|
coordinateAdjuster->setPos(editedPos);
|
|
}
|
|
|
|
break;
|
|
|
|
case (FCT_EditedHandle_CPointX):
|
|
movePoint(true, false);
|
|
|
|
if (coordinateAdjuster->is_visible()) {
|
|
editedPos.at(0) = curve.x.at(lit_point);
|
|
coordinateAdjuster->setPos(editedPos);
|
|
}
|
|
|
|
break;
|
|
|
|
case (FCT_EditedHandle_CPointY):
|
|
movePoint(false, true);
|
|
|
|
if (coordinateAdjuster->is_visible()) {
|
|
editedPos.at(1) = curve.y.at(lit_point);
|
|
coordinateAdjuster->setPos(editedPos);
|
|
}
|
|
|
|
break;
|
|
|
|
case (FCT_EditedHandle_LeftTan): {
|
|
double prevValue = curve.leftTangent.at(lit_point);
|
|
|
|
ugpX -= deltaX * 3;
|
|
ugpX = CLAMP(ugpX, 0., 1.);
|
|
|
|
if (snapTo) {
|
|
// since this handle can only move in one direction, we can reuse the snapCoordinateX mechanism
|
|
snapCoordinateX(0.0, ugpX);
|
|
snapCoordinateX(0.35, ugpX);
|
|
snapCoordinateX(0.5, ugpX);
|
|
snapCoordinateX(1.0, ugpX);
|
|
curve.leftTangent.at(lit_point) = snapToValX;
|
|
} else {
|
|
curve.leftTangent.at(lit_point) = ugpX;
|
|
}
|
|
|
|
if (curve.leftTangent.at(lit_point) != prevValue) {
|
|
curveIsDirty = true;
|
|
setDirty(true);
|
|
draw ();
|
|
notifyListener ();
|
|
|
|
if (coordinateAdjuster->is_visible()) {
|
|
editedPos.at(2) = curve.leftTangent.at(lit_point);
|
|
coordinateAdjuster->setPos(editedPos);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case (FCT_EditedHandle_RightTan): {
|
|
double prevValue = curve.rightTangent.at(lit_point);
|
|
|
|
ugpX += deltaX * 3;
|
|
ugpX = CLAMP(ugpX, 0., 1.);
|
|
|
|
if (snapTo) {
|
|
// since this handle can only move in one direction, we can reuse the snapCoordinateX mechanism
|
|
snapCoordinateX(0.0, ugpX);
|
|
snapCoordinateX(0.35, ugpX);
|
|
snapCoordinateX(0.5, ugpX);
|
|
snapCoordinateX(1.0, ugpX);
|
|
curve.rightTangent.at(lit_point) = snapToValX;
|
|
} else {
|
|
curve.rightTangent.at(lit_point) = ugpX;
|
|
}
|
|
|
|
if (curve.rightTangent.at(lit_point) != prevValue) {
|
|
curveIsDirty = true;
|
|
setDirty(true);
|
|
draw ();
|
|
notifyListener ();
|
|
editedPos.at(3) = curve.rightTangent.at(lit_point);
|
|
coordinateAdjuster->setPos(editedPos);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// already processed before the "switch" instruction
|
|
//case (FCT_EditedHandle_CPointUD):
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (edited_point == -1) {
|
|
if (lit_point == -1) {
|
|
editedPos.at(0) = editedPos.at(1) = editedPos.at(2) = editedPos.at(3) = 0;
|
|
} else if (editedPos.at(0) != curve.x.at(lit_point)
|
|
|| editedPos.at(1) != curve.y.at(lit_point)
|
|
|| editedPos.at(2) != curve.leftTangent.at(lit_point)
|
|
|| editedPos.at(3) != curve.rightTangent.at(lit_point)) {
|
|
editedPos.at(0) = curve.x.at(lit_point);
|
|
editedPos.at(1) = curve.y.at(lit_point);
|
|
editedPos.at(2) = curve.leftTangent.at(lit_point);
|
|
editedPos.at(3) = curve.rightTangent.at(lit_point);
|
|
coordinateAdjuster->setPos(editedPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
retval = true;
|
|
break;
|
|
|
|
case Gdk::LEAVE_NOTIFY:
|
|
|
|
// Pointer can LEAVE even when dragging the point, so we don't modify the cursor in this case
|
|
// The cursor will have to LEAVE another time after the drag...
|
|
if (editedHandle == FCT_EditedHandle_None) {
|
|
new_type = CSArrow;
|
|
lit_point = -1;
|
|
tanHandlesDisplayed = false;
|
|
pipetteR = pipetteG = pipetteB = -1.f;
|
|
setDirty(true);
|
|
draw ();
|
|
}
|
|
|
|
retval = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (new_type != cursor_type) {
|
|
cursor_type = new_type;
|
|
CursorManager::setCursorOfMainWindow(get_window(), cursor_type);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
void MyFlatCurve::pipetteMouseOver (CurveEditor *ce, EditDataProvider *provider, int modifierKey)
|
|
{
|
|
if (!provider) {
|
|
// occurs when leaving the preview area -> cleanup the curve editor
|
|
pipetteR = pipetteG = pipetteB = -1.f;
|
|
lit_point = -1;
|
|
return;
|
|
}
|
|
|
|
pipetteR = provider->pipetteVal[0];
|
|
pipetteG = provider->pipetteVal[1];
|
|
pipetteB = provider->pipetteVal[2];
|
|
pipetteVal = 0.f;
|
|
|
|
if (listener) {
|
|
pipetteVal = listener->blendPipetteValues(ce, pipetteR, pipetteG, pipetteB);
|
|
} else {
|
|
int n = 0;
|
|
|
|
if (pipetteR != -1.f) {
|
|
pipetteVal += pipetteR;
|
|
++n;
|
|
}
|
|
|
|
if (pipetteG != -1.f) {
|
|
pipetteVal += pipetteG;
|
|
++n;
|
|
}
|
|
|
|
if (pipetteB != -1.f) {
|
|
pipetteVal += pipetteB;
|
|
++n;
|
|
}
|
|
|
|
if (n > 1) {
|
|
pipetteVal /= n;
|
|
} else if (!n) {
|
|
pipetteVal = -1.f;
|
|
}
|
|
}
|
|
|
|
snapToElmt = -100;
|
|
|
|
/* graphW and graphH are the size of the graph */
|
|
calcDimensions();
|
|
|
|
if ((graphW < 0) || (graphH < 0)) {
|
|
return;
|
|
}
|
|
|
|
int previous_lit_point = lit_point;
|
|
|
|
// hide the handles
|
|
tanHandlesDisplayed = false;
|
|
|
|
// get the pointer position
|
|
int px = graphX + int(float(graphW) * pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here!
|
|
getCursorPosition(Gdk::EventType(Gdk::BUTTON_PRESS), false, px, graphY, Gdk::ModifierType(modifierKey));
|
|
|
|
if (edited_point == -1) {
|
|
getMouseOverArea();
|
|
}
|
|
|
|
if (area == FCT_Area_Point) {
|
|
area = FCT_Area_V;
|
|
}
|
|
|
|
snapToMinDistY = snapToMinDistX = 10.;
|
|
snapToValY = snapToValX = 0.;
|
|
|
|
if (edited_point == -1) {
|
|
if (editedHandle == FCT_EditedHandle_None && lit_point != previous_lit_point) {
|
|
setDirty(true);
|
|
draw ();
|
|
}
|
|
} else {
|
|
draw();
|
|
}
|
|
|
|
if (edited_point == -1) {
|
|
editedPos.at(0) = pipetteVal;
|
|
editedPos.at(1) = point.getVal01(pipetteVal);
|
|
coordinateAdjuster->setPos(editedPos);
|
|
}
|
|
}
|
|
|
|
void MyFlatCurve::pipetteButton1Pressed(EditDataProvider *provider, int modifierKey)
|
|
{
|
|
if (edited_point > -1) {
|
|
return;
|
|
}
|
|
|
|
buttonPressed = true;
|
|
|
|
// get the pointer position
|
|
int px = graphX + int(float(graphW) * pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here!
|
|
getCursorPosition(Gdk::EventType(Gdk::BUTTON_PRESS), false, px, graphY, Gdk::ModifierType(modifierKey));
|
|
getMouseOverArea();
|
|
|
|
// hide the tangent handles
|
|
tanHandlesDisplayed = false;
|
|
|
|
// Action on BUTTON_PRESS and no edited point
|
|
switch (area) {
|
|
|
|
case (FCT_Area_Insertion): {
|
|
rtengine::FlatCurve rtCurve(getPoints(), GRAPH_SIZE);
|
|
|
|
std::vector<double>::iterator itx, ity, itlt, itrt;
|
|
int num = (int)curve.x.size();
|
|
|
|
/* insert a new control point */
|
|
if (num > 0) {
|
|
if (clampedX > curve.x.at(closest_point)) {
|
|
++closest_point;
|
|
}
|
|
}
|
|
|
|
itx = curve.x.begin();
|
|
ity = curve.y.begin();
|
|
itlt = curve.leftTangent.begin();
|
|
itrt = curve.rightTangent.begin();
|
|
|
|
for (int i = 0; i < closest_point; i++) {
|
|
itx++;
|
|
ity++;
|
|
itlt++;
|
|
itrt++;
|
|
}
|
|
|
|
curve.x.insert (itx, 0);
|
|
curve.y.insert (ity, 0);
|
|
curve.leftTangent.insert (itlt, 0);
|
|
curve.rightTangent.insert (itrt, 0);
|
|
num++;
|
|
|
|
// the graph is refreshed only if a new point is created
|
|
curve.x.at(closest_point) = clampedX;
|
|
curve.y.at(closest_point) = clampedY = rtCurve.getVal(pipetteVal);
|
|
curve.leftTangent.at(closest_point) = 0.35;
|
|
curve.rightTangent.at(closest_point) = 0.35;
|
|
|
|
curveIsDirty = true;
|
|
setDirty(true);
|
|
draw ();
|
|
notifyListener ();
|
|
|
|
lit_point = closest_point;
|
|
|
|
// point automatically activated
|
|
editedHandle = FCT_EditedHandle_CPointY;
|
|
ugpX = curve.x.at(lit_point);
|
|
ugpY = curve.y.at(lit_point);
|
|
break;
|
|
}
|
|
|
|
case (FCT_Area_V):
|
|
editedHandle = FCT_EditedHandle_CPointY;
|
|
ugpX = curve.x.at(lit_point);
|
|
ugpY = curve.y.at(lit_point);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
void MyFlatCurve::pipetteButton1Released(EditDataProvider *provider)
|
|
{
|
|
if (edited_point > -1) {
|
|
return;
|
|
}
|
|
|
|
buttonPressed = false;
|
|
remove_modal_grab ();
|
|
|
|
editedHandle = FCT_EditedHandle_None;
|
|
lit_point = -1;
|
|
|
|
// get the pointer position
|
|
int px = graphX + int(float(graphW) * pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here!
|
|
getCursorPosition(Gdk::EventType(Gdk::BUTTON_PRESS), false, px, graphY, Gdk::ModifierType(0));
|
|
getMouseOverArea();
|
|
|
|
setDirty(true);
|
|
draw ();
|
|
//notifyListener ();
|
|
}
|
|
|
|
void MyFlatCurve::pipetteDrag(EditDataProvider *provider, int modifierKey)
|
|
{
|
|
if (edited_point > -1) {
|
|
return;
|
|
}
|
|
|
|
snapToMinDistY = snapToMinDistX = 10.;
|
|
snapToValY = snapToValX = 0.;
|
|
snapToElmt = -100;
|
|
|
|
// get the pointer position
|
|
getCursorPosition(Gdk::MOTION_NOTIFY, false, cursorX + graphX, graphY + provider->deltaScreen.y, Gdk::ModifierType(modifierKey));
|
|
getMouseOverArea();
|
|
|
|
if (editedHandle == FCT_EditedHandle_CPointY) {
|
|
movePoint(false, true, true);
|
|
}
|
|
}
|
|
|
|
void MyFlatCurve::movePoint(bool moveX, bool moveY, bool pipetteDrag)
|
|
{
|
|
|
|
// bounds of the grabbed point
|
|
double leftBound;
|
|
double rightBound;
|
|
double const bottomBound = 0.;
|
|
double const topBound = 1.;
|
|
|
|
// we memorize the previous position of the point, for optimization purpose
|
|
double prevPosX = curve.x.at(lit_point);
|
|
double prevPosY = curve.y.at(lit_point);
|
|
|
|
int nbPoints = (int)curve.x.size();
|
|
|
|
// left and right bound rely on curve periodicity
|
|
leftBound = (lit_point == 0 ) ? (periodic && !snapTo ? curve.x.at(nbPoints - 1) - 1. : 0.) : curve.x.at(lit_point - 1);
|
|
rightBound = (lit_point == nbPoints - 1) ? (periodic && !snapTo ? curve.x.at(0) + 1. : 1.) : curve.x.at(lit_point + 1);
|
|
|
|
double leftDeletionBound = leftBound - minDistanceX;
|
|
double rightDeletionBound = rightBound + minDistanceX;
|
|
double bottomDeletionBound = bottomBound - minDistanceY;
|
|
double topDeletionBound = topBound + minDistanceY;
|
|
|
|
if (moveX) {
|
|
// we memorize the previous position of the point, for optimization purpose
|
|
ugpX += deltaX;
|
|
|
|
// handling periodicity (the first and last point can reappear at the other side of the X range)
|
|
if (periodic) {
|
|
if (snapTo) {
|
|
if (lit_point == 0) {
|
|
snapCoordinateX(0.0, ugpX);
|
|
curve.x.at(0) = snapToValX;
|
|
} else if (lit_point == (nbPoints - 1)) {
|
|
snapCoordinateX(1.0, ugpX);
|
|
curve.x.at(nbPoints - 1) = snapToValX;
|
|
}
|
|
} else if (lit_point == 0 && ugpX < 0.) {
|
|
// the first point has to be placed at the tail of the point list
|
|
std::vector<double>::iterator itx, ity, itlt, itrt;
|
|
|
|
ugpX += 1.;
|
|
leftBound += 1.;
|
|
rightBound += 1.;
|
|
leftDeletionBound += 1.;
|
|
rightDeletionBound += 1.;
|
|
|
|
// adding a copy of the first point to the tail of the list
|
|
curve.x.push_back(curve.x.at(0));
|
|
curve.y.push_back(curve.y.at(0));
|
|
curve.leftTangent.push_back(curve.leftTangent.at(0));
|
|
curve.rightTangent.push_back(curve.rightTangent.at(0));
|
|
|
|
// deleting the first point
|
|
itx = curve.x.begin();
|
|
ity = curve.y.begin();
|
|
itlt = curve.leftTangent.begin();
|
|
itrt = curve.rightTangent.begin();
|
|
|
|
curve.x.erase(itx);
|
|
curve.y.erase(ity);
|
|
curve.leftTangent.erase(itlt);
|
|
curve.rightTangent.erase(itrt);
|
|
|
|
lit_point = nbPoints - 1;
|
|
} else if (lit_point == (nbPoints - 1) && ugpX > 1.) {
|
|
// the last point has to be placed at the head of the point list
|
|
std::vector<double>::iterator itx, ity, itlt, itrt;
|
|
|
|
ugpX -= 1.;
|
|
leftBound -= 1.;
|
|
rightBound -= 1.;
|
|
leftDeletionBound -= 1.;
|
|
rightDeletionBound -= 1.;
|
|
|
|
// adding a copy of the last point to the head of the list
|
|
itx = curve.x.begin();
|
|
ity = curve.y.begin();
|
|
itlt = curve.leftTangent.begin();
|
|
itrt = curve.rightTangent.begin();
|
|
|
|
curve.x.insert(itx, 0);
|
|
curve.y.insert(ity, 0);
|
|
curve.leftTangent.insert(itlt, 0);
|
|
curve.rightTangent.insert(itrt, 0);
|
|
|
|
curve.x.at(0) = curve.x.at(nbPoints);
|
|
curve.y.at(0) = curve.y.at(nbPoints);
|
|
curve.leftTangent.at(0) = curve.leftTangent.at(nbPoints);
|
|
curve.rightTangent.at(0) = curve.rightTangent.at(nbPoints);
|
|
|
|
// deleting the last point
|
|
curve.x.pop_back();
|
|
curve.y.pop_back();
|
|
curve.leftTangent.pop_back();
|
|
curve.rightTangent.pop_back();
|
|
|
|
lit_point = 0;
|
|
}
|
|
}
|
|
|
|
// handling limitations along X axis
|
|
if (ugpX >= rightDeletionBound && nbPoints > 2 && !snapTo) {
|
|
curve.x.at(lit_point) = -1.;
|
|
} else if (ugpX <= leftDeletionBound && nbPoints > 2 && !snapTo) {
|
|
curve.x.at(lit_point) = -1.;
|
|
} else
|
|
// nextPosX is in bounds
|
|
{
|
|
curve.x.at(lit_point) = CLAMP(ugpX, leftBound, rightBound);
|
|
}
|
|
}
|
|
|
|
if (moveY) {
|
|
|
|
// we memorize the previous position of the point, for optimization purpose
|
|
ugpY += deltaY;
|
|
|
|
// the points stay in the bounds (and can't be deleted) in pipette drag mode
|
|
if (pipetteDrag) {
|
|
ugpY = CLAMP(ugpY, 0.0, 1.0);
|
|
}
|
|
|
|
// snapping point to specific values
|
|
if (snapTo && curve.x.at(lit_point) != -1) {
|
|
|
|
// the unclamped grabbed point is brought back in the range
|
|
ugpY = CLAMP(ugpY, 0.0, 1.0);
|
|
|
|
if (lit_point == 0) {
|
|
int prevP = curve.y.size() - 1;
|
|
|
|
if (snapCoordinateY(curve.y.at(prevP), ugpY)) {
|
|
snapToElmt = prevP;
|
|
}
|
|
} else {
|
|
int prevP = lit_point - 1;
|
|
|
|
if (snapCoordinateY(curve.y.at(prevP), ugpY)) {
|
|
snapToElmt = prevP;
|
|
}
|
|
}
|
|
|
|
if (curve.y.size() > 2) {
|
|
if (lit_point == (curve.y.size() - 1)) {
|
|
if (snapCoordinateY(curve.y.at(0), ugpY)) {
|
|
snapToElmt = 0;
|
|
}
|
|
} else {
|
|
int nextP = lit_point + 1;
|
|
|
|
if (snapCoordinateY(curve.y.at(nextP), ugpY)) {
|
|
snapToElmt = nextP;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (snapCoordinateY(1.0, ugpY)) {
|
|
snapToElmt = -3;
|
|
}
|
|
|
|
if (snapCoordinateY(0.5, ugpY)) {
|
|
snapToElmt = -2;
|
|
}
|
|
|
|
if (snapCoordinateY(0.0, ugpY)) {
|
|
snapToElmt = -1;
|
|
}
|
|
|
|
curve.y.at(lit_point) = snapToValY;
|
|
}
|
|
|
|
// Handling limitations along Y axis
|
|
if (ugpY >= topDeletionBound && nbPoints > 2) {
|
|
if (curve.x.at(lit_point) != -1.) {
|
|
deletedPointX = curve.x.at(lit_point);
|
|
curve.x.at(lit_point) = -1.;
|
|
curve.y.at(lit_point) = ugpY; // This is only to force the redraw of the curve
|
|
}
|
|
} else if (ugpY <= bottomDeletionBound && nbPoints > 2) {
|
|
if (curve.x.at(lit_point) != -1.) {
|
|
deletedPointX = curve.x.at(lit_point);
|
|
curve.x.at(lit_point) = -1.;
|
|
curve.y.at(lit_point) = ugpY; // This is only to force the redraw of the curve
|
|
}
|
|
} else {
|
|
// nextPosY is in the bounds
|
|
if (!snapTo) {
|
|
curve.y.at(lit_point) = CLAMP(ugpY, 0.0, 1.0);
|
|
}
|
|
|
|
if (!moveX && curve.x.at(lit_point) == -1.) {
|
|
// bring back the X value of the point if it reappear
|
|
curve.x.at(lit_point) = deletedPointX;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (curve.x.at(lit_point) != prevPosX || curve.y.at(lit_point) != prevPosY) {
|
|
// we recompute the curve only if we have to
|
|
curveIsDirty = true;
|
|
setDirty(true);
|
|
draw ();
|
|
notifyListener ();
|
|
}
|
|
}
|
|
|
|
// Set datas relative to cursor position
|
|
void MyFlatCurve::getCursorPosition(Gdk::EventType evType, bool isHint, int evX, int evY, Gdk::ModifierType modifierKey)
|
|
{
|
|
int tx, ty;
|
|
int prevCursorX, prevCursorY;
|
|
double incrementX = 1. / double(graphW);
|
|
double incrementY = 1. / double(graphH);
|
|
|
|
switch (evType) {
|
|
case (Gdk::MOTION_NOTIFY) :
|
|
if (isHint) {
|
|
get_window()->get_pointer (tx, ty, mod_type);
|
|
} else {
|
|
tx = evX;
|
|
ty = evY;
|
|
mod_type = modifierKey;
|
|
}
|
|
|
|
break;
|
|
|
|
case (Gdk::BUTTON_PRESS) :
|
|
case (Gdk::BUTTON_RELEASE) :
|
|
tx = evX;
|
|
ty = evY;
|
|
mod_type = modifierKey;
|
|
break;
|
|
|
|
default :
|
|
// The cursor position is not available
|
|
return;
|
|
break;
|
|
}
|
|
|
|
if (editedHandle != FCT_EditedHandle_None) {
|
|
prevCursorX = cursorX;
|
|
prevCursorY = cursorY;
|
|
}
|
|
|
|
cursorX = tx - graphX;
|
|
cursorY = graphY - ty;
|
|
|
|
preciseCursorX = cursorX * incrementX;
|
|
preciseCursorY = cursorY * incrementY;
|
|
|
|
snapTo = false;
|
|
|
|
// update deltaX/Y if the user drags a point
|
|
if (editedHandle != FCT_EditedHandle_None) {
|
|
// set the dragging factor
|
|
int control_key = mod_type & GDK_CONTROL_MASK;
|
|
int shift_key = mod_type & GDK_SHIFT_MASK;
|
|
|
|
// the increment get smaller if modifier key are used, and "snap to" may be enabled
|
|
if (control_key) {
|
|
incrementX *= 0.05;
|
|
incrementY *= 0.05;
|
|
}
|
|
|
|
if (shift_key) {
|
|
snapTo = true;
|
|
}
|
|
|
|
deltaX = (double)(cursorX - prevCursorX) * incrementX;
|
|
deltaY = (double)(cursorY - prevCursorY) * incrementY;
|
|
}
|
|
// otherwise set the position of the new point (modifier keys has no effect here)
|
|
else {
|
|
clampedX = CLAMP (preciseCursorX, 0., 1.); // X position of the pointer from the origin of the graph
|
|
clampedY = CLAMP (preciseCursorY, 0., 1.); // Y position of the pointer from the origin of the graph
|
|
}
|
|
|
|
}
|
|
|
|
// Find out the active area under the cursor
|
|
void MyFlatCurve::getMouseOverArea ()
|
|
{
|
|
|
|
// When dragging an element, editedHandle keep its value
|
|
if (editedHandle == FCT_EditedHandle_None) { // && curve.type!=Parametric
|
|
|
|
double minDist = 1000; // used to find out the point pointed by the cursor (over it)
|
|
double minDistX = 1000; // used to find out the closest point
|
|
double dX, dY;
|
|
double absDX;
|
|
double dist;
|
|
bool aboveVLine = false;
|
|
|
|
// NB: this function assume that the graph's shape is a square
|
|
|
|
// Check if the cursor is over a tangent handle
|
|
if (tanHandlesDisplayed) {
|
|
|
|
if (preciseCursorX >= (leftTanHandle.centerX - minDistanceX) && preciseCursorX <= (leftTanHandle.centerX + minDistanceX + 0.00001)
|
|
&& preciseCursorY >= (leftTanHandle.centerY - minDistanceY) && preciseCursorY <= (leftTanHandle.centerY + minDistanceY)) {
|
|
area = FCT_Area_LeftTan;
|
|
return;
|
|
}
|
|
|
|
if (preciseCursorX >= (rightTanHandle.centerX - minDistanceX - 0.00001) && preciseCursorX <= (rightTanHandle.centerX + minDistanceX)
|
|
&& preciseCursorY >= (rightTanHandle.centerY - minDistanceY ) && preciseCursorY <= (rightTanHandle.centerY + minDistanceY)) {
|
|
area = FCT_Area_RightTan;
|
|
return;
|
|
}
|
|
}
|
|
|
|
area = FCT_Area_None;
|
|
closest_point = 0;
|
|
lit_point = -1;
|
|
|
|
for (int i = 0; i < (int)curve.x.size(); i++) {
|
|
if (curve.x.at(i) != -1) {
|
|
dX = curve.x.at(i) - preciseCursorX;
|
|
absDX = dX > 0 ? dX : -dX;
|
|
|
|
if (absDX < minDistX) {
|
|
minDistX = absDX;
|
|
closest_point = i;
|
|
lit_point = i;
|
|
}
|
|
|
|
if (absDX <= minDistanceX) {
|
|
aboveVLine = true;
|
|
dY = curve.y.at(i) - preciseCursorY;
|
|
dist = sqrt(dX * dX + dY * dY);
|
|
|
|
if (dist < minDist) {
|
|
minDist = dist;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (minDist <= minDistanceX) {
|
|
// the cursor is over the point
|
|
area = FCT_Area_Point;
|
|
} else if (aboveVLine) {
|
|
area = FCT_Area_V;
|
|
} else {
|
|
// Check if the cursor is in an insertion area
|
|
lit_point = -1;
|
|
|
|
if (preciseCursorX >= 0.0 && preciseCursorX <= 1.0 && preciseCursorY >= 0.0 && preciseCursorY <= 1.0) {
|
|
area = FCT_Area_Insertion;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<double> MyFlatCurve::getPoints ()
|
|
{
|
|
std::vector<double> result;
|
|
|
|
/*if (curve.type==FCT_Parametric) {
|
|
result.push_back ((double)(Parametric));
|
|
for (int i=0; i<(int)curve.x.size(); i++) {
|
|
result.push_back (curve.x.at(i));
|
|
}
|
|
}
|
|
else {*/
|
|
// the first value gives the type of the curve
|
|
if (curve.type == FCT_Linear) {
|
|
result.push_back ((double)(FCT_Linear));
|
|
} else if (curve.type == FCT_MinMaxCPoints) {
|
|
result.push_back ((double)(FCT_MinMaxCPoints));
|
|
}
|
|
|
|
// then we push all the points coordinate
|
|
for (int i = 0; i < (int)curve.x.size(); i++) {
|
|
if (curve.x.at(i) >= 0) {
|
|
result.push_back (curve.x.at(i));
|
|
result.push_back (curve.y.at(i));
|
|
result.push_back (curve.leftTangent.at(i));
|
|
result.push_back (curve.rightTangent.at(i));
|
|
}
|
|
}
|
|
|
|
//}
|
|
return result;
|
|
}
|
|
|
|
void MyFlatCurve::setPoints (const std::vector<double>& p)
|
|
{
|
|
int ix = 0;
|
|
stopNumericalAdjustment();
|
|
FlatCurveType t = (FlatCurveType)p[ix++];
|
|
curve.type = t;
|
|
|
|
if (t == FCT_MinMaxCPoints) {
|
|
curve.x.clear ();
|
|
curve.y.clear ();
|
|
curve.leftTangent.clear();
|
|
curve.rightTangent.clear();
|
|
|
|
for (int i = 0; i < (int)p.size() / 4; i++) {
|
|
curve.x.push_back (p[ix++]);
|
|
curve.y.push_back (p[ix++]);
|
|
curve.leftTangent.push_back (p[ix++]);
|
|
curve.rightTangent.push_back (p[ix++]);
|
|
}
|
|
}
|
|
|
|
curveIsDirty = true;
|
|
setDirty(true);
|
|
queue_draw ();
|
|
}
|
|
|
|
void MyFlatCurve::setPos(double pos, int chanIdx)
|
|
{
|
|
assert (edited_point > -1);
|
|
|
|
switch (chanIdx) {
|
|
case (0):
|
|
curve.x.at(edited_point) = pos;
|
|
break;
|
|
|
|
case (1):
|
|
curve.y.at(edited_point) = pos;
|
|
break;
|
|
|
|
case (2):
|
|
curve.leftTangent.at(edited_point) = pos;
|
|
break;
|
|
|
|
case (3):
|
|
curve.rightTangent.at(edited_point) = pos;
|
|
break;
|
|
}
|
|
|
|
curveIsDirty = true;
|
|
setDirty(true);
|
|
draw();
|
|
notifyListener ();
|
|
}
|
|
|
|
void MyFlatCurve::stopNumericalAdjustment()
|
|
{
|
|
if (edited_point > -1) {
|
|
edited_point = lit_point = -1;
|
|
area = FCT_Area_None;
|
|
coordinateAdjuster->stopNumericalAdjustment();
|
|
setDirty(true);
|
|
draw();
|
|
}
|
|
}
|
|
|
|
void MyFlatCurve::setType (FlatCurveType t)
|
|
{
|
|
|
|
curve.type = t;
|
|
setDirty(true);
|
|
}
|
|
|
|
void MyFlatCurve::reset(const std::vector<double> &resetCurve, double identityValue)
|
|
{
|
|
calcDimensions();
|
|
|
|
stopNumericalAdjustment();
|
|
|
|
// If a resetCurve exist (non empty)
|
|
if (!resetCurve.empty()) {
|
|
setPoints(resetCurve);
|
|
return;
|
|
}
|
|
|
|
switch (curve.type) {
|
|
case FCT_MinMaxCPoints :
|
|
defaultCurve(identityValue);
|
|
lit_point = -1;
|
|
curveIsDirty = true;
|
|
break;
|
|
|
|
//case Parametric :
|
|
// Nothing to do (?)
|
|
default:
|
|
break;
|
|
}
|
|
|
|
setDirty(true);
|
|
draw();
|
|
}
|
|
|
|
void MyFlatCurve::defaultCurve (double iVal)
|
|
{
|
|
|
|
curve.x.resize(6);
|
|
curve.y.resize(6);
|
|
curve.leftTangent.resize(6);
|
|
curve.rightTangent.resize(6);
|
|
|
|
// Point for RGBCMY colors
|
|
for (int i = 0; i < 6; i++) {
|
|
curve.x.at(i) = (1. / 6.) * i;
|
|
curve.y.at(i) = iVal;
|
|
curve.leftTangent.at(i) = 0.35;
|
|
curve.rightTangent.at(i) = 0.35;
|
|
}
|
|
}
|