bool getInverted(TimeValue time) const
    {
        KnobBoolPtr knob = invert.lock();

        if (!knob) {
            return false;
        }

        return knob->getValueAtTime(time);
    }
    bool getEnabled(TimeValue time,
                    int index) const
    {
        KnobBoolPtr knob = enable[index].lock();

        if (!knob) {
            return true;
        }

        return knob->getValueAtTime(time);
    }
bool
RotoShapeRenderNodePrivate::renderStroke_generic(RenderStrokeDataPtr userData,
                                                 PFNRenderStrokeBeginRender beginCallback,
                                                 PFNRenderStrokeRenderDot renderDotCallback,
                                                 PFNRenderStrokeEndRender endCallback,
                                                 const std::list<std::list<std::pair<Point, double> > >& strokes,
                                                 const double distToNextIn,
                                                 const Point& lastCenterPointIn,
                                                 const RotoDrawableItemPtr& stroke,
                                                 bool doBuildup,
                                                 double opacity,
                                                 TimeValue time,
                                                 ViewIdx /*view*/,
                                                 const RenderScale& scale,
                                                 double* distToNextOut,
                                                 Point* lastCenterPoint)
{
    assert(distToNextOut && lastCenterPoint);
    *distToNextOut = 0;

    double brushSize, brushSizePixelX, brushSizePixelY, brushSpacing, brushHardness, writeOnStart, writeOnEnd;
    bool pressureAffectsOpacity, pressureAffectsHardness, pressureAffectsSize;
    {
        KnobDoublePtr brushSizeKnob = stroke->getBrushSizeKnob();
        brushSize = brushSizeKnob->getValueAtTime(time);
        KnobDoublePtr brushSpacingKnob = stroke->getBrushSpacingKnob();
        brushSpacing = brushSpacingKnob->getValueAtTime(time);
        if (brushSpacing == 0.) {
            return false;
        }
        brushSpacing = std::max(brushSpacing, 0.05);



        KnobDoublePtr brushHardnessKnob = stroke->getBrushHardnessKnob();
        brushHardness = brushHardnessKnob->getValueAtTime(time);
        KnobDoublePtr visiblePortionKnob = stroke->getBrushVisiblePortionKnob();
        writeOnStart = visiblePortionKnob->getValueAtTime(time);
        writeOnEnd = visiblePortionKnob->getValueAtTime(time, DimIdx(1));
        if ( (writeOnEnd - writeOnStart) <= 0. ) {
            return false;
        }


        // This function is also used for opened bezier which do not have pressure.
        RotoStrokeItemPtr isStroke = toRotoStrokeItem(stroke);
        if (!isStroke) {
            pressureAffectsOpacity = false;
            pressureAffectsSize = false;
            pressureAffectsHardness = false;
        } else {
            KnobBoolPtr pressureOpacityKnob = isStroke->getPressureOpacityKnob();
            KnobBoolPtr pressureSizeKnob = isStroke->getPressureSizeKnob();
            KnobBoolPtr pressureHardnessKnob = isStroke->getPressureHardnessKnob();
            pressureAffectsOpacity = pressureOpacityKnob->getValueAtTime(time);
            pressureAffectsSize = pressureSizeKnob->getValueAtTime(time);
            pressureAffectsHardness = pressureHardnessKnob->getValueAtTime(time);
        }
        brushSizePixelX = brushSize;
        brushSizePixelY = brushSizePixelX;

        brushSizePixelX = std::max( 1., brushSizePixelX * scale.x);
        brushSizePixelY = std::max( 1., brushSizePixelY * scale.y);
    }

    double distToNext = distToNextIn;

    bool hasRenderedDot = false;
    beginCallback(userData, brushSizePixelX, brushSizePixelY, brushSpacing, brushHardness, pressureAffectsOpacity, pressureAffectsHardness, pressureAffectsSize, doBuildup, opacity);


    *lastCenterPoint = lastCenterPointIn;
    Point prevCenter = lastCenterPointIn;
    for (std::list<std::list<std::pair<Point, double> > >::const_iterator strokeIt = strokes.begin(); strokeIt != strokes.end(); ++strokeIt) {
        int firstPoint = (int)std::floor( (strokeIt->size() * writeOnStart) );
        int endPoint = (int)std::ceil( (strokeIt->size() * writeOnEnd) );
        assert( firstPoint >= 0 && firstPoint < (int)strokeIt->size() && endPoint > firstPoint && endPoint <= (int)strokeIt->size() );


        ///The visible portion of the paint's stroke with points adjusted to pixel coordinates
        std::list<std::pair<Point, double> > visiblePortion;
        std::list<std::pair<Point, double> >::const_iterator startingIt = strokeIt->begin();
        std::list<std::pair<Point, double> >::const_iterator endingIt = strokeIt->begin();
        std::advance(startingIt, firstPoint);
        std::advance(endingIt, endPoint);
        for (std::list<std::pair<Point, double> >::const_iterator it = startingIt; it != endingIt; ++it) {
            visiblePortion.push_back(*it);
        }
        if ( visiblePortion.empty() ) {
            continue;
        }

        std::list<std::pair<Point, double> >::iterator it = visiblePortion.begin();

        if (visiblePortion.size() == 1) {
            double spacing;
            *lastCenterPoint = it->first;
            renderDotCallback(userData, prevCenter, *lastCenterPoint, it->second, &spacing);
            distToNext += spacing;
            continue;
        }

        //prevCenter = it->first;

        std::list<std::pair<Point, double> >::iterator next = it;
        ++next;

        while ( next != visiblePortion.end() ) {
            //Render for each point a dot. Spacing is a percentage of brushSize:
            //Spacing at 1 means no dot is overlapping another (so the spacing is in fact brushSize)
            //Spacing at 0 we do not render the stroke
            double dx = next->first.x - it->first.x;
            double dy = next->first.y - it->first.y;

            // This is the distance between the current and next discretized points
            // Since the distance between each points may vary, we uniformly position a dot along the segments
            double dist = std::sqrt(dx * dx + dy * dy);

            // while the next point can be drawn on this segment, draw a point and advance
            while (distToNext <= dist) {
                double a = dist == 0. ? 0. : distToNext / dist;
                lastCenterPoint->x = it->first.x * (1 - a) + next->first.x * a;
                lastCenterPoint->y = it->first.y * (1 - a) + next->first.y * a;
                double pressure = it->second * (1 - a) + next->second * a;

                // draw the dot
                double spacing;
                bool rendered = renderDotCallback(userData, prevCenter, *lastCenterPoint, pressure, &spacing);
                hasRenderedDot |= rendered;
                prevCenter = *lastCenterPoint;
                distToNext += spacing;
                /*if (rendered) {
                } else {
                    break;
                }*/
            }

            // go to the next segment
            distToNext -= dist;
            ++next;
            ++it;
        }
    }

    endCallback(userData);

    *distToNextOut = distToNext;

    return hasRenderedDot;
}