<commit_before>/*
 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
 * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies)
 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
 * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
 * Copyright (C) 2012, 2013 Intel Corporation. All rights reserved.
 * Copyright (C) 2012, 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "config.h"
#include "core/html/canvas/CanvasPathMethods.h"

#include "bindings/core/v8/ExceptionState.h"
#include "core/dom/ExceptionCode.h"
#include "platform/geometry/FloatRect.h"
#include "platform/transforms/AffineTransform.h"
#include "wtf/MathExtras.h"

namespace blink {

void CanvasPathMethods::closePath()
{
    if (m_path.isEmpty())
        return;

    FloatRect boundRect = m_path.boundingRect();
    if (boundRect.width() || boundRect.height())
        m_path.closeSubpath();
}

void CanvasPathMethods::moveTo(float x, float y)
{
    if (!std::isfinite(x) || !std::isfinite(y))
        return;
    if (!isTransformInvertible())
        return;
    m_path.moveTo(FloatPoint(x, y));
}

void CanvasPathMethods::lineTo(float x, float y)
{
    if (!std::isfinite(x) || !std::isfinite(y))
        return;
    if (!isTransformInvertible())
        return;

    FloatPoint p1 = FloatPoint(x, y);
    if (!m_path.hasCurrentPoint())
        m_path.moveTo(p1);
    else if (p1 != m_path.currentPoint())
        m_path.addLineTo(p1);
}

void CanvasPathMethods::quadraticCurveTo(float cpx, float cpy, float x, float y)
{
    if (!std::isfinite(cpx) || !std::isfinite(cpy) || !std::isfinite(x) || !std::isfinite(y))
        return;
    if (!isTransformInvertible())
        return;
    if (!m_path.hasCurrentPoint())
        m_path.moveTo(FloatPoint(cpx, cpy));

    FloatPoint p1 = FloatPoint(x, y);
    FloatPoint cp = FloatPoint(cpx, cpy);
    if (p1 != m_path.currentPoint() || p1 != cp)
        m_path.addQuadCurveTo(cp, p1);
}

void CanvasPathMethods::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
{
    if (!std::isfinite(cp1x) || !std::isfinite(cp1y) || !std::isfinite(cp2x) || !std::isfinite(cp2y) || !std::isfinite(x) || !std::isfinite(y))
        return;
    if (!isTransformInvertible())
        return;
    if (!m_path.hasCurrentPoint())
        m_path.moveTo(FloatPoint(cp1x, cp1y));

    FloatPoint p1 = FloatPoint(x, y);
    FloatPoint cp1 = FloatPoint(cp1x, cp1y);
    FloatPoint cp2 = FloatPoint(cp2x, cp2y);
    if (p1 != m_path.currentPoint() || p1 != cp1 ||  p1 != cp2)
        m_path.addBezierCurveTo(cp1, cp2, p1);
}

void CanvasPathMethods::arcTo(float x1, float y1, float x2, float y2, float r, ExceptionState& exceptionState)
{
    if (!std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(x2) || !std::isfinite(y2) || !std::isfinite(r))
        return;

    if (r < 0) {
        exceptionState.throwDOMException(IndexSizeError, "The radius provided (" + String::number(r) + ") is negative.");
        return;
    }

    if (!isTransformInvertible())
        return;

    FloatPoint p1 = FloatPoint(x1, y1);
    FloatPoint p2 = FloatPoint(x2, y2);

    if (!m_path.hasCurrentPoint())
        m_path.moveTo(p1);
    else if (p1 == m_path.currentPoint() || p1 == p2 || !r)
        lineTo(x1, y1);
    else
        m_path.addArcTo(p1, p2, r);
}

namespace {

float adjustEndAngle(float startAngle, float endAngle, bool anticlockwise)
{
    float newEndAngle = endAngle;
    /* http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arc
     * If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2pi, or,
     * if the anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2pi,
     * then the arc is the whole circumference of this ellipse, and the point at startAngle along this circle's circumference,
     * measured in radians clockwise from the ellipse's semi-major axis, acts as both the start point and the end point.
     */
    if (!anticlockwise && endAngle - startAngle >= twoPiFloat)
        newEndAngle = startAngle + twoPiFloat;
    else if (anticlockwise && startAngle - endAngle >= twoPiFloat)
        newEndAngle = startAngle - twoPiFloat;

    /*
     * Otherwise, the arc is the path along the circumference of this ellipse from the start point to the end point,
     * going anti-clockwise if the anticlockwise argument is true, and clockwise otherwise.
     * Since the points are on the ellipse, as opposed to being simply angles from zero,
     * the arc can never cover an angle greater than 2pi radians.
     */
    /* NOTE: When startAngle = 0, endAngle = 2Pi and anticlockwise = true, the spec does not indicate clearly.
     * We draw the entire circle, because some web sites use arc(x, y, r, 0, 2*Math.PI, true) to draw circle.
     * We preserve backward-compatibility.
     */
    else if (!anticlockwise && startAngle > endAngle)
        newEndAngle = startAngle + (twoPiFloat - fmodf(startAngle - endAngle, twoPiFloat));
    else if (anticlockwise && startAngle < endAngle)
        newEndAngle = startAngle - (twoPiFloat - fmodf(endAngle - startAngle, twoPiFloat));

    ASSERT(ellipseIsRenderable(startAngle, newEndAngle));
    return newEndAngle;
}

inline void lineToFloatPoint(CanvasPathMethods* path, const FloatPoint& p)
{
    path->lineTo(p.x(), p.y());
}

inline FloatPoint getPointOnEllipse(float radiusX, float radiusY, float theta)
{
    return FloatPoint(radiusX * cosf(theta), radiusY * sinf(theta));
}

void canonicalizeAngle(float* startAngle, float* endAngle)
{
    // Make 0 <= startAngle < 2*PI
    float newStartAngle = *startAngle;
    if (newStartAngle < 0)
        newStartAngle = twoPiFloat + fmodf(newStartAngle, -twoPiFloat);
    else
        newStartAngle = fmodf(newStartAngle, twoPiFloat);

    float delta = newStartAngle - *startAngle;
    *startAngle = newStartAngle;
    *endAngle = *endAngle + delta;
    ASSERT(newStartAngle >= 0 && newStartAngle < twoPiFloat);
}

/*
 * degenerateEllipse() handles a degenerated ellipse using several lines.
 *
 * Let's see a following example: line to ellipse to line.
 *        _--^\
 *       (     )
 * -----(      )
 *            )
 *           /--------
 *
 * If radiusX becomes zero, the ellipse of the example is degenerated.
 *         _
 *        // P
 *       //
 * -----//
 *      /
 *     /--------
 *
 * To draw the above example, need to get P that is a local maximum point.
 * Angles for P are 0.5Pi and 1.5Pi in the ellipse coordinates.
 *
 * If radiusY becomes zero, the result is as follows.
 * -----__
 *        --_
 *          ----------
 *            ``P
 * Angles for P are 0 and Pi in the ellipse coordinates.
 *
 * To handle both cases, degenerateEllipse() lines to start angle, local maximum points(every 0.5Pi), and end angle.
 * NOTE: Before ellipse() calls this function, adjustEndAngle() is called, so endAngle - startAngle must be equal to or less than 2Pi.
 */
void degenerateEllipse(CanvasPathMethods* path, float x, float y, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise)
{
    ASSERT(ellipseIsRenderable(startAngle, endAngle));
    ASSERT(startAngle >= 0 && startAngle < twoPiFloat);
    ASSERT((anticlockwise && (startAngle - endAngle) >= 0) || (!anticlockwise && (endAngle - startAngle) >= 0));

    FloatPoint center(x, y);
    AffineTransform rotationMatrix;
    rotationMatrix.rotateRadians(rotation);
    // First, if the object's path has any subpaths, then the method must add a straight line from the last point in the subpath to the start point of the arc.
    lineToFloatPoint(path, center + rotationMatrix.mapPoint(getPointOnEllipse(radiusX, radiusY, startAngle)));
    if ((!radiusX && !radiusY) || startAngle == endAngle)
        return;

    if (!anticlockwise) {
        // startAngle - fmodf(startAngle, piOverTwoFloat) + piOverTwoFloat is the one of (0, 0.5Pi, Pi, 1.5Pi, 2Pi)
        // that is the closest to startAngle on the clockwise direction.
        for (float angle = startAngle - fmodf(startAngle, piOverTwoFloat) + piOverTwoFloat; angle < endAngle; angle += piOverTwoFloat)
            lineToFloatPoint(path, center + rotationMatrix.mapPoint(getPointOnEllipse(radiusX, radiusY, angle)));
    } else {
        for (float angle = startAngle - fmodf(startAngle, piOverTwoFloat); angle > endAngle; angle -= piOverTwoFloat)
            lineToFloatPoint(path, center + rotationMatrix.mapPoint(getPointOnEllipse(radiusX, radiusY, angle)));
    }

    lineToFloatPoint(path, center + rotationMatrix.mapPoint(getPointOnEllipse(radiusX, radiusY, endAngle)));
}

} // namespace

void CanvasPathMethods::arc(float x, float y, float radius, float startAngle, float endAngle, bool anticlockwise, ExceptionState& exceptionState)
{
    if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(radius) || !std::isfinite(startAngle) || !std::isfinite(endAngle))
        return;

    if (radius < 0) {
        exceptionState.throwDOMException(IndexSizeError, "The radius provided (" + String::number(radius) + ") is negative.");
        return;
    }

    if (!isTransformInvertible())
        return;

    if (!radius || startAngle == endAngle) {
        // The arc is empty but we still need to draw the connecting line.
        lineTo(x + radius * cosf(startAngle), y + radius * sinf(startAngle));
        return;
    }

    canonicalizeAngle(&startAngle, &endAngle);
    float adjustedEndAngle = adjustEndAngle(startAngle, endAngle, anticlockwise);
    m_path.addArc(FloatPoint(x, y), radius, startAngle, adjustedEndAngle, anticlockwise);
}

void CanvasPathMethods::ellipse(float x, float y, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise, ExceptionState& exceptionState)
{
    if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(radiusX) || !std::isfinite(radiusY) || !std::isfinite(rotation) || !std::isfinite(startAngle) || !std::isfinite(endAngle))
        return;

    if (radiusX < 0) {
        exceptionState.throwDOMException(IndexSizeError, "The major-axis radius provided (" + String::number(radiusX) + ") is negative.");
        return;
    }
    if (radiusY < 0) {
        exceptionState.throwDOMException(IndexSizeError, "The minor-axis radius provided (" + String::number(radiusY) + ") is negative.");
        return;
    }

    if (!isTransformInvertible())
        return;

    canonicalizeAngle(&startAngle, &endAngle);
    float adjustedEndAngle = adjustEndAngle(startAngle, endAngle, anticlockwise);
    if (!radiusX || !radiusY || startAngle == adjustedEndAngle) {
        // The ellipse is empty but we still need to draw the connecting line to start point.
        degenerateEllipse(this, x, y, radiusX, radiusY, rotation, startAngle, adjustedEndAngle, anticlockwise);
        return;
    }

    m_path.addEllipse(FloatPoint(x, y), radiusX, radiusY, rotation, startAngle, adjustedEndAngle, anticlockwise);
}

void CanvasPathMethods::rect(float x, float y, float width, float height)
{
    if (!isTransformInvertible())
        return;

    if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(width) || !std::isfinite(height))
        return;

    if (!width && !height) {
        m_path.moveTo(FloatPoint(x, y));
        return;
    }

    m_path.addRect(FloatRect(x, y, width, height));
}
}
<commit_msg>ASSERTION FAILED: newStartAngle >= 0 && newStartAngle < twoPiFloat.<commit_after>/*
 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
 * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies)
 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
 * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
 * Copyright (C) 2012, 2013 Intel Corporation. All rights reserved.
 * Copyright (C) 2012, 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "config.h"
#include "core/html/canvas/CanvasPathMethods.h"

#include "bindings/core/v8/ExceptionState.h"
#include "core/dom/ExceptionCode.h"
#include "platform/geometry/FloatRect.h"
#include "platform/transforms/AffineTransform.h"
#include "wtf/MathExtras.h"

namespace blink {

void CanvasPathMethods::closePath()
{
    if (m_path.isEmpty())
        return;

    FloatRect boundRect = m_path.boundingRect();
    if (boundRect.width() || boundRect.height())
        m_path.closeSubpath();
}

void CanvasPathMethods::moveTo(float x, float y)
{
    if (!std::isfinite(x) || !std::isfinite(y))
        return;
    if (!isTransformInvertible())
        return;
    m_path.moveTo(FloatPoint(x, y));
}

void CanvasPathMethods::lineTo(float x, float y)
{
    if (!std::isfinite(x) || !std::isfinite(y))
        return;
    if (!isTransformInvertible())
        return;

    FloatPoint p1 = FloatPoint(x, y);
    if (!m_path.hasCurrentPoint())
        m_path.moveTo(p1);
    else if (p1 != m_path.currentPoint())
        m_path.addLineTo(p1);
}

void CanvasPathMethods::quadraticCurveTo(float cpx, float cpy, float x, float y)
{
    if (!std::isfinite(cpx) || !std::isfinite(cpy) || !std::isfinite(x) || !std::isfinite(y))
        return;
    if (!isTransformInvertible())
        return;
    if (!m_path.hasCurrentPoint())
        m_path.moveTo(FloatPoint(cpx, cpy));

    FloatPoint p1 = FloatPoint(x, y);
    FloatPoint cp = FloatPoint(cpx, cpy);
    if (p1 != m_path.currentPoint() || p1 != cp)
        m_path.addQuadCurveTo(cp, p1);
}

void CanvasPathMethods::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
{
    if (!std::isfinite(cp1x) || !std::isfinite(cp1y) || !std::isfinite(cp2x) || !std::isfinite(cp2y) || !std::isfinite(x) || !std::isfinite(y))
        return;
    if (!isTransformInvertible())
        return;
    if (!m_path.hasCurrentPoint())
        m_path.moveTo(FloatPoint(cp1x, cp1y));

    FloatPoint p1 = FloatPoint(x, y);
    FloatPoint cp1 = FloatPoint(cp1x, cp1y);
    FloatPoint cp2 = FloatPoint(cp2x, cp2y);
    if (p1 != m_path.currentPoint() || p1 != cp1 ||  p1 != cp2)
        m_path.addBezierCurveTo(cp1, cp2, p1);
}

void CanvasPathMethods::arcTo(float x1, float y1, float x2, float y2, float r, ExceptionState& exceptionState)
{
    if (!std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(x2) || !std::isfinite(y2) || !std::isfinite(r))
        return;

    if (r < 0) {
        exceptionState.throwDOMException(IndexSizeError, "The radius provided (" + String::number(r) + ") is negative.");
        return;
    }

    if (!isTransformInvertible())
        return;

    FloatPoint p1 = FloatPoint(x1, y1);
    FloatPoint p2 = FloatPoint(x2, y2);

    if (!m_path.hasCurrentPoint())
        m_path.moveTo(p1);
    else if (p1 == m_path.currentPoint() || p1 == p2 || !r)
        lineTo(x1, y1);
    else
        m_path.addArcTo(p1, p2, r);
}

namespace {

float adjustEndAngle(float startAngle, float endAngle, bool anticlockwise)
{
    float newEndAngle = endAngle;
    /* http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arc
     * If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2pi, or,
     * if the anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2pi,
     * then the arc is the whole circumference of this ellipse, and the point at startAngle along this circle's circumference,
     * measured in radians clockwise from the ellipse's semi-major axis, acts as both the start point and the end point.
     */
    if (!anticlockwise && endAngle - startAngle >= twoPiFloat)
        newEndAngle = startAngle + twoPiFloat;
    else if (anticlockwise && startAngle - endAngle >= twoPiFloat)
        newEndAngle = startAngle - twoPiFloat;

    /*
     * Otherwise, the arc is the path along the circumference of this ellipse from the start point to the end point,
     * going anti-clockwise if the anticlockwise argument is true, and clockwise otherwise.
     * Since the points are on the ellipse, as opposed to being simply angles from zero,
     * the arc can never cover an angle greater than 2pi radians.
     */
    /* NOTE: When startAngle = 0, endAngle = 2Pi and anticlockwise = true, the spec does not indicate clearly.
     * We draw the entire circle, because some web sites use arc(x, y, r, 0, 2*Math.PI, true) to draw circle.
     * We preserve backward-compatibility.
     */
    else if (!anticlockwise && startAngle > endAngle)
        newEndAngle = startAngle + (twoPiFloat - fmodf(startAngle - endAngle, twoPiFloat));
    else if (anticlockwise && startAngle < endAngle)
        newEndAngle = startAngle - (twoPiFloat - fmodf(endAngle - startAngle, twoPiFloat));

    ASSERT(ellipseIsRenderable(startAngle, newEndAngle));
    return newEndAngle;
}

inline void lineToFloatPoint(CanvasPathMethods* path, const FloatPoint& p)
{
    path->lineTo(p.x(), p.y());
}

inline FloatPoint getPointOnEllipse(float radiusX, float radiusY, float theta)
{
    return FloatPoint(radiusX * cosf(theta), radiusY * sinf(theta));
}

void canonicalizeAngle(float* startAngle, float* endAngle)
{
    // Make 0 <= startAngle < 2*PI
    float newStartAngle = fmodf(*startAngle, twoPiFloat);
    if (newStartAngle < 0)
        newStartAngle += twoPiFloat;

    float delta = newStartAngle - *startAngle;
    *startAngle = newStartAngle;
    *endAngle = *endAngle + delta;

    ASSERT(newStartAngle >= 0 && newStartAngle < twoPiFloat);
}

/*
 * degenerateEllipse() handles a degenerated ellipse using several lines.
 *
 * Let's see a following example: line to ellipse to line.
 *        _--^\
 *       (     )
 * -----(      )
 *            )
 *           /--------
 *
 * If radiusX becomes zero, the ellipse of the example is degenerated.
 *         _
 *        // P
 *       //
 * -----//
 *      /
 *     /--------
 *
 * To draw the above example, need to get P that is a local maximum point.
 * Angles for P are 0.5Pi and 1.5Pi in the ellipse coordinates.
 *
 * If radiusY becomes zero, the result is as follows.
 * -----__
 *        --_
 *          ----------
 *            ``P
 * Angles for P are 0 and Pi in the ellipse coordinates.
 *
 * To handle both cases, degenerateEllipse() lines to start angle, local maximum points(every 0.5Pi), and end angle.
 * NOTE: Before ellipse() calls this function, adjustEndAngle() is called, so endAngle - startAngle must be equal to or less than 2Pi.
 */
void degenerateEllipse(CanvasPathMethods* path, float x, float y, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise)
{
    ASSERT(ellipseIsRenderable(startAngle, endAngle));
    ASSERT(startAngle >= 0 && startAngle < twoPiFloat);
    ASSERT((anticlockwise && (startAngle - endAngle) >= 0) || (!anticlockwise && (endAngle - startAngle) >= 0));

    FloatPoint center(x, y);
    AffineTransform rotationMatrix;
    rotationMatrix.rotateRadians(rotation);
    // First, if the object's path has any subpaths, then the method must add a straight line from the last point in the subpath to the start point of the arc.
    lineToFloatPoint(path, center + rotationMatrix.mapPoint(getPointOnEllipse(radiusX, radiusY, startAngle)));
    if ((!radiusX && !radiusY) || startAngle == endAngle)
        return;

    if (!anticlockwise) {
        // startAngle - fmodf(startAngle, piOverTwoFloat) + piOverTwoFloat is the one of (0, 0.5Pi, Pi, 1.5Pi, 2Pi)
        // that is the closest to startAngle on the clockwise direction.
        for (float angle = startAngle - fmodf(startAngle, piOverTwoFloat) + piOverTwoFloat; angle < endAngle; angle += piOverTwoFloat)
            lineToFloatPoint(path, center + rotationMatrix.mapPoint(getPointOnEllipse(radiusX, radiusY, angle)));
    } else {
        for (float angle = startAngle - fmodf(startAngle, piOverTwoFloat); angle > endAngle; angle -= piOverTwoFloat)
            lineToFloatPoint(path, center + rotationMatrix.mapPoint(getPointOnEllipse(radiusX, radiusY, angle)));
    }

    lineToFloatPoint(path, center + rotationMatrix.mapPoint(getPointOnEllipse(radiusX, radiusY, endAngle)));
}

} // namespace

void CanvasPathMethods::arc(float x, float y, float radius, float startAngle, float endAngle, bool anticlockwise, ExceptionState& exceptionState)
{
    if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(radius) || !std::isfinite(startAngle) || !std::isfinite(endAngle))
        return;

    if (radius < 0) {
        exceptionState.throwDOMException(IndexSizeError, "The radius provided (" + String::number(radius) + ") is negative.");
        return;
    }

    if (!isTransformInvertible())
        return;

    if (!radius || startAngle == endAngle) {
        // The arc is empty but we still need to draw the connecting line.
        lineTo(x + radius * cosf(startAngle), y + radius * sinf(startAngle));
        return;
    }

    canonicalizeAngle(&startAngle, &endAngle);
    float adjustedEndAngle = adjustEndAngle(startAngle, endAngle, anticlockwise);
    m_path.addArc(FloatPoint(x, y), radius, startAngle, adjustedEndAngle, anticlockwise);
}

void CanvasPathMethods::ellipse(float x, float y, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise, ExceptionState& exceptionState)
{
    if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(radiusX) || !std::isfinite(radiusY) || !std::isfinite(rotation) || !std::isfinite(startAngle) || !std::isfinite(endAngle))
        return;

    if (radiusX < 0) {
        exceptionState.throwDOMException(IndexSizeError, "The major-axis radius provided (" + String::number(radiusX) + ") is negative.");
        return;
    }
    if (radiusY < 0) {
        exceptionState.throwDOMException(IndexSizeError, "The minor-axis radius provided (" + String::number(radiusY) + ") is negative.");
        return;
    }

    if (!isTransformInvertible())
        return;

    canonicalizeAngle(&startAngle, &endAngle);
    float adjustedEndAngle = adjustEndAngle(startAngle, endAngle, anticlockwise);
    if (!radiusX || !radiusY || startAngle == adjustedEndAngle) {
        // The ellipse is empty but we still need to draw the connecting line to start point.
        degenerateEllipse(this, x, y, radiusX, radiusY, rotation, startAngle, adjustedEndAngle, anticlockwise);
        return;
    }

    m_path.addEllipse(FloatPoint(x, y), radiusX, radiusY, rotation, startAngle, adjustedEndAngle, anticlockwise);
}

void CanvasPathMethods::rect(float x, float y, float width, float height)
{
    if (!isTransformInvertible())
        return;

    if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(width) || !std::isfinite(height))
        return;

    if (!width && !height) {
        m_path.moveTo(FloatPoint(x, y));
        return;
    }

    m_path.addRect(FloatRect(x, y, width, height));
}
}
<|endoftext|>


<commit_before>/**
 *  @copyright Copyright 2016 The J-PET Framework Authors. All rights reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may find a copy of the License in the LICENCE file.
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  @file JPetTaskChainExecutor.cpp
 */

#include "JPetTaskChainExecutor.h"

#include <cassert>
#include <memory>
#include <chrono>

#include "JPetTaskChainExecutorUtils.h"
#include "../JPetLoggerInclude.h"

JPetTaskChainExecutor::JPetTaskChainExecutor(TaskGeneratorChain* taskGeneratorChain, int processedFileId, const OptionsPerFile& opts):
  fInputSeqId(processedFileId),
  ftaskGeneratorChain(taskGeneratorChain)
{
  assert(taskGeneratorChain->size() == opts.size());
  /// ParamManager is generated and added to fParams
  fParams = JPetTaskChainExecutorUtils::generateParams(opts);
  assert(fParams.front().getParamManager());
  if (taskGeneratorChain) {
    for (auto taskGenerator : *ftaskGeneratorChain) {
      auto task = taskGenerator();
      fTasks.push_back(task);
    }
  } else {
    ERROR("taskGeneratorChain is null while constructing JPetTaskChainExecutor");
  }
}

bool JPetTaskChainExecutor::preprocessing(const std::vector<JPetParams>& params)
{
  if (params.empty()) {
    ERROR("No parameters provided!");
    return false;
  } else {
    return JPetTaskChainExecutorUtils::process(params.front());
  }
}

bool JPetTaskChainExecutor::process()
{
  JPetTimer timer;
  timer.startMeasurement();

  if (!preprocessing(fParams)) {
    ERROR("Error in preprocessing phase");
    return false;
  }

  timer.stopMeasurement("Preprocessing");

  JPetDataInterface nullDataObject;
  JPetParams outputParams;

  assert(fTasks.size() == fParams.size());
  auto currParamsIt = fParams.begin();

  /// We iterate over both tasks and parameters
  for (auto currentTaskIt = fTasks.begin(); currentTaskIt != fTasks.end(); currentTaskIt++) {

    auto currentTask  =  *currentTaskIt;
    auto taskName = currentTask->getName();

    assert(currParamsIt != fParams.end());
    auto currParams = *currParamsIt;
    /// We generate input parameters based on the current parameter set and the outputParams produced by
    /// the previous task.
    auto inputParams = JPetTaskChainExecutorUtils::generateParams(currParams, outputParams);
    jpet_options_tools::printOptionsToLog(currParams.getOptions(), std::string("Options for ") + taskName);
    currParamsIt++;

    timer.startMeasurement();
    INFO(Form("Starting task: %s", taskName.c_str()));
    if (!currentTask->init(inputParams)) {
      ERROR("In task initialization");
      return false;
    }
    if (!currentTask->run(nullDataObject)) {
      ERROR("In task run()");
      return false;
    }
    if (!currentTask->terminate(outputParams)) {
      ERROR("In task terminate() ");
      return false;
    }

    timer.stopMeasurement("task " + taskName);
  }
  INFO(timer.getAllMeasuredTimes());
  INFO(timer.getTotalMeasuredTime());
  return true;
}

void* JPetTaskChainExecutor::processProxy(void* runner)
{
  assert(runner);
  static_cast<JPetTaskChainExecutor*>(runner)->process();
  return 0;
}

TThread* JPetTaskChainExecutor::run()
{
  TThread* thread = new TThread(std::to_string(fInputSeqId).c_str(), processProxy, (void*)this);
  assert(thread);
  thread->Run();
  return thread;
}

JPetTaskChainExecutor::~JPetTaskChainExecutor()
{
  for (auto& task : fTasks) {
    if (task) {
      delete task;
      task = 0;
    }
  }
}
<commit_msg>Change the comments and outputParams -> controlParams for readability.<commit_after>/**
 *  @copyright Copyright 2016 The J-PET Framework Authors. All rights reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may find a copy of the License in the LICENCE file.
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  @file JPetTaskChainExecutor.cpp
 */

#include "JPetTaskChainExecutor.h"

#include <cassert>
#include <memory>
#include <chrono>

#include "JPetTaskChainExecutorUtils.h"
#include "../JPetLoggerInclude.h"

JPetTaskChainExecutor::JPetTaskChainExecutor(TaskGeneratorChain* taskGeneratorChain, int processedFileId, const OptionsPerFile& opts):
  fInputSeqId(processedFileId),
  ftaskGeneratorChain(taskGeneratorChain)
{
  assert(taskGeneratorChain->size() == opts.size());
  /// ParamManager is generated and added to fParams
  fParams = JPetTaskChainExecutorUtils::generateParams(opts);
  assert(fParams.front().getParamManager());
  if (taskGeneratorChain) {
    for (auto taskGenerator : *ftaskGeneratorChain) {
      auto task = taskGenerator();
      fTasks.push_back(task);
    }
  } else {
    ERROR("taskGeneratorChain is null while constructing JPetTaskChainExecutor");
  }
}

bool JPetTaskChainExecutor::preprocessing(const std::vector<JPetParams>& params)
{
  if (params.empty()) {
    ERROR("No parameters provided!");
    return false;
  } else {
    return JPetTaskChainExecutorUtils::process(params.front());
  }
}

bool JPetTaskChainExecutor::process()
{
  JPetTimer timer;
  timer.startMeasurement();

  if (!preprocessing(fParams)) {
    ERROR("Error in preprocessing phase");
    return false;
  }

  timer.stopMeasurement("Preprocessing");

  JPetDataInterface nullDataObject;
  JPetParams controlParams; /// Parameters used to control the input file type and event range.

  assert(fTasks.size() == fParams.size());
  auto currParamsIt = fParams.begin();

  /// We iterate over both tasks and parameters
  for (auto currentTaskIt = fTasks.begin(); currentTaskIt != fTasks.end(); currentTaskIt++) {

    auto currentTask  =  *currentTaskIt;
    auto taskName = currentTask->getName();

    assert(currParamsIt != fParams.end());
    auto currParams = *currParamsIt;
    /// We generate input parameters based on the current parameter set and the controlParams produced by
    /// the previous task.
    auto inputParams = JPetTaskChainExecutorUtils::generateParams(currParams, controlParams);
    jpet_options_tools::printOptionsToLog(currParams.getOptions(), std::string("Options for ") + taskName);
    currParamsIt++;

    timer.startMeasurement();
    INFO(Form("Starting task: %s", taskName.c_str()));
    if (!currentTask->init(inputParams)) {
      ERROR("In task initialization");
      return false;
    }
    if (!currentTask->run(nullDataObject)) {
      ERROR("In task run()");
      return false;
    }
    if (!currentTask->terminate(controlParams)) { /// Here controParams can be modified by the current task.
      ERROR("In task terminate() ");
      return false;
    }

    timer.stopMeasurement("task " + taskName);
  }
  INFO(timer.getAllMeasuredTimes());
  INFO(timer.getTotalMeasuredTime());
  return true;
}

void* JPetTaskChainExecutor::processProxy(void* runner)
{
  assert(runner);
  static_cast<JPetTaskChainExecutor*>(runner)->process();
  return 0;
}

TThread* JPetTaskChainExecutor::run()
{
  TThread* thread = new TThread(std::to_string(fInputSeqId).c_str(), processProxy, (void*)this);
  assert(thread);
  thread->Run();
  return thread;
}

JPetTaskChainExecutor::~JPetTaskChainExecutor()
{
  for (auto& task : fTasks) {
    if (task) {
      delete task;
      task = 0;
    }
  }
}
<|endoftext|>


<commit_before>#include <map>
#include <limits>
#include <algorithm>
#include "ObjModel.hpp"
#include "glm/geometric.hpp"

namespace
{

class VertexIndexIndexer
{
public:

    VertexIndexIndexer() : _object(0)
    {
    }

    VertexIndexIndexer& operator << (vfm::Object &o)
    {
        if (_object != &o)
        {
            _vertexIndexPtrMap.clear();
            _object = &o;
        }
        return *this;
    }

    vfm::index_t getIndex(const vfm::VertexIndex &vi)
    {
        size_t mapSize = _vertexIndexPtrMap.size();
        vfm::index_t *index = &_vertexIndexPtrMap[vi];
        if (_vertexIndexPtrMap.size() > mapSize)
        {
            *index = mapSize;
            _object->vertexIndices.push_back(vi);
        }
        return *index;
    }

private:
    struct VertexIndexPtrComparator {
        bool operator() (const vfm::VertexIndex &vi1, const vfm::VertexIndex &vi2) const
        {
          if (vi1.vertex == vi2.vertex)
          {
              if(vi1.normal == vi2.normal)
              {
                  return vi1.texture < vi2.texture;
              }
              return vi1.normal < vi2.normal;
          }
          return vi1.vertex < vi2.vertex;
        }
    };
    typedef std::map<vfm::VertexIndex, vfm::index_t, VertexIndexPtrComparator> VertexIndexMap;

    vfm::Object *_object;
    VertexIndexMap _vertexIndexPtrMap;
};

inline std::istream& eatline (std::istream& is)
{
    return is.ignore(std::numeric_limits<long>::max(), '\n');
}

}

vfm::VertexIndex::VertexIndex (index_t vertex, index_t normal, index_t texture)
    : vertex(vertex), normal(normal), texture(texture)
{
}

bool vfm::VertexIndex::operator == (const VertexIndex &vi) const
{
    return this->vertex == vi.vertex && this->normal == vi.normal && this->texture == vi.texture;
}

std::istream & vfm::operator >> (std::istream &is, glm::vec3 &v)
{
    is >> v.x;
    if(is.good())
    {
        is >> v.y;
        if (is.fail())
        {
            v.y = 0;
            v.z = 0;
            is.clear();
        }
        else if(is.good())
        {
            is >> v.z;
            if (is.fail())
            {
                v.z = 0;
                is.clear();
            }
        }
    }
    return is;
}

std::istream & vfm::operator >> (std::istream &is, glm::vec4 &v)
{
    is >> v.x >> v.y >> v.z;
    if (is.good())
    {
        is >> v.w;
        // w is optional, no matter if the stream has not
        // succeeded in reading it
        if (is.fail())
        {
            v.w = 1;
            is.clear();
        }
    }
    return is;
}

std::istream & vfm::operator >> (std::istream &is, VertexIndex &vi)
{
    is >> vi.vertex;
    if (is.good() && is.peek() == '/')
    {
        is.ignore(1);
        if(is.good() && is.peek() != '/')
        {
            is >> vi.texture;
        }
        if(is.good() && is.peek() == '/')
        {
            is.ignore(1);
            is >> vi.normal;
        }
    }
    return is;
}

std::istream & vfm::operator >> (std::istream &is, VertexIndexVector &viv)
{
    bool eol = false;
    while(is.good() && !eol)
    {
        VertexIndex vi;
        is >> vi;
        viv.push_back(vi);
        while (is.good())
        {
            int c = is.peek();
            if (c == '#' || c == '\n')
            {
                is >> eatline;
                eol = true;
                break;
            }
            else if (std::isspace(c))
            {
                is.get();
            }
            else {
                break;
            }
        }
    }
    return is;
}

static void createTriangles(const vfm::IndexVector &polygons, vfm::IndexVector &triangles)
{
    vfm::index_t nbIndices = polygons.size();
    if (nbIndices < 3)
    {
        return;
    }

    vfm::index_t descIndex = nbIndices;
    vfm::index_t ascIndex = 2;

    vfm::index_t previous = polygons[0];
    vfm::index_t current = polygons[1];
    vfm::index_t next = polygons[2];

    for(bool asc = false; ascIndex < descIndex; asc = !asc)
    {
        triangles.push_back(previous);
        triangles.push_back(current);
        triangles.push_back(next);

        if (asc)
        {
            current = next;
            next = polygons[++ascIndex];
        }
        else
        {
            current = previous;
            previous = polygons[--descIndex];
        }
    }
}

std::istream & vfm::operator >> (std::istream &is, ObjModel &model)
{
    std::string token;
    glm::vec4 vec4;
    glm::vec3 vec3;
    VertexIndexIndexer vertexIndexIndexer;
    vfm::IndexVector polygons;
    VertexIndexVector face;
    Object *object = 0;

    while (is >> token)
    {
        if (! token.empty() && token[0] == '#')
        {
            // ignore comments
            is >> eatline;
        }
        else if (token == "o")
        {
            model.objects.push_back(Object());
            object = &model.objects.back();
            vertexIndexIndexer << *object;
            is >> object->name;
        }
        else if (token == "v")
        {
            // read new vertex
            is >> vec4;
            if(!is.fail()) model.vertices.push_back(vec4);
        }
        else if (token == "vt")
        {
            // read texture coordinates
            is >> vec3;
            if (!is.fail()) model.textures.push_back(vec3);
        }
        else if (token == "vn")
        {
            // read normals coordinates
            is >> vec3;
            if (!is.fail()) model.normals.push_back(vec3);
        }
        else if (token == "f")
        {
            if (object == 0)
            {
                model.objects.push_back(Object());
                object = &model.objects.back();
                vertexIndexIndexer << *object;
            }

            face.clear();
            // read faces indices
            is >> face;

            if(!is.fail())
            {
                polygons.clear();
                for(vfm::VertexIndexVector::iterator it = face.begin(); it < face.end(); ++it)
                {
                    polygons.push_back(vertexIndexIndexer.getIndex(*it));
                }

                createTriangles(polygons, object->triangles);
            }
        }
        else
        {
            // ignore line
            is >> eatline;
        }
    }

    return is;
}

void vfm::ObjModel::computeNormals(bool normalized)
{
    this->normals.clear();
    this->normals.resize(this->vertices.size());
    VertexIndex *vertexIndices[3];
    for(ObjectVector::iterator itObj = this->objects.begin(); itObj != this->objects.end(); ++itObj)
    {
        Object *o = &(*itObj);
        for(IndexVector::iterator it = o->triangles.begin(); it != o->triangles.end(); ++it)
        {
            vertexIndices[0] = &o->vertexIndices[*it++];
            vertexIndices[1] = &o->vertexIndices[*it++];
            vertexIndices[2] = &o->vertexIndices[*it];
            glm::vec3 a(this->vertices[vertexIndices[0]->vertex-1]);
            glm::vec3 b(this->vertices[vertexIndices[1]->vertex-1]);
            glm::vec3 c(this->vertices[vertexIndices[2]->vertex-1]);

            glm::vec3 normal = glm::cross(b-a, c-a);
            for (int i = 0; i < 3; ++i)
            {
                index_t vertexIndex = vertexIndices[i]->vertex;
                this->normals[vertexIndex - 1] += normal;
                vertexIndices[i]->normal = vertexIndex;
            }
        }
    }

    if(normalized)
    {
        for(Vec3Vector::iterator itNormal = this->normals.begin(); itNormal != this->normals.end(); ++itNormal)
        {
            *itNormal = glm::normalize(*itNormal);
        }
    }
}
<commit_msg>removing unecessary headers inclusions<commit_after>#include <map>
#include "glm/geometric.hpp"
#include "ObjModel.hpp"

namespace
{

class VertexIndexIndexer
{
public:

    VertexIndexIndexer() : _object(0)
    {
    }

    VertexIndexIndexer& operator << (vfm::Object &o)
    {
        if (_object != &o)
        {
            _vertexIndexPtrMap.clear();
            _object = &o;
        }
        return *this;
    }

    vfm::index_t getIndex(const vfm::VertexIndex &vi)
    {
        size_t mapSize = _vertexIndexPtrMap.size();
        vfm::index_t *index = &_vertexIndexPtrMap[vi];
        if (_vertexIndexPtrMap.size() > mapSize)
        {
            *index = mapSize;
            _object->vertexIndices.push_back(vi);
        }
        return *index;
    }

private:
    struct VertexIndexPtrComparator {
        bool operator() (const vfm::VertexIndex &vi1, const vfm::VertexIndex &vi2) const
        {
          if (vi1.vertex == vi2.vertex)
          {
              if(vi1.normal == vi2.normal)
              {
                  return vi1.texture < vi2.texture;
              }
              return vi1.normal < vi2.normal;
          }
          return vi1.vertex < vi2.vertex;
        }
    };
    typedef std::map<vfm::VertexIndex, vfm::index_t, VertexIndexPtrComparator> VertexIndexMap;

    vfm::Object *_object;
    VertexIndexMap _vertexIndexPtrMap;
};

inline std::istream& eatline (std::istream& is)
{
    return is.ignore(std::numeric_limits<long>::max(), '\n');
}

}

vfm::VertexIndex::VertexIndex (index_t vertex, index_t normal, index_t texture)
    : vertex(vertex), normal(normal), texture(texture)
{
}

bool vfm::VertexIndex::operator == (const VertexIndex &vi) const
{
    return this->vertex == vi.vertex && this->normal == vi.normal && this->texture == vi.texture;
}

std::istream & vfm::operator >> (std::istream &is, glm::vec3 &v)
{
    is >> v.x;
    if(is.good())
    {
        is >> v.y;
        if (is.fail())
        {
            v.y = 0;
            v.z = 0;
            is.clear();
        }
        else if(is.good())
        {
            is >> v.z;
            if (is.fail())
            {
                v.z = 0;
                is.clear();
            }
        }
    }
    return is;
}

std::istream & vfm::operator >> (std::istream &is, glm::vec4 &v)
{
    is >> v.x >> v.y >> v.z;
    if (is.good())
    {
        is >> v.w;
        // w is optional, no matter if the stream has not
        // succeeded in reading it
        if (is.fail())
        {
            v.w = 1;
            is.clear();
        }
    }
    return is;
}

std::istream & vfm::operator >> (std::istream &is, VertexIndex &vi)
{
    is >> vi.vertex;
    if (is.good() && is.peek() == '/')
    {
        is.ignore(1);
        if(is.good() && is.peek() != '/')
        {
            is >> vi.texture;
        }
        if(is.good() && is.peek() == '/')
        {
            is.ignore(1);
            is >> vi.normal;
        }
    }
    return is;
}

std::istream & vfm::operator >> (std::istream &is, VertexIndexVector &viv)
{
    bool eol = false;
    while(is.good() && !eol)
    {
        VertexIndex vi;
        is >> vi;
        viv.push_back(vi);
        while (is.good())
        {
            int c = is.peek();
            if (c == '#' || c == '\n')
            {
                is >> eatline;
                eol = true;
                break;
            }
            else if (std::isspace(c))
            {
                is.get();
            }
            else {
                break;
            }
        }
    }
    return is;
}

static void createTriangles(const vfm::IndexVector &polygons, vfm::IndexVector &triangles)
{
    vfm::index_t nbIndices = polygons.size();
    if (nbIndices < 3)
    {
        return;
    }

    vfm::index_t descIndex = nbIndices;
    vfm::index_t ascIndex = 2;

    vfm::index_t previous = polygons[0];
    vfm::index_t current = polygons[1];
    vfm::index_t next = polygons[2];

    for(bool asc = false; ascIndex < descIndex; asc = !asc)
    {
        triangles.push_back(previous);
        triangles.push_back(current);
        triangles.push_back(next);

        if (asc)
        {
            current = next;
            next = polygons[++ascIndex];
        }
        else
        {
            current = previous;
            previous = polygons[--descIndex];
        }
    }
}

std::istream & vfm::operator >> (std::istream &is, ObjModel &model)
{
    std::string token;
    glm::vec4 vec4;
    glm::vec3 vec3;
    VertexIndexIndexer vertexIndexIndexer;
    vfm::IndexVector polygons;
    VertexIndexVector face;
    Object *object = 0;

    while (is >> token)
    {
        if (! token.empty() && token[0] == '#')
        {
            // ignore comments
            is >> eatline;
        }
        else if (token == "o")
        {
            model.objects.push_back(Object());
            object = &model.objects.back();
            vertexIndexIndexer << *object;
            is >> object->name;
        }
        else if (token == "v")
        {
            // read new vertex
            is >> vec4;
            if(!is.fail()) model.vertices.push_back(vec4);
        }
        else if (token == "vt")
        {
            // read texture coordinates
            is >> vec3;
            if (!is.fail()) model.textures.push_back(vec3);
        }
        else if (token == "vn")
        {
            // read normals coordinates
            is >> vec3;
            if (!is.fail()) model.normals.push_back(vec3);
        }
        else if (token == "f")
        {
            if (object == 0)
            {
                model.objects.push_back(Object());
                object = &model.objects.back();
                vertexIndexIndexer << *object;
            }

            face.clear();
            // read faces indices
            is >> face;

            if(!is.fail())
            {
                polygons.clear();
                for(vfm::VertexIndexVector::iterator it = face.begin(); it < face.end(); ++it)
                {
                    polygons.push_back(vertexIndexIndexer.getIndex(*it));
                }

                createTriangles(polygons, object->triangles);
            }
        }
        else
        {
            // ignore line
            is >> eatline;
        }
    }

    return is;
}

void vfm::ObjModel::computeNormals(bool normalized)
{
    this->normals.clear();
    this->normals.resize(this->vertices.size());
    VertexIndex *vertexIndices[3];
    for(ObjectVector::iterator itObj = this->objects.begin(); itObj != this->objects.end(); ++itObj)
    {
        Object *o = &(*itObj);
        for(IndexVector::iterator it = o->triangles.begin(); it != o->triangles.end(); ++it)
        {
            vertexIndices[0] = &o->vertexIndices[*it++];
            vertexIndices[1] = &o->vertexIndices[*it++];
            vertexIndices[2] = &o->vertexIndices[*it];
            glm::vec3 a(this->vertices[vertexIndices[0]->vertex-1]);
            glm::vec3 b(this->vertices[vertexIndices[1]->vertex-1]);
            glm::vec3 c(this->vertices[vertexIndices[2]->vertex-1]);

            glm::vec3 normal = glm::cross(b-a, c-a);
            for (int i = 0; i < 3; ++i)
            {
                index_t vertexIndex = vertexIndices[i]->vertex;
                this->normals[vertexIndex - 1] += normal;
                vertexIndices[i]->normal = vertexIndex;
            }
        }
    }

    if(normalized)
    {
        for(Vec3Vector::iterator itNormal = this->normals.begin(); itNormal != this->normals.end(); ++itNormal)
        {
            *itNormal = glm::normalize(*itNormal);
        }
    }
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <unordered_map>

#include "Platform.hpp"
#include "SymbolTable.hpp"
#include "FunctionContext.hpp"
#include "Type.hpp"

#include "mtac/RegisterAllocation.hpp"
#include "mtac/Utils.hpp"

using namespace eddic;

void mtac::register_param_allocation(){
    PlatformDescriptor* descriptor = getPlatformDescriptor(platform);

    for(auto function_info : symbols){
        auto function = function_info.second;

        //Only custom functions have a context
        if(function->context){
            for(unsigned int i = 0; i < function->parameters.size(); ++i){
                auto& parameter = function->parameters[i];
                auto type = parameter.paramType;
                unsigned int position = function->getParameterPositionByType(parameter.name);
                auto param = function->context->getVariable(parameter.name);

                if((type == INT && position <= descriptor->numberOfIntParamRegisters()) || (type == FLOAT && position <= descriptor->numberOfFloatParamRegisters())){
                    Position oldPosition = param->position();

                    function->context->allocate_in_param_register(param, position);

                    //We have to change the position of the all the following parameters
                    for(unsigned int j = i + 1; j < function->parameters.size(); ++j){
                        auto p = function->context->getVariable(function->parameters[j].name);
                        Position paramPosition = p->position();
                        p->setPosition(oldPosition);
                        oldPosition = paramPosition;
                    }
                }
            }
        }
    }
}

typedef std::set<std::shared_ptr<Variable>> Candidates;

void search_candidates(mtac::VariableUsage& usage, Candidates& candidates, std::shared_ptr<Variable> variable, unsigned int variables){
    if(candidates.size() < variables){
        candidates.insert(variable);
    } else {
        std::shared_ptr<Variable> min_var;

        for(auto& v : candidates){
            if(!min_var){
                min_var = v;
            } else if(usage[v] < usage[min_var]){
                min_var = v;
            }
        }

        if(usage[variable] > usage[min_var]){
            candidates.erase(min_var);
            candidates.insert(variable);
        }
    }
}

void mtac::register_variable_allocation(std::shared_ptr<mtac::Program> program){
    PlatformDescriptor* descriptor = getPlatformDescriptor(platform);

    if(descriptor->number_of_variable_registers() > 0 || descriptor->number_of_float_variable_registers() > 0){
        for(auto function : program->functions){
            auto usage = mtac::compute_variable_usage(function);

            Candidates int_var;
            Candidates float_var;

            for(auto variable_pair : function->context->stored_variables()){
                auto variable = variable_pair.second;

                if(usage[variable] > 0){
                    if(variable->type() == INT){
                        search_candidates(usage, int_var, variable, descriptor->number_of_variable_registers());
                    } else if(variable->type() == FLOAT){
                        search_candidates(usage, float_var, variable, descriptor->number_of_float_variable_registers());
                    }
                }
            }

            unsigned int position = 0;
            for(auto variable : int_var){
                function->context->allocate_in_register(variable, ++position);
            }

            position = 0;
            for(auto variable : float_var){
                function->context->allocate_in_register(variable, ++position);
            }
        }
    }
}
<commit_msg>Allocate only stack variables into registers<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <unordered_map>

#include "Platform.hpp"
#include "SymbolTable.hpp"
#include "FunctionContext.hpp"
#include "Type.hpp"

#include "mtac/RegisterAllocation.hpp"
#include "mtac/Utils.hpp"

using namespace eddic;

void mtac::register_param_allocation(){
    PlatformDescriptor* descriptor = getPlatformDescriptor(platform);

    for(auto function_info : symbols){
        auto function = function_info.second;

        //Only custom functions have a context
        if(function->context){
            for(unsigned int i = 0; i < function->parameters.size(); ++i){
                auto& parameter = function->parameters[i];
                auto type = parameter.paramType;
                unsigned int position = function->getParameterPositionByType(parameter.name);
                auto param = function->context->getVariable(parameter.name);

                if((type == INT && position <= descriptor->numberOfIntParamRegisters()) || (type == FLOAT && position <= descriptor->numberOfFloatParamRegisters())){
                    Position oldPosition = param->position();

                    function->context->allocate_in_param_register(param, position);

                    //We have to change the position of the all the following parameters
                    for(unsigned int j = i + 1; j < function->parameters.size(); ++j){
                        auto p = function->context->getVariable(function->parameters[j].name);
                        Position paramPosition = p->position();
                        p->setPosition(oldPosition);
                        oldPosition = paramPosition;
                    }
                }
            }
        }
    }
}

typedef std::set<std::shared_ptr<Variable>> Candidates;

void search_candidates(mtac::VariableUsage& usage, Candidates& candidates, std::shared_ptr<Variable> variable, unsigned int variables){
    if(candidates.size() < variables){
        candidates.insert(variable);
    } else {
        std::shared_ptr<Variable> min_var;

        for(auto& v : candidates){
            if(!min_var){
                min_var = v;
            } else if(usage[v] < usage[min_var]){
                min_var = v;
            }
        }

        if(usage[variable] > usage[min_var]){
            candidates.erase(min_var);
            candidates.insert(variable);
        }
    }
}

void mtac::register_variable_allocation(std::shared_ptr<mtac::Program> program){
    PlatformDescriptor* descriptor = getPlatformDescriptor(platform);

    if(descriptor->number_of_variable_registers() > 0 || descriptor->number_of_float_variable_registers() > 0){
        for(auto function : program->functions){
            auto usage = mtac::compute_variable_usage(function);

            Candidates int_var;
            Candidates float_var;

            for(auto variable_pair : function->context->stored_variables()){
                auto variable = variable_pair.second;

                if(variable->position().isStack()){
                    if(usage[variable] > 0){
                        if(variable->type() == INT){
                            search_candidates(usage, int_var, variable, descriptor->number_of_variable_registers());
                        } else if(variable->type() == FLOAT){
                            search_candidates(usage, float_var, variable, descriptor->number_of_float_variable_registers());
                        }
                    }
                }
            }

            unsigned int position = 0;
            for(auto variable : int_var){
                function->context->allocate_in_register(variable, ++position);
            }

            position = 0;
            for(auto variable : float_var){
                function->context->allocate_in_register(variable, ++position);
            }
        }
    }
}
<|endoftext|>


<commit_before>/**
The MIT License (MIT)

Copyright (c) <2013> <Moritz Schulze>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
**/

#include "NodeSpotify.h"
#include "../../Application.h"
#include "../../exceptions.h"
#include "../../common_macros.h"
#include "../../callbacks/SessionCallbacks.h"
#include "../spotify/SpotifyOptions.h"
#include "NodePlaylist.h"
#include "NodePlaylistFolder.h"
#include "NodePlaylistContainer.h"
#include "NodePlayer.h"
#include "NodeArtist.h"
#include "NodeAlbum.h"
#include "NodeTrack.h"
#include "NodeUser.h"

extern Application* application;

NodeSpotify::NodeSpotify(Handle<Object> options) {
  /*
   * Important note: The session callbacks must be initialized before
   * the sp_session is started. The uv timer and async handle must be set up
   * as they will be used as soon as the sp_session is created, which happens in the
   * NodeSpotify ctor.
   */
  //initiate uv_timer and uv_async
  SessionCallbacks::init();

  SpotifyOptions _options;
  HandleScope scope;
  Handle<String> settingsFolderKey = String::New("settingsFolder");
  Handle<String> cacheFolderKey = String::New("cacheFolder");
  Handle<String> traceFileKey = String::New("traceFile");
  Handle<String> appkeyFileKey = String::New("appkeyFile");
  if(options->Has(settingsFolderKey)) {
    String::Utf8Value settingsFolderValue(options->Get(settingsFolderKey)->ToString());
    _options.settingsFolder = *settingsFolderValue;
  } else {
    _options.settingsFolder = "settings";
  }
  if(options->Has(cacheFolderKey)) {
    String::Utf8Value cacheFolderValue(options->Get(cacheFolderKey)->ToString());
    _options.cacheFolder = *cacheFolderValue;
  } else {
    _options.cacheFolder = "cache";
  }
  if(options->Has(traceFileKey)) {
    String::Utf8Value traceFileValue(options->Get(traceFileKey)->ToString());
    _options.traceFile = *traceFileValue;
  }
  if(options->Has(appkeyFileKey)) {
    String::Utf8Value appkeyFileValue(options->Get(appkeyFileKey)->ToString());
    _options.appkeyFile = *appkeyFileValue;
  }
  spotify = std::unique_ptr<Spotify>(new Spotify(_options));
  scope.Close(Undefined());
}

NodeSpotify::~NodeSpotify() {

}

Handle<Value> NodeSpotify::createFromLink(const Arguments& args) {
  HandleScope scope;
  Handle<Value> out;
  String::Utf8Value linkToParse(args[0]->ToString());
  sp_link* parsedLink = sp_link_create_from_string(*linkToParse);
  if(parsedLink != nullptr) {
    sp_linktype linkType = sp_link_type(parsedLink);
    switch(linkType) {
      case SP_LINKTYPE_TRACK:
      {
        sp_track* track = sp_link_as_track(parsedLink);
        NodeTrack* nodeTrack = new NodeTrack(std::make_shared<Track>(track));
        out = nodeTrack->getV8Object();
        break;
      }
      case SP_LINKTYPE_ALBUM:
      {
        sp_album* album = sp_link_as_album(parsedLink);
        NodeAlbum* nodeAlbum = new NodeAlbum(std::make_shared<Album>(album));
        out = nodeAlbum->getV8Object();
        break;
      }
      case SP_LINKTYPE_ARTIST:
      {
        sp_artist* artist = sp_link_as_artist(parsedLink);
        NodeArtist* nodeArtist = new NodeArtist(std::make_shared<Artist>(artist));
        out = nodeArtist->getV8Object();
        break;
      }
      case SP_LINKTYPE_PLAYLIST:
      {
        sp_playlist* spPlaylist = sp_playlist_create(application->session, parsedLink);
        auto playlist = Playlist::fromCache(spPlaylist);
        NodePlaylist* nodePlaylist = new NodePlaylist(playlist);
        out = nodePlaylist->getV8Object();
        break;
      }
      default:
        out = Undefined();
    }
    sp_link_release(parsedLink);
  } else {
    out = Undefined();
  }
  return scope.Close(out);
}

Handle<Value> NodeSpotify::login(const Arguments& args) {
  HandleScope scope;
  NodeSpotify* nodeSpotify = node::ObjectWrap::Unwrap<NodeSpotify>(args.This());
  String::Utf8Value v8User(args[0]->ToString());
  String::Utf8Value v8Password(args[1]->ToString());
  bool rememberMe = args[2]->ToBoolean()->Value();
  bool withRemembered = args[3]->ToBoolean()->Value();
  std::string user(*v8User);
  std::string password(*v8Password);
  nodeSpotify->spotify->login(user, password, rememberMe, withRemembered);
  return scope.Close(Undefined());
}

Handle<Value> NodeSpotify::logout(const Arguments& args) {
  HandleScope scope;
  if(args.Length() > 0) {
    Handle<Function> fun = Handle<Function>::Cast(args[0]);
    Persistent<Function> p = Persistent<Function>::New(fun);
    SessionCallbacks::logoutCallback = p;
  }
  NodeSpotify* nodeSpotify = node::ObjectWrap::Unwrap<NodeSpotify>(args.This());
  nodeSpotify->spotify->logout();
  return scope.Close(Undefined());
}

Handle<Value> NodeSpotify::getLogoutCallback(Local<String> property, const AccessorInfo& info) {
  HandleScope scope;
  return scope.Close(SessionCallbacks::logoutCallback);
}

void NodeSpotify::setLogoutCallback(Local<String> property, Local<Value> value, const AccessorInfo& info) {
  HandleScope scope;
  Handle<Function> fun = Handle<Function>::Cast(value);
  Persistent<Function> p = Persistent<Function>::New(fun);
  SessionCallbacks::logoutCallback = p;
  scope.Close(Undefined());
}

Handle<Value> NodeSpotify::ready(const Arguments& args) {
  HandleScope scope;
  Handle<Function> fun = Handle<Function>::Cast(args[0]);
  Persistent<Function> p = Persistent<Function>::New(fun);
  SessionCallbacks::loginCallback = p;
  return scope.Close(Undefined());
}

Handle<Value> NodeSpotify::getPlaylistContainer(Local<String> property, const AccessorInfo& info) {
  HandleScope scope;
  NodePlaylistContainer* nodePlaylistContainer = new NodePlaylistContainer(application->playlistContainer);
  return scope.Close(nodePlaylistContainer->getV8Object());
}

Handle<Value> NodeSpotify::getRememberedUser(Local<String> property, const AccessorInfo& info) {
  HandleScope scope;
  NodeSpotify* nodeSpotify = node::ObjectWrap::Unwrap<NodeSpotify>(info.Holder());
  return scope.Close(String::New(nodeSpotify->spotify->rememberedUser().c_str()));
}

Handle<Value> NodeSpotify::getSessionUser(Local<String> property, const AccessorInfo& info) {
  HandleScope scope;
  NodeSpotify* nodeSpotify = node::ObjectWrap::Unwrap<NodeSpotify>(info.Holder());
  NodeUser* nodeUser = new NodeUser(nodeSpotify->spotify->sessionUser());
  return scope.Close(nodeUser->getV8Object());
}

Handle<Value> NodeSpotify::getConstants(Local<String> property, const AccessorInfo& info) {
  HandleScope scope;
  Local<Object> constants = Object::New();
  constants->Set(String::NewSymbol("ARTISTBROWSE_FULL"), Number::New(SP_ARTISTBROWSE_FULL));
  constants->Set(String::NewSymbol("ARTISTBROWSE_NO_TRACKS"), Number::New(SP_ARTISTBROWSE_NO_TRACKS));
  constants->Set(String::NewSymbol("ARTISTBROWSE_NO_ALBUMS"), Number::New(SP_ARTISTBROWSE_NO_ALBUMS));

  constants->Set(String::NewSymbol("PLAYLIST_TYPE_PLAYLIST"), Number::New(SP_PLAYLIST_TYPE_PLAYLIST));
  constants->Set(String::NewSymbol("PLAYLIST_TYPE_START_FOLDER"), Number::New(SP_PLAYLIST_TYPE_START_FOLDER));
  constants->Set(String::NewSymbol("PLAYLIST_TYPE_END_FOLDER"), Number::New(SP_PLAYLIST_TYPE_END_FOLDER));
  return scope.Close(constants);
}

Handle<Value> NodeSpotify::onMetadataUpdated(const Arguments& args) {
  HandleScope scope;
  if(args.Length() < 1) {
    return scope.Close(V8_EXCEPTION("onMetadataUpdated needs a function as an argument."));
  }
  Handle<Function> fun = Handle<Function>::Cast(args[0]);
  Persistent<Function> p = Persistent<Function>::New(fun);
  SessionCallbacks::metadataUpdatedCallback = p;
  return scope.Close(Undefined());
}

void NodeSpotify::init() {
  HandleScope scope;
  Handle<FunctionTemplate> constructorTemplate = NodeWrapped::init("Spotify");
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "login", login);
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "logout", logout);
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "ready", ready);
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "createFromLink", createFromLink);
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "onMetadataUpdated", onMetadataUpdated);
  constructorTemplate->InstanceTemplate()->SetAccessor(String::NewSymbol("rememberedUser"), getRememberedUser);
  constructorTemplate->InstanceTemplate()->SetAccessor(String::NewSymbol("sessionUser"), getSessionUser);
  constructorTemplate->InstanceTemplate()->SetAccessor(String::NewSymbol("playlistContainer"), getPlaylistContainer);
  constructorTemplate->InstanceTemplate()->SetAccessor(String::NewSymbol("constants"), getConstants);
  constructorTemplate->InstanceTemplate()->SetAccessor(String::NewSymbol("onLogout"), getLogoutCallback, setLogoutCallback);
  constructor = Persistent<Function>::New(constructorTemplate->GetFunction());
  scope.Close(Undefined());
}
<commit_msg>Profile links are useable in createFromLink<commit_after>/**
The MIT License (MIT)

Copyright (c) <2013> <Moritz Schulze>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
**/

#include "NodeSpotify.h"
#include "../../Application.h"
#include "../../exceptions.h"
#include "../../common_macros.h"
#include "../../callbacks/SessionCallbacks.h"
#include "../spotify/SpotifyOptions.h"
#include "NodePlaylist.h"
#include "NodePlaylistFolder.h"
#include "NodePlaylistContainer.h"
#include "NodePlayer.h"
#include "NodeArtist.h"
#include "NodeAlbum.h"
#include "NodeTrack.h"
#include "NodeUser.h"

extern Application* application;

NodeSpotify::NodeSpotify(Handle<Object> options) {
  /*
   * Important note: The session callbacks must be initialized before
   * the sp_session is started. The uv timer and async handle must be set up
   * as they will be used as soon as the sp_session is created, which happens in the
   * NodeSpotify ctor.
   */
  //initiate uv_timer and uv_async
  SessionCallbacks::init();

  SpotifyOptions _options;
  HandleScope scope;
  Handle<String> settingsFolderKey = String::New("settingsFolder");
  Handle<String> cacheFolderKey = String::New("cacheFolder");
  Handle<String> traceFileKey = String::New("traceFile");
  Handle<String> appkeyFileKey = String::New("appkeyFile");
  if(options->Has(settingsFolderKey)) {
    String::Utf8Value settingsFolderValue(options->Get(settingsFolderKey)->ToString());
    _options.settingsFolder = *settingsFolderValue;
  } else {
    _options.settingsFolder = "settings";
  }
  if(options->Has(cacheFolderKey)) {
    String::Utf8Value cacheFolderValue(options->Get(cacheFolderKey)->ToString());
    _options.cacheFolder = *cacheFolderValue;
  } else {
    _options.cacheFolder = "cache";
  }
  if(options->Has(traceFileKey)) {
    String::Utf8Value traceFileValue(options->Get(traceFileKey)->ToString());
    _options.traceFile = *traceFileValue;
  }
  if(options->Has(appkeyFileKey)) {
    String::Utf8Value appkeyFileValue(options->Get(appkeyFileKey)->ToString());
    _options.appkeyFile = *appkeyFileValue;
  }
  spotify = std::unique_ptr<Spotify>(new Spotify(_options));
  scope.Close(Undefined());
}

NodeSpotify::~NodeSpotify() {

}

Handle<Value> NodeSpotify::createFromLink(const Arguments& args) {
  HandleScope scope;
  Handle<Value> out;
  String::Utf8Value linkToParse(args[0]->ToString());
  sp_link* parsedLink = sp_link_create_from_string(*linkToParse);
  if(parsedLink != nullptr) {
    sp_linktype linkType = sp_link_type(parsedLink);
    switch(linkType) {
      case SP_LINKTYPE_TRACK:
      {
        sp_track* track = sp_link_as_track(parsedLink);
        NodeTrack* nodeTrack = new NodeTrack(std::make_shared<Track>(track));
        out = nodeTrack->getV8Object();
        break;
      }
      case SP_LINKTYPE_ALBUM:
      {
        sp_album* album = sp_link_as_album(parsedLink);
        NodeAlbum* nodeAlbum = new NodeAlbum(std::make_shared<Album>(album));
        out = nodeAlbum->getV8Object();
        break;
      }
      case SP_LINKTYPE_ARTIST:
      {
        sp_artist* artist = sp_link_as_artist(parsedLink);
        NodeArtist* nodeArtist = new NodeArtist(std::make_shared<Artist>(artist));
        out = nodeArtist->getV8Object();
        break;
      }
      case SP_LINKTYPE_PROFILE:
      {
        sp_user* user = sp_link_as_user(parsedLink);
        NodeUser* nodeUser = new NodeUser(std::make_shared<User>(user));
        out = nodeUser->getV8Object();
        break;
      }
      case SP_LINKTYPE_PLAYLIST:
      {
        sp_playlist* spPlaylist = sp_playlist_create(application->session, parsedLink);
        auto playlist = Playlist::fromCache(spPlaylist);
        NodePlaylist* nodePlaylist = new NodePlaylist(playlist);
        out = nodePlaylist->getV8Object();
        break;
      }
      default:
        out = Undefined();
    }
    sp_link_release(parsedLink);
  } else {
    out = Undefined();
  }
  return scope.Close(out);
}

Handle<Value> NodeSpotify::login(const Arguments& args) {
  HandleScope scope;
  NodeSpotify* nodeSpotify = node::ObjectWrap::Unwrap<NodeSpotify>(args.This());
  String::Utf8Value v8User(args[0]->ToString());
  String::Utf8Value v8Password(args[1]->ToString());
  bool rememberMe = args[2]->ToBoolean()->Value();
  bool withRemembered = args[3]->ToBoolean()->Value();
  std::string user(*v8User);
  std::string password(*v8Password);
  nodeSpotify->spotify->login(user, password, rememberMe, withRemembered);
  return scope.Close(Undefined());
}

Handle<Value> NodeSpotify::logout(const Arguments& args) {
  HandleScope scope;
  if(args.Length() > 0) {
    Handle<Function> fun = Handle<Function>::Cast(args[0]);
    Persistent<Function> p = Persistent<Function>::New(fun);
    SessionCallbacks::logoutCallback = p;
  }
  NodeSpotify* nodeSpotify = node::ObjectWrap::Unwrap<NodeSpotify>(args.This());
  nodeSpotify->spotify->logout();
  return scope.Close(Undefined());
}

Handle<Value> NodeSpotify::getLogoutCallback(Local<String> property, const AccessorInfo& info) {
  HandleScope scope;
  return scope.Close(SessionCallbacks::logoutCallback);
}

void NodeSpotify::setLogoutCallback(Local<String> property, Local<Value> value, const AccessorInfo& info) {
  HandleScope scope;
  Handle<Function> fun = Handle<Function>::Cast(value);
  Persistent<Function> p = Persistent<Function>::New(fun);
  SessionCallbacks::logoutCallback = p;
  scope.Close(Undefined());
}

Handle<Value> NodeSpotify::ready(const Arguments& args) {
  HandleScope scope;
  Handle<Function> fun = Handle<Function>::Cast(args[0]);
  Persistent<Function> p = Persistent<Function>::New(fun);
  SessionCallbacks::loginCallback = p;
  return scope.Close(Undefined());
}

Handle<Value> NodeSpotify::getPlaylistContainer(Local<String> property, const AccessorInfo& info) {
  HandleScope scope;
  NodePlaylistContainer* nodePlaylistContainer = new NodePlaylistContainer(application->playlistContainer);
  return scope.Close(nodePlaylistContainer->getV8Object());
}

Handle<Value> NodeSpotify::getRememberedUser(Local<String> property, const AccessorInfo& info) {
  HandleScope scope;
  NodeSpotify* nodeSpotify = node::ObjectWrap::Unwrap<NodeSpotify>(info.Holder());
  return scope.Close(String::New(nodeSpotify->spotify->rememberedUser().c_str()));
}

Handle<Value> NodeSpotify::getSessionUser(Local<String> property, const AccessorInfo& info) {
  HandleScope scope;
  NodeSpotify* nodeSpotify = node::ObjectWrap::Unwrap<NodeSpotify>(info.Holder());
  NodeUser* nodeUser = new NodeUser(nodeSpotify->spotify->sessionUser());
  return scope.Close(nodeUser->getV8Object());
}

Handle<Value> NodeSpotify::getConstants(Local<String> property, const AccessorInfo& info) {
  HandleScope scope;
  Local<Object> constants = Object::New();
  constants->Set(String::NewSymbol("ARTISTBROWSE_FULL"), Number::New(SP_ARTISTBROWSE_FULL));
  constants->Set(String::NewSymbol("ARTISTBROWSE_NO_TRACKS"), Number::New(SP_ARTISTBROWSE_NO_TRACKS));
  constants->Set(String::NewSymbol("ARTISTBROWSE_NO_ALBUMS"), Number::New(SP_ARTISTBROWSE_NO_ALBUMS));

  constants->Set(String::NewSymbol("PLAYLIST_TYPE_PLAYLIST"), Number::New(SP_PLAYLIST_TYPE_PLAYLIST));
  constants->Set(String::NewSymbol("PLAYLIST_TYPE_START_FOLDER"), Number::New(SP_PLAYLIST_TYPE_START_FOLDER));
  constants->Set(String::NewSymbol("PLAYLIST_TYPE_END_FOLDER"), Number::New(SP_PLAYLIST_TYPE_END_FOLDER));
  return scope.Close(constants);
}

Handle<Value> NodeSpotify::onMetadataUpdated(const Arguments& args) {
  HandleScope scope;
  if(args.Length() < 1) {
    return scope.Close(V8_EXCEPTION("onMetadataUpdated needs a function as an argument."));
  }
  Handle<Function> fun = Handle<Function>::Cast(args[0]);
  Persistent<Function> p = Persistent<Function>::New(fun);
  SessionCallbacks::metadataUpdatedCallback = p;
  return scope.Close(Undefined());
}

void NodeSpotify::init() {
  HandleScope scope;
  Handle<FunctionTemplate> constructorTemplate = NodeWrapped::init("Spotify");
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "login", login);
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "logout", logout);
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "ready", ready);
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "createFromLink", createFromLink);
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "onMetadataUpdated", onMetadataUpdated);
  constructorTemplate->InstanceTemplate()->SetAccessor(String::NewSymbol("rememberedUser"), getRememberedUser);
  constructorTemplate->InstanceTemplate()->SetAccessor(String::NewSymbol("sessionUser"), getSessionUser);
  constructorTemplate->InstanceTemplate()->SetAccessor(String::NewSymbol("playlistContainer"), getPlaylistContainer);
  constructorTemplate->InstanceTemplate()->SetAccessor(String::NewSymbol("constants"), getConstants);
  constructorTemplate->InstanceTemplate()->SetAccessor(String::NewSymbol("onLogout"), getLogoutCallback, setLogoutCallback);
  constructor = Persistent<Function>::New(constructorTemplate->GetFunction());
  scope.Close(Undefined());
}
<|endoftext|>


<commit_before>#include "oddlib/compressiontype3.hpp"
#include "oddlib/stream.hpp"
#include "logger.hpp"
#include <windows.h>

namespace Oddlib
{
    static void NextBits(signed int& bitCounter, unsigned int& src_data, IStream& stream)
    {
        if (bitCounter > 0)
        {
            if (bitCounter == 14)
            {
                bitCounter = 30;
                src_data = (ReadUint16(stream) << 14) | src_data;
            }
        }
        else
        {
            bitCounter = 32;
            src_data = ReadUint32(stream);
        }
        bitCounter -= 6;
    }

    // Function 0x004031E0 in AO
    // NOTE: A lot of the code in AbeWin.exe for this algorithm is dead, it attempts to gain some "other" buffer at the end of the
    // animation data which actually doesn't exist. Thus all this "extra" code does is write black pixels to an already black canvas.
    std::vector<Uint8> CompressionType3::Decompress(IStream& stream, Uint32 finalW, Uint32 /*w*/, Uint32 h, Uint32 dataSize)
    {
        size_t dstPos = 0;
        std::vector<unsigned char> buffer;
        buffer.resize(finalW*h);

        int numBytesInFrameCnt = dataSize & 0xFFFFFFFC;
        if (numBytesInFrameCnt > 0)
        {
            unsigned int src_data = 0;
            signed int bitCounter = 0;
            do
            {
                NextBits(bitCounter, src_data, stream);
                unsigned char bits = src_data & 0x3F;
                src_data = src_data >> 6;
                 --numBytesInFrameCnt;
                if (bits & 0x20)
                {
                    // 0x20 = 0b100000
                    // 0x3F = 0b111111
                    int numberOfBytes = (bits & 0x1F) + 1;
                    if (numberOfBytes)
                    {
                        do
                        {
                            if (numBytesInFrameCnt && dstPos < buffer.size())
                            {
                                NextBits(bitCounter, src_data, stream);
                                bits = src_data & 0x3F;
                                src_data = src_data >> 6;
                                 --numBytesInFrameCnt;
                            }
                            else
                            {
                                bits = 0;
                            }
                            buffer[dstPos++] = bits;
                        } while (--numberOfBytes != 0);
                    }
                }
                else
                {
                    // literal flag isn't set, so we have up to 5 bits of "black" pixels
                    dstPos += bits + 1;
                }

            } while (numBytesInFrameCnt);
        }

        return buffer;
    }
}
<commit_msg>remove windows.h<commit_after>#include "oddlib/compressiontype3.hpp"
#include "oddlib/stream.hpp"
#include "logger.hpp"

namespace Oddlib
{
    static void NextBits(signed int& bitCounter, unsigned int& src_data, IStream& stream)
    {
        if (bitCounter > 0)
        {
            if (bitCounter == 14)
            {
                bitCounter = 30;
                src_data = (ReadUint16(stream) << 14) | src_data;
            }
        }
        else
        {
            bitCounter = 32;
            src_data = ReadUint32(stream);
        }
        bitCounter -= 6;
    }

    // Function 0x004031E0 in AO
    // NOTE: A lot of the code in AbeWin.exe for this algorithm is dead, it attempts to gain some "other" buffer at the end of the
    // animation data which actually doesn't exist. Thus all this "extra" code does is write black pixels to an already black canvas.
    std::vector<Uint8> CompressionType3::Decompress(IStream& stream, Uint32 finalW, Uint32 /*w*/, Uint32 h, Uint32 dataSize)
    {
        size_t dstPos = 0;
        std::vector<unsigned char> buffer;
        buffer.resize(finalW*h);

        int numBytesInFrameCnt = (dataSize /4) *4;
        if (numBytesInFrameCnt > 0)
        {
            unsigned int src_data = 0;
            signed int bitCounter = 0;
            do
            {
                NextBits(bitCounter, src_data, stream);
                unsigned char bits = src_data & 0x3F;
                src_data = src_data >> 6;
                 --numBytesInFrameCnt;
                if (bits & 0x20)
                {
                    // 0x20 = 0b100000
                    // 0x3F = 0b111111
                    int numberOfBytes = (bits & 0x1F) + 1;
                    if (numberOfBytes)
                    {
                        do
                        {
                            if (numBytesInFrameCnt && dstPos < buffer.size())
                            {
                                NextBits(bitCounter, src_data, stream);
                                bits = src_data & 0x3F;
                                src_data = src_data >> 6;
                                 --numBytesInFrameCnt;
                            }
                            else
                            {
                                bits = 0;
                            }
                            buffer[dstPos++] = bits;
                        } while (--numberOfBytes != 0);
                    }
                }
                else
                {
                    // literal flag isn't set, so we have up to 5 bits of "black" pixels
                    dstPos += bits + 1;
                }

            } while (numBytesInFrameCnt);
        }

        return buffer;
    }
}
<|endoftext|>


<commit_before>
// Copyright (c) 2015 Pierre MOULON.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "openMVG/sfm/sfm_data_io.hpp"

#include "openMVG/stl/stlMap.hpp"
#include "third_party/stlplus3/filesystemSimplified/file_system.hpp"

#include "openMVG/sfm/sfm_data_io_cereal.hpp"
#include "openMVG/sfm/sfm_data_io_ply.hpp"
#include "openMVG/sfm/sfm_data_io_baf.hpp"
#include "openMVG/sfm/sfm_data_io_gt.hpp"

#include "openMVG/sfm/AlembicExporter.hpp"
#include "openMVG/sfm/AlembicImporter.hpp"

namespace openMVG {
namespace sfm {

///Check that each pose have a valid intrinsic and pose id in the existing View ids
bool ValidIds(const SfM_Data & sfm_data, ESfM_Data flags_part)
{
  const bool bCheck_Intrinsic = (flags_part & INTRINSICS);
  const bool bCheck_Extrinsic = (flags_part & EXTRINSICS);

  std::set<IndexT> intrinsicIdsDeclared;
  transform(sfm_data.GetIntrinsics().begin(), sfm_data.GetIntrinsics().end(),
    std::inserter(intrinsicIdsDeclared, intrinsicIdsDeclared.begin()), stl::RetrieveKey());

  std::set<IndexT> extrinsicIdsDeclared; //unique so can use a set
  transform(sfm_data.GetPoses().begin(), sfm_data.GetPoses().end(),
    std::inserter(extrinsicIdsDeclared, extrinsicIdsDeclared.begin()), stl::RetrieveKey());

  // Collect id_intrinsic and id_extrinsic referenced from views
  std::set<IndexT> intrinsicIdsReferenced;
  std::set<IndexT> extrinsicIdsReferenced;
  for(const auto& v: sfm_data.GetViews())
  {
    const IndexT id_intrinsic = v.second.get()->id_intrinsic;
    intrinsicIdsReferenced.insert(id_intrinsic);

    const IndexT id_pose = v.second.get()->id_pose;
    extrinsicIdsReferenced.insert(id_pose);
  }

  // We may have some views with undefined Intrinsics,
  // so erase the UndefinedIndex value if exist.
  intrinsicIdsReferenced.erase(UndefinedIndexT);
  extrinsicIdsReferenced.erase(UndefinedIndexT);

  // Check if defined intrinsic & extrinsic are at least connected to views
  bool bRet = true;
  if(bCheck_Intrinsic && intrinsicIdsDeclared != intrinsicIdsReferenced)
  {
    std::cerr << "The number of intrinsics is incoherent:" << std::endl;
    std::cerr << intrinsicIdsDeclared.size() << " intrinsics declared and " << intrinsicIdsReferenced.size() << " intrinsics used." << std::endl;
    std::set<IndexT> undefinedIntrinsicIds;
    // undefinedIntrinsicIds = intrinsicIdsReferenced - intrinsicIdsDeclared
    std::set_difference(intrinsicIdsReferenced.begin(), intrinsicIdsReferenced.end(),
                        intrinsicIdsDeclared.begin(), intrinsicIdsDeclared.end(),
                        std::inserter(undefinedIntrinsicIds, undefinedIntrinsicIds.begin()));
    // If undefinedIntrinsicIds is not empty,
    // some intrinsics are used in Views but never declared.
    // So the file structure is invalid and may create troubles.
    if(!undefinedIntrinsicIds.empty())
      bRet = false; // error
  }

  if (bCheck_Extrinsic && extrinsicIdsDeclared != extrinsicIdsReferenced)
  {
    std::cerr << "The number of extrinsics is incoherent:" << std::endl;
    std::cerr << extrinsicIdsDeclared.size() << " extrinsics declared and " << extrinsicIdsReferenced.size() << " extrinsics used." << std::endl;
    std::set<IndexT> undefinedExtrinsicIds;
    // undefinedExtrinsicIds = extrinsicIdsReferenced - extrinsicIdsDeclared
    std::set_difference(extrinsicIdsDeclared.begin(), extrinsicIdsDeclared.end(),
                        extrinsicIdsReferenced.begin(), extrinsicIdsReferenced.end(),
                        std::inserter(undefinedExtrinsicIds, undefinedExtrinsicIds.begin()));
    // If undefinedExtrinsicIds is not empty,
    // some extrinsics are used in Views but never declared.
    // So the file structure is invalid and may create troubles.
    if(!undefinedExtrinsicIds.empty())
      bRet = false; // error
  }

  return bRet;
}

bool Load(SfM_Data & sfm_data, const std::string & filename, ESfM_Data flags_part)
{
  bool bStatus = false;
  const std::string ext = stlplus::extension_part(filename);
  if (ext == "json")
    bStatus = Load_Cereal<cereal::JSONInputArchive>(sfm_data, filename, flags_part);
  else if (ext == "bin")
    bStatus = Load_Cereal<cereal::PortableBinaryInputArchive>(sfm_data, filename, flags_part);
  else if (ext == "xml")
    bStatus = Load_Cereal<cereal::XMLInputArchive>(sfm_data, filename, flags_part);
#if HAVE_ALEMBIC
  else if (ext == "abc") {
    openMVG::dataio::AlembicImporter(filename).populate(sfm_data, flags_part);
    bStatus = true;
  }
#endif // HAVE_ALEMBIC
  else
  {
    // Try if it's a folder
    if (stlplus::folder_exists(filename))
    {
      bStatus = readGt(filename, sfm_data);
    }
    // It is not a folder or known format, return false
    else
    {
      std::cerr << "Unknown sfm_data input format: " << ext << std::endl;
      return false;
    }
  }

  // Assert that loaded intrinsics | extrinsics are linked to valid view
  if(bStatus &&
     (flags_part & VIEWS) &&
     ((flags_part & INTRINSICS) || (flags_part & EXTRINSICS)))
  {
    return ValidIds(sfm_data, flags_part);
  }
  return bStatus;
}

bool Save(const SfM_Data & sfm_data, const std::string & filename, ESfM_Data flags_part)
{
  const std::string ext = stlplus::extension_part(filename);
  if (ext == "json")
    return Save_Cereal<cereal::JSONOutputArchive>(sfm_data, filename, flags_part);
  else if (ext == "bin")
    return Save_Cereal<cereal::PortableBinaryOutputArchive>(sfm_data, filename, flags_part);
  else if (ext == "xml")
    return Save_Cereal<cereal::XMLOutputArchive>(sfm_data, filename, flags_part);
  else if (ext == "ply")
    return Save_PLY(sfm_data, filename, flags_part);
  else if (ext == "baf") // Bundle Adjustment file
    return Save_BAF(sfm_data, filename, flags_part);
#if HAVE_ALEMBIC
  else if (ext == "abc") // Alembic
  {
    openMVG::dataio::AlembicExporter(filename).add(sfm_data, flags_part);
    return true;
  }
#endif // HAVE_ALEMBIC
  std::cerr << "ERROR: Cannot save the SfM Data: " << filename << ".\n"
            << "The file extension is not recognized." << std::endl;
  return false;
}

} // namespace sfm
} // namespace openMVG

<commit_msg>remove useless else #157<commit_after>
// Copyright (c) 2015 Pierre MOULON.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "openMVG/sfm/sfm_data_io.hpp"

#include "openMVG/stl/stlMap.hpp"
#include "third_party/stlplus3/filesystemSimplified/file_system.hpp"

#include "openMVG/sfm/sfm_data_io_cereal.hpp"
#include "openMVG/sfm/sfm_data_io_ply.hpp"
#include "openMVG/sfm/sfm_data_io_baf.hpp"
#include "openMVG/sfm/sfm_data_io_gt.hpp"

#include "openMVG/sfm/AlembicExporter.hpp"
#include "openMVG/sfm/AlembicImporter.hpp"

namespace openMVG {
namespace sfm {

///Check that each pose have a valid intrinsic and pose id in the existing View ids
bool ValidIds(const SfM_Data & sfm_data, ESfM_Data flags_part)
{
  const bool bCheck_Intrinsic = (flags_part & INTRINSICS);
  const bool bCheck_Extrinsic = (flags_part & EXTRINSICS);

  std::set<IndexT> intrinsicIdsDeclared;
  transform(sfm_data.GetIntrinsics().begin(), sfm_data.GetIntrinsics().end(),
    std::inserter(intrinsicIdsDeclared, intrinsicIdsDeclared.begin()), stl::RetrieveKey());

  std::set<IndexT> extrinsicIdsDeclared; //unique so can use a set
  transform(sfm_data.GetPoses().begin(), sfm_data.GetPoses().end(),
    std::inserter(extrinsicIdsDeclared, extrinsicIdsDeclared.begin()), stl::RetrieveKey());

  // Collect id_intrinsic and id_extrinsic referenced from views
  std::set<IndexT> intrinsicIdsReferenced;
  std::set<IndexT> extrinsicIdsReferenced;
  for(const auto& v: sfm_data.GetViews())
  {
    const IndexT id_intrinsic = v.second.get()->id_intrinsic;
    intrinsicIdsReferenced.insert(id_intrinsic);

    const IndexT id_pose = v.second.get()->id_pose;
    extrinsicIdsReferenced.insert(id_pose);
  }

  // We may have some views with undefined Intrinsics,
  // so erase the UndefinedIndex value if exist.
  intrinsicIdsReferenced.erase(UndefinedIndexT);
  extrinsicIdsReferenced.erase(UndefinedIndexT);

  // Check if defined intrinsic & extrinsic are at least connected to views
  bool bRet = true;
  if(bCheck_Intrinsic && intrinsicIdsDeclared != intrinsicIdsReferenced)
  {
    std::cerr << "The number of intrinsics is incoherent:" << std::endl;
    std::cerr << intrinsicIdsDeclared.size() << " intrinsics declared and " << intrinsicIdsReferenced.size() << " intrinsics used." << std::endl;
    std::set<IndexT> undefinedIntrinsicIds;
    // undefinedIntrinsicIds = intrinsicIdsReferenced - intrinsicIdsDeclared
    std::set_difference(intrinsicIdsReferenced.begin(), intrinsicIdsReferenced.end(),
                        intrinsicIdsDeclared.begin(), intrinsicIdsDeclared.end(),
                        std::inserter(undefinedIntrinsicIds, undefinedIntrinsicIds.begin()));
    // If undefinedIntrinsicIds is not empty,
    // some intrinsics are used in Views but never declared.
    // So the file structure is invalid and may create troubles.
    if(!undefinedIntrinsicIds.empty())
      bRet = false; // error
  }

  if (bCheck_Extrinsic && extrinsicIdsDeclared != extrinsicIdsReferenced)
  {
    std::cerr << "The number of extrinsics is incoherent:" << std::endl;
    std::cerr << extrinsicIdsDeclared.size() << " extrinsics declared and " << extrinsicIdsReferenced.size() << " extrinsics used." << std::endl;
    std::set<IndexT> undefinedExtrinsicIds;
    // undefinedExtrinsicIds = extrinsicIdsReferenced - extrinsicIdsDeclared
    std::set_difference(extrinsicIdsDeclared.begin(), extrinsicIdsDeclared.end(),
                        extrinsicIdsReferenced.begin(), extrinsicIdsReferenced.end(),
                        std::inserter(undefinedExtrinsicIds, undefinedExtrinsicIds.begin()));
    // If undefinedExtrinsicIds is not empty,
    // some extrinsics are used in Views but never declared.
    // So the file structure is invalid and may create troubles.
    if(!undefinedExtrinsicIds.empty())
      bRet = false; // error
  }

  return bRet;
}

bool Load(SfM_Data & sfm_data, const std::string & filename, ESfM_Data flags_part)
{
  bool bStatus = false;
  const std::string ext = stlplus::extension_part(filename);
  if (ext == "json")
    bStatus = Load_Cereal<cereal::JSONInputArchive>(sfm_data, filename, flags_part);
  else if (ext == "bin")
    bStatus = Load_Cereal<cereal::PortableBinaryInputArchive>(sfm_data, filename, flags_part);
  else if (ext == "xml")
    bStatus = Load_Cereal<cereal::XMLInputArchive>(sfm_data, filename, flags_part);
#if HAVE_ALEMBIC
  else if (ext == "abc") {
    openMVG::dataio::AlembicImporter(filename).populate(sfm_data, flags_part);
    bStatus = true;
  }
#endif // HAVE_ALEMBIC
  else if (stlplus::folder_exists(filename))
  {
    bStatus = readGt(filename, sfm_data);
  }
  // It is not a folder or known format, return false
  else
  {
    std::cerr << "Unknown sfm_data input format: " << ext << std::endl;
    return false;
  }

  // Assert that loaded intrinsics | extrinsics are linked to valid view
  if(bStatus &&
     (flags_part & VIEWS) &&
     ((flags_part & INTRINSICS) || (flags_part & EXTRINSICS)))
  {
    return ValidIds(sfm_data, flags_part);
  }
  return bStatus;
}

bool Save(const SfM_Data & sfm_data, const std::string & filename, ESfM_Data flags_part)
{
  const std::string ext = stlplus::extension_part(filename);
  if (ext == "json")
    return Save_Cereal<cereal::JSONOutputArchive>(sfm_data, filename, flags_part);
  else if (ext == "bin")
    return Save_Cereal<cereal::PortableBinaryOutputArchive>(sfm_data, filename, flags_part);
  else if (ext == "xml")
    return Save_Cereal<cereal::XMLOutputArchive>(sfm_data, filename, flags_part);
  else if (ext == "ply")
    return Save_PLY(sfm_data, filename, flags_part);
  else if (ext == "baf") // Bundle Adjustment file
    return Save_BAF(sfm_data, filename, flags_part);
#if HAVE_ALEMBIC
  else if (ext == "abc") // Alembic
  {
    openMVG::dataio::AlembicExporter(filename).add(sfm_data, flags_part);
    return true;
  }
#endif // HAVE_ALEMBIC
  std::cerr << "ERROR: Cannot save the SfM Data: " << filename << ".\n"
            << "The file extension is not recognized." << std::endl;
  return false;
}

} // namespace sfm
} // namespace openMVG

<|endoftext|>