예제 #1
0
파일: Curve.cpp 프로젝트: sanfx/Natron
double Curve::getDerivativeAt(double t) const
{
    QReadLocker l(&_imp->_lock);

    if (_imp->keyFrames.empty()) {
        throw std::runtime_error("Curve has no control points!");
    }
    assert(_imp->type == CurvePrivate::DOUBLE_CURVE); // only real-valued curves can be derived
    
    // even when there is only one keyframe, there may be tangents!
    //if (_imp->keyFrames.size() == 1) {
    //    //if there's only 1 keyframe, don't bother interpolating
    //    return (*_imp->keyFrames.begin()).getValue();
    //}
    double tcur,tnext;
    double vcurDerivRight ,vnextDerivLeft ,vcur ,vnext ;
    Natron::KeyframeType interp ,interpNext;

    KeyFrame k(t,0.);
    // find the first keyframe with time greater than t
    KeyFrameSet::const_iterator itup;
    itup = _imp->keyFrames.upper_bound(k);
    interParams(_imp->keyFrames,
                t,
                itup,
                &tcur,
                &vcur,
                &vcurDerivRight,
                &interp,
                &tnext,
                &vnext,
                &vnextDerivLeft,
                &interpNext);

    double d;

    if (mustClamp()) {
        std::pair<double,double> minmax = getCurveYRange();
        d = Natron::derive_clamp(tcur,vcur,
                                 vcurDerivRight,
                                 vnextDerivLeft,
                                 tnext,vnext,
                                 t,
                                 minmax.first, minmax.second,
                                 interp,
                                 interpNext);
    } else {
        d = Natron::derive(tcur,vcur,
                           vcurDerivRight,
                           vnextDerivLeft,
                           tnext,vnext,
                           t,
                           interp,
                           interpNext);
    }

    return d;
}
예제 #2
0
파일: Curve.cpp 프로젝트: sanfx/Natron
double Curve::clampValueToCurveYRange(double v) const
{
    // PRIVATE - should not lock
    ////clamp to min/max if the owner of the curve is a Double or Int knob.
    std::pair<double,double> minmax = getCurveYRange();

    if (v > minmax.second) {
        return minmax.second;
    } else if (v < minmax.first) {
        return minmax.first;
    }
    return v;
}
예제 #3
0
파일: Curve.cpp 프로젝트: sanfx/Natron
double Curve::getIntegrateFromTo(double t1, double t2) const
{
    QReadLocker l(&_imp->_lock);
    bool opposite = false;

    // the following assumes that t2 > t1. If it's not the case, swap them and return the opposite.
    if (t1 > t2) {
        opposite = true;
        std::swap(t1,t2);
    }

    if (_imp->keyFrames.empty()) {
        throw std::runtime_error("Curve has no control points!");
    }
    assert(_imp->type == CurvePrivate::DOUBLE_CURVE); // only real-valued curves can be derived

    // even when there is only one keyframe, there may be tangents!
    //if (_imp->keyFrames.size() == 1) {
    //    //if there's only 1 keyframe, don't bother interpolating
    //    return (*_imp->keyFrames.begin()).getValue();
    //}
    double tcur,tnext;
    double vcurDerivRight ,vnextDerivLeft ,vcur ,vnext ;
    Natron::KeyframeType interp ,interpNext;

    KeyFrame k(t1,0.);
    // find the first keyframe with time strictly greater than t1
    KeyFrameSet::const_iterator itup;
    itup = _imp->keyFrames.upper_bound(k);
    interParams(_imp->keyFrames,
                t1,
                itup,
                &tcur,
                &vcur,
                &vcurDerivRight,
                &interp,
                &tnext,
                &vnext,
                &vnextDerivLeft,
                &interpNext);

    double sum = 0.;

    // while there are still keyframes after the current time, add to the total sum and advance
    while (itup != _imp->keyFrames.end() && itup->getTime() < t2) {
        // add integral from t1 to itup->getTime() to sum
        if (mustClamp()) {
            std::pair<double,double> minmax = getCurveYRange();
            sum += Natron::integrate_clamp(tcur,vcur,
                                           vcurDerivRight,
                                           vnextDerivLeft,
                                           tnext,vnext,
                                           t1, itup->getTime(),
                                           minmax.first, minmax.second,
                                           interp,
                                           interpNext);
        } else {
            sum += Natron::integrate(tcur,vcur,
                                     vcurDerivRight,
                                     vnextDerivLeft,
                                     tnext,vnext,
                                     t1, itup->getTime(),
                                     interp,
                                     interpNext);
        }
        // advance
        t1 = itup->getTime();
        ++itup;
        interParams(_imp->keyFrames,
                    t1,
                    itup,
                    &tcur,
                    &vcur,
                    &vcurDerivRight,
                    &interp,
                    &tnext,
                    &vnext,
                    &vnextDerivLeft,
                    &interpNext);
    }

    assert(itup == _imp->keyFrames.end() || t2 <= itup->getTime());
    // add integral from t1 to t2 to sum
    if (mustClamp()) {
        std::pair<double,double> minmax = getCurveYRange();
        sum += Natron::integrate_clamp(tcur,vcur,
                                       vcurDerivRight,
                                       vnextDerivLeft,
                                       tnext,vnext,
                                       t1, t2,
                                       minmax.first, minmax.second,
                                       interp,
                                       interpNext);
    } else {
        sum += Natron::integrate(tcur,vcur,
                                 vcurDerivRight,
                                 vnextDerivLeft,
                                 tnext,vnext,
                                 t1, t2,
                                 interp,
                                 interpNext);
    }

    return opposite ? -sum : sum;
}
예제 #4
0
파일: CurveGui.cpp 프로젝트: azerupi/Natron
void
CurveGui::drawCurve(int curveIndex,
                    int curvesCount)
{
    // always running in the main thread
    assert( qApp && qApp->thread() == QThread::currentThread() );

    AnimItemBasePtr item = _imp->item.lock();
    if (!item) {
        return;
    }

    std::vector<float> vertices, exprVertices;
    const double widgetWidth = _imp->curveWidget->width();
    KeyFrameSet keyframes;
    bool hasDrawnExpr = false;
    if (item->hasExpression(_imp->dimension, _imp->view)) {

        //we have no choice but to evaluate the expression at each time
        for (int i = 0; i < widgetWidth; ++i) {
            double x = _imp->curveWidget->toZoomCoordinates(i, 0).x();
            double y = evaluate(true /*useExpr*/, x);
            exprVertices.push_back(x);
            exprVertices.push_back(y);
        }
        hasDrawnExpr = true;

    }


    QPointF btmLeft = _imp->curveWidget->toZoomCoordinates(0, _imp->curveWidget->height() - 1);
    QPointF topRight = _imp->curveWidget->toZoomCoordinates(_imp->curveWidget->width() - 1, 0);

    


    bool isPeriodic = false;
    std::pair<double,double> parametricRange = std::make_pair(-std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity());

    keyframes = getInternalCurve()->getKeyFrames_mt_safe();
    isPeriodic = getInternalCurve()->isCurvePeriodic();
    parametricRange = getInternalCurve()->getXRange();

    if ( keyframes.empty() ) {
        // Add a horizontal line for constant knobs, except string knobs.
        KnobIPtr isKnob = boost::dynamic_pointer_cast<KnobI>(item->getInternalAnimItem());

        if (isKnob) {
            KnobStringBasePtr isString = boost::dynamic_pointer_cast<KnobStringBase>(isKnob);
            if (!isString) {
                double value = evaluate(false, 0);
                vertices.push_back(btmLeft.x() + 1);
                vertices.push_back(value);
                vertices.push_back(topRight.x() - 1);
                vertices.push_back(value);
            }
        }
    } else {
        try {
            double x1 = 0;
            double x2;

            bool isX1AKey = false;
            KeyFrame x1Key;
            KeyFrameSet::const_iterator lastUpperIt = keyframes.end();

            while ( x1 < (widgetWidth - 1) ) {
                double x, y;
                if (!isX1AKey) {
                    x = _imp->curveWidget->toZoomCoordinates(x1, 0).x();
                    y = evaluate(false, x);
                } else {
                    x = x1Key.getTime();
                    y = x1Key.getValue();
                }

                vertices.push_back( (float)x );
                vertices.push_back( (float)y );
                nextPointForSegment(x, keyframes, isPeriodic, parametricRange.first, parametricRange.second,  &lastUpperIt, &x2, &x1Key, &isX1AKey);
                x1 = x2;
            }
            //also add the last point
            {
                double x = _imp->curveWidget->toZoomCoordinates(x1, 0).x();
                double y = evaluate(false, x);
                vertices.push_back( (float)x );
                vertices.push_back( (float)y );
            }
        } catch (...) {
        }
    }

    // No Expr curve or no vertices for the curve, don't draw anything else
    if (exprVertices.empty() && vertices.empty()) {
        return;
    }

    AnimationModuleSelectionModelPtr selectionModel = item->getModel()->getSelectionModel();
    assert(selectionModel);
    const AnimItemDimViewKeyFramesMap& selectedKeys = selectionModel->getCurrentKeyFramesSelection();


    const KeyFrameWithStringSet* foundThisCurveSelectedKeys = 0;
    {
        AnimItemDimViewIndexID k;
        k.item = item;
        k.dim = _imp->dimension;
        k.view = _imp->view;
        AnimItemDimViewKeyFramesMap::const_iterator foundDimView = selectedKeys.find(k);

        if (foundDimView != selectedKeys.end()) {
            foundThisCurveSelectedKeys = &foundDimView->second;
        }
    }


    {
        GLProtectAttrib<GL_GPU> a(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_CURRENT_BIT);

        // If this is the only curve selected, draw min/max
        if (foundThisCurveSelectedKeys && selectedKeys.size()) {

            // Draw y min/max axis so the user understands why the value is clamped
            Curve::YRange curveYRange = getCurveYRange();
            if (curveYRange.min != INT_MIN &&
                curveYRange.min != -std::numeric_limits<double>::infinity() &&
                curveYRange.max != INT_MAX &&
                curveYRange.max != std::numeric_limits<double>::infinity() ) {
                QColor minMaxColor;
                minMaxColor.setRgbF(0.398979, 0.398979, 0.398979);
                GL_GPU::Color4d(minMaxColor.redF(), minMaxColor.greenF(), minMaxColor.blueF(), 1.);
                GL_GPU::Begin(GL_LINES);
                GL_GPU::Vertex2d(btmLeft.x(), curveYRange.min);
                GL_GPU::Vertex2d(topRight.x(), curveYRange.min);
                GL_GPU::Vertex2d(btmLeft.x(), curveYRange.max);
                GL_GPU::Vertex2d(topRight.x(), curveYRange.max);
                GL_GPU::End();
                GL_GPU::Color4d(1., 1., 1., 1.);

                double xText = _imp->curveWidget->toZoomCoordinates(10, 0).x();

                _imp->curveWidget->renderText( xText, curveYRange.min, tr("min").toStdString(), minMaxColor.redF(), minMaxColor.greenF(), minMaxColor.blueF(), minMaxColor.alphaF());
                _imp->curveWidget->renderText( xText, curveYRange.max, tr("max").toStdString(), minMaxColor.redF(), minMaxColor.greenF(), minMaxColor.blueF(), minMaxColor.alphaF());
            }
        }


        GL_GPU::Color4f(_imp->color[0], _imp->color[1], _imp->color[2], _imp->color[3]);
        GL_GPU::PointSize(_imp->lineWidth);
        GL_GPU::Enable(GL_BLEND);
        GL_GPU::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        GL_GPU::Enable(GL_LINE_SMOOTH);
        GL_GPU::Hint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
        GL_GPU::LineWidth(1.5);
        glCheckError(GL_GPU);
        if (hasDrawnExpr) {
            drawLineStrip(exprVertices, btmLeft, topRight);
            GL_GPU::LineStipple(2, 0xAAAA);
            GL_GPU::Enable(GL_LINE_STIPPLE);
        }
        drawLineStrip(vertices, btmLeft, topRight);
        if (hasDrawnExpr) {
            GL_GPU::Disable(GL_LINE_STIPPLE);
        }

        glCheckErrorIgnoreOSXBug(GL_GPU);

        //render the name of the curve
        GL_GPU::Color4f(1.f, 1.f, 1.f, 1.f);


        double interval = ( topRight.x() - btmLeft.x() ) / (double)curvesCount;
        double textX = _imp->curveWidget->toZoomCoordinates(15, 0).x() + interval * (double)curveIndex;
        double textY;

        CurvePtr internalCurve = _imp->internalCurve.lock();
        QString curveName = getName();
        QColor thisColor;
        thisColor.setRgbF(Image::clamp(_imp->color[0], 0., 1.),
                          Image::clamp(_imp->color[1], 0., 1.),
                          Image::clamp(_imp->color[2], 0., 1.));

        try {
            // Use expression to place the text if the curve is not animated
            textY = evaluate(internalCurve && !internalCurve->isAnimated(), textX);
        } catch (...) {
            // if it fails attempt without expression, this will most likely return a constant value
            textY = evaluate(false /*useExpression*/, textX);
        }

        if ( ( textX >= btmLeft.x() ) && ( textX <= topRight.x() ) && ( textY >= btmLeft.y() ) && ( textY <= topRight.y() ) ) {
            _imp->curveWidget->renderText( textX, textY, curveName.toStdString(), thisColor.redF(), thisColor.greenF(), thisColor.blueF(), thisColor.alphaF());
        }
        GL_GPU::Color4f(_imp->color[0], _imp->color[1], _imp->color[2], _imp->color[3]);

        //draw keyframes
        GL_GPU::PointSize(7.f);
        GL_GPU::Enable(GL_POINT_SMOOTH);


        KeyFrameWithStringSet::const_iterator foundSelectedKey;
        if (foundThisCurveSelectedKeys) {
            foundSelectedKey = foundThisCurveSelectedKeys->end();
        }
        for (KeyFrameSet::const_iterator k = keyframes.begin(); k != keyframes.end(); ++k) {
            const KeyFrame & key = (*k);

            // Do not draw keyframes out of range
            if ( ( key.getTime() < btmLeft.x() ) || ( key.getTime() > topRight.x() ) || ( key.getValue() < btmLeft.y() ) || ( key.getValue() > topRight.y() ) ) {
                continue;
            }

            GL_GPU::Color4f(_imp->color[0], _imp->color[1], _imp->color[2], _imp->color[3]);

            bool drawKeySelected = false;
            if (foundThisCurveSelectedKeys) {
                KeyFrameWithStringSet::const_iterator start = foundSelectedKey == foundThisCurveSelectedKeys->end() ? foundThisCurveSelectedKeys->begin() : foundSelectedKey;
                foundSelectedKey = std::find_if(start, foundThisCurveSelectedKeys->end(), KeyFrameWithStringTimePredicate(key.getTime()));
                drawKeySelected = foundSelectedKey != foundThisCurveSelectedKeys->end();
                if (!drawKeySelected) {
                    // Also draw the keyframe as selected if it is inside the selection rectangle (but not yet selected)
                    RectD selectionRect = _imp->curveWidget->getSelectionRectangle();
                    drawKeySelected |= _imp->curveWidget->_imp->eventTriggeredFromCurveEditor && (!selectionRect.isNull() && selectionRect.contains(key.getTime(), key.getValue()));
                }
            }
            // If the key is selected change its color
            if (drawKeySelected) {
                GL_GPU::Color4f(0.8f, 0.8f, 0.8f, 1.f);
            }


            RectD keyframeBbox = _imp->curveWidget->_imp->getKeyFrameBoundingRectCanonical(_imp->curveWidget->_imp->curveEditorZoomContext, key.getTime(), key.getValue());

            // draw texture of the keyframe
            {
                AnimationModuleViewPrivate::KeyframeTexture texType = AnimationModuleViewPrivate::kfTextureFromKeyframeType( key.getInterpolation(), drawKeySelected);
                if (texType != AnimationModuleViewPrivate::kfTextureNone) {
                    _imp->curveWidget->_imp->drawTexturedKeyframe(texType, keyframeBbox, false /*drawdimed*/);
                }
            }

            
            // Draw tangents if not constant
            bool drawTangents = drawKeySelected && internalCurve->isYComponentMovable() && (key.getInterpolation() != eKeyframeTypeConstant);
            if (drawTangents) {
                QFontMetrics m( _imp->curveWidget->font());


                // If interpolation is not free and not broken display with dashes the tangents lines
                if ( (key.getInterpolation() != eKeyframeTypeFree) && (key.getInterpolation() != eKeyframeTypeBroken) ) {
                    GL_GPU::LineStipple(2, 0xAAAA);
                    GL_GPU::Enable(GL_LINE_STIPPLE);
                }
                
                QPointF leftTanPos, rightTanPos;
                _imp->curveWidget->getKeyTangentPoints(k, keyframes, &leftTanPos, &rightTanPos);

                // Draw the derivatives lines
                GL_GPU::Begin(GL_LINES);
                GL_GPU::Color4f(1., 0.35, 0.35, 1.);
                GL_GPU::Vertex2f( leftTanPos.x(), leftTanPos.y() );
                GL_GPU::Vertex2f(key.getTime(), key.getValue());
                GL_GPU::Vertex2f(key.getTime(), key.getValue());
                GL_GPU::Vertex2f( rightTanPos.x(), rightTanPos.y());
                GL_GPU::End();
                if ( (key.getInterpolation() != eKeyframeTypeFree) && (key.getInterpolation() != eKeyframeTypeBroken) ) {
                    GL_GPU::Disable(GL_LINE_STIPPLE);
                }


                // Draw the tangents handles
                GL_GPU::Begin(GL_POINTS);
                GL_GPU::Vertex2f( leftTanPos.x(), leftTanPos.y() );
                GL_GPU::Vertex2f( rightTanPos.x(), rightTanPos.y());
                GL_GPU::End();

                // If only one keyframe is selected, also draw the coordinates
                if (selectedKeys.size() == 1 && foundThisCurveSelectedKeys && foundThisCurveSelectedKeys->size() == 1) {
                    double rounding = std::pow(10., CURVEWIDGET_DERIVATIVE_ROUND_PRECISION);
                    QString leftDerivStr = QString::fromUtf8("l: %1").arg(std::floor( (key.getLeftDerivative() * rounding) + 0.5 ) / rounding);
                    QString rightDerivStr = QString::fromUtf8("r: %1").arg(std::floor( (key.getRightDerivative() * rounding) + 0.5 ) / rounding);
                    double yLeftWidgetCoord = _imp->curveWidget->toWidgetCoordinates(0, leftTanPos.y()).y();
                    yLeftWidgetCoord += (m.height() + 4);

                    double yRightWidgetCoord = _imp->curveWidget->toWidgetCoordinates(0, rightTanPos.y()).y();
                    yRightWidgetCoord += (m.height() + 4);

                    GL_GPU::Color4f(1., 1., 1., 1.);
                    glCheckFramebufferError(GL_GPU);
                    _imp->curveWidget->renderText( leftTanPos.x(), _imp->curveWidget->toZoomCoordinates(0, yLeftWidgetCoord).y(),
                                              leftDerivStr.toStdString(), 0.9, 0.9, 0.9, 1.);
                    _imp->curveWidget->renderText( rightTanPos.x(), _imp->curveWidget->toZoomCoordinates(0, yRightWidgetCoord).y(),
                                                  rightDerivStr.toStdString(), 0.9, 0.9, 0.9, 1.);


                    QString coordStr = QString::fromUtf8("x: %1, y: %2");
                    coordStr = coordStr.arg(key.getTime()).arg(key.getValue());
                    double yWidgetCoord = _imp->curveWidget->toWidgetCoordinates( 0, key.getValue() ).y();
                    yWidgetCoord += (m.height() + 4);
                    GL_GPU::Color4f(1., 1., 1., 1.);
                    glCheckFramebufferError(GL_GPU);
                    _imp->curveWidget->renderText( key.getTime(), _imp->curveWidget->toZoomCoordinates(0, yWidgetCoord).y(),
                                                  coordStr.toStdString(), 0.9, 0.9, 0.9, 1.);

                }

            } // drawTangents

        } // for (KeyFrameSet::const_iterator k = keyframes.begin(); k != keyframes.end(); ++k) {
    } // GLProtectAttrib(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_CURRENT_BIT);

    glCheckError(GL_GPU);
} // drawCurve