<commit_before>/*
 * Copyright (C) 2012 Google Inc. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS CONTRIBUTORS 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 "web/LinkHighlight.h"

#include "SkMatrix44.h"
#include "core/dom/Node.h"
#include "core/dom/NodeRenderingTraversal.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/layout/Layer.h"
#include "core/layout/LayoutLayerModelObject.h"
#include "core/layout/LayoutObject.h"
#include "core/layout/compositing/CompositedLayerMapping.h"
#include "core/layout/style/ShadowData.h"
#include "core/rendering/RenderView.h"
#include "platform/graphics/Color.h"
#include "platform/graphics/paint/DisplayItemList.h"
#include "platform/graphics/paint/DrawingRecorder.h"
#include "public/platform/Platform.h"
#include "public/platform/WebCompositorAnimationCurve.h"
#include "public/platform/WebCompositorSupport.h"
#include "public/platform/WebFloatAnimationCurve.h"
#include "public/platform/WebFloatPoint.h"
#include "public/platform/WebRect.h"
#include "public/platform/WebSize.h"
#include "public/web/WebKit.h"
#include "web/WebLocalFrameImpl.h"
#include "web/WebSettingsImpl.h"
#include "web/WebViewImpl.h"
#include "wtf/CurrentTime.h"

namespace blink {

class WebViewImpl;

PassOwnPtr<LinkHighlight> LinkHighlight::create(Node* node, WebViewImpl* owningWebViewImpl)
{
    return adoptPtr(new LinkHighlight(node, owningWebViewImpl));
}

LinkHighlight::LinkHighlight(Node* node, WebViewImpl* owningWebViewImpl)
    : m_node(node)
    , m_owningWebViewImpl(owningWebViewImpl)
    , m_currentGraphicsLayer(0)
    , m_geometryNeedsUpdate(false)
    , m_isAnimating(false)
    , m_startTime(monotonicallyIncreasingTime())
{
    ASSERT(m_node);
    ASSERT(owningWebViewImpl);
    WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport();
    m_contentLayer = adoptPtr(compositorSupport->createContentLayer(this));
    m_clipLayer = adoptPtr(compositorSupport->createLayer());
    m_clipLayer->setTransformOrigin(WebFloatPoint3D());
    m_clipLayer->addChild(m_contentLayer->layer());
    m_contentLayer->layer()->setAnimationDelegate(this);
    m_contentLayer->layer()->setDrawsContent(true);
    m_contentLayer->layer()->setOpacity(1);
    m_geometryNeedsUpdate = true;
    updateGeometry();
}

LinkHighlight::~LinkHighlight()
{
    clearGraphicsLayerLinkHighlightPointer();
    releaseResources();
}

WebContentLayer* LinkHighlight::contentLayer()
{
    return m_contentLayer.get();
}

WebLayer* LinkHighlight::clipLayer()
{
    return m_clipLayer.get();
}

void LinkHighlight::releaseResources()
{
    m_node.clear();
}

void LinkHighlight::attachLinkHighlightToCompositingLayer(const LayoutLayerModelObject* paintInvalidationContainer)
{
    GraphicsLayer* newGraphicsLayer = paintInvalidationContainer->layer()->graphicsLayerBacking();
    if (!newGraphicsLayer->drawsContent())
        newGraphicsLayer = paintInvalidationContainer->layer()->graphicsLayerBackingForScrolling();
    if (!newGraphicsLayer)
        return;

    m_clipLayer->setTransform(SkMatrix44(SkMatrix44::kIdentity_Constructor));

    if (m_currentGraphicsLayer != newGraphicsLayer) {
        if (m_currentGraphicsLayer)
            clearGraphicsLayerLinkHighlightPointer();

        m_currentGraphicsLayer = newGraphicsLayer;
        m_currentGraphicsLayer->addLinkHighlight(this);
    }
}

static void convertTargetSpaceQuadToCompositedLayer(const FloatQuad& targetSpaceQuad, LayoutObject* targetRenderer, const LayoutLayerModelObject* paintInvalidationContainer, FloatQuad& compositedSpaceQuad)
{
    ASSERT(targetRenderer);
    ASSERT(paintInvalidationContainer);
    for (unsigned i = 0; i < 4; ++i) {
        IntPoint point;
        switch (i) {
        case 0: point = roundedIntPoint(targetSpaceQuad.p1()); break;
        case 1: point = roundedIntPoint(targetSpaceQuad.p2()); break;
        case 2: point = roundedIntPoint(targetSpaceQuad.p3()); break;
        case 3: point = roundedIntPoint(targetSpaceQuad.p4()); break;
        }

        // FIXME: this does not need to be absolute, just in the paint invalidation container's space.
        point = targetRenderer->frame()->view()->contentsToWindow(point);
        point = paintInvalidationContainer->frame()->view()->windowToContents(point);
        FloatPoint floatPoint = paintInvalidationContainer->absoluteToLocal(point, UseTransforms);
        Layer::mapPointToPaintBackingCoordinates(paintInvalidationContainer, floatPoint);

        switch (i) {
        case 0: compositedSpaceQuad.setP1(floatPoint); break;
        case 1: compositedSpaceQuad.setP2(floatPoint); break;
        case 2: compositedSpaceQuad.setP3(floatPoint); break;
        case 3: compositedSpaceQuad.setP4(floatPoint); break;
        }
    }
}

static void addQuadToPath(const FloatQuad& quad, Path& path)
{
    // FIXME: Make this create rounded quad-paths, just like the axis-aligned case.
    path.moveTo(quad.p1());
    path.addLineTo(quad.p2());
    path.addLineTo(quad.p3());
    path.addLineTo(quad.p4());
    path.closeSubpath();
}

void LinkHighlight::computeQuads(const Node& node, Vector<FloatQuad>& outQuads) const
{
    if (!node.renderer())
        return;

    LayoutObject* renderer = node.renderer();

    // For inline elements, absoluteQuads will return a line box based on the line-height
    // and font metrics, which is technically incorrect as replaced elements like images
    // should use their intristic height and expand the linebox  as needed. To get an
    // appropriately sized highlight we descend into the children and have them add their
    // boxes.
    if (renderer->isRenderInline()) {
        for (Node* child = NodeRenderingTraversal::firstChild(node); child; child = NodeRenderingTraversal::nextSibling(*child))
            computeQuads(*child, outQuads);
    } else {
        // FIXME: this does not need to be absolute, just in the paint invalidation container's space.
        renderer->absoluteQuads(outQuads);
    }
}

bool LinkHighlight::computeHighlightLayerPathAndPosition(const LayoutLayerModelObject* paintInvalidationContainer)
{
    if (!m_node || !m_node->renderer() || !m_currentGraphicsLayer)
        return false;
    ASSERT(paintInvalidationContainer);

    // Get quads for node in absolute coordinates.
    Vector<FloatQuad> quads;
    computeQuads(*m_node, quads);
    ASSERT(quads.size());
    Path newPath;

    FloatPoint positionAdjustForCompositedScrolling = IntPoint(m_currentGraphicsLayer->offsetFromRenderer());

    for (size_t quadIndex = 0; quadIndex < quads.size(); ++quadIndex) {
        FloatQuad absoluteQuad = quads[quadIndex];

        // FIXME: this hack should not be necessary. It's a consequence of the fact that composited layers for scrolling are represented
        // differently in Blink than other composited layers.
        if (paintInvalidationContainer->layer()->needsCompositedScrolling() && m_node->renderer() != paintInvalidationContainer)
            absoluteQuad.move(-positionAdjustForCompositedScrolling.x(), -positionAdjustForCompositedScrolling.y());

        // Transform node quads in target absolute coords to local coordinates in the compositor layer.
        FloatQuad transformedQuad;
        convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->renderer(), paintInvalidationContainer, transformedQuad);

        // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that
        // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage
        // links: these should ideally be merged into a single rect before creating the path, but that's
        // another CL.
        if (quads.size() == 1 && transformedQuad.isRectilinear()
            && !m_owningWebViewImpl->settingsImpl()->mockGestureTapHighlightsEnabled()) {
            FloatSize rectRoundingRadii(3, 3);
            newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii);
        } else
            addQuadToPath(transformedQuad, newPath);
    }

    FloatRect boundingRect = newPath.boundingRect();
    newPath.translate(-toFloatSize(boundingRect.location()));

    bool pathHasChanged = !(newPath == m_path);
    if (pathHasChanged) {
        m_path = newPath;
        m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size());
    }

    m_contentLayer->layer()->setPosition(boundingRect.location());

    return pathHasChanged;
}

void LinkHighlight::paintContents(WebCanvas* canvas, const WebRect& webClipRect, WebContentLayerClient::PaintingControlSetting paintingControl)
{
    if (!m_node || !m_node->renderer())
        return;

    GraphicsContext::DisabledMode disabledMode = GraphicsContext::NothingDisabled;
    if (paintingControl == WebContentLayerClient::DisplayListConstructionDisabled)
        disabledMode = GraphicsContext::FullyDisabled;

    OwnPtr<GraphicsContext> graphicsContext;
    OwnPtr<DisplayItemList> displayItemList;
    if (RuntimeEnabledFeatures::slimmingPaintEnabled()) {
        displayItemList = DisplayItemList::create();
        graphicsContext = adoptPtr(new GraphicsContext(nullptr, displayItemList.get(), disabledMode));
    } else {
        graphicsContext = adoptPtr(new GraphicsContext(canvas, nullptr, disabledMode));
    }

    IntRect clipRect(IntPoint(webClipRect.x, webClipRect.y), IntSize(webClipRect.width, webClipRect.height));
    {
        DrawingRecorder drawingRecorder(graphicsContext.get(), displayItemClient(), DisplayItem::LinkHighlight, clipRect);

        graphicsContext->clip(clipRect);
        graphicsContext->setFillColor(m_node->renderer()->style()->tapHighlightColor());
        graphicsContext->fillPath(m_path);
    }

    if (RuntimeEnabledFeatures::slimmingPaintEnabled()) {
        GraphicsContext canvasGraphicsContext(canvas, nullptr, disabledMode);
        displayItemList->replay(&canvasGraphicsContext);
    }
}

void LinkHighlight::startHighlightAnimationIfNeeded()
{
    if (m_isAnimating)
        return;

    m_isAnimating = true;
    const float startOpacity = 1;
    // FIXME: Should duration be configurable?
    const float fadeDuration = 0.1f;
    const float minPreFadeDuration = 0.1f;

    m_contentLayer->layer()->setOpacity(startOpacity);

    WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport();

    OwnPtr<WebFloatAnimationCurve> curve = adoptPtr(compositorSupport->createFloatAnimationCurve());

    curve->add(WebFloatKeyframe(0, startOpacity));
    // Make sure we have displayed for at least minPreFadeDuration before starting to fade out.
    float extraDurationRequired = std::max(0.f, minPreFadeDuration - static_cast<float>(monotonicallyIncreasingTime() - m_startTime));
    if (extraDurationRequired)
        curve->add(WebFloatKeyframe(extraDurationRequired, startOpacity));
    // For layout tests we don't fade out.
    curve->add(WebFloatKeyframe(fadeDuration + extraDurationRequired, layoutTestMode() ? startOpacity : 0));

    OwnPtr<WebCompositorAnimation> animation = adoptPtr(compositorSupport->createAnimation(*curve, WebCompositorAnimation::TargetPropertyOpacity));

    m_contentLayer->layer()->setDrawsContent(true);
    m_contentLayer->layer()->addAnimation(animation.leakPtr());

    invalidate();
    m_owningWebViewImpl->scheduleAnimation();
}

void LinkHighlight::clearGraphicsLayerLinkHighlightPointer()
{
    if (m_currentGraphicsLayer) {
        m_currentGraphicsLayer->removeLinkHighlight(this);
        m_currentGraphicsLayer = 0;
    }
}

void LinkHighlight::notifyAnimationStarted(double, int)
{
}

void LinkHighlight::notifyAnimationFinished(double, int)
{
    // Since WebViewImpl may hang on to us for a while, make sure we
    // release resources as soon as possible.
    clearGraphicsLayerLinkHighlightPointer();
    releaseResources();
}

void LinkHighlight::updateGeometry()
{
    // To avoid unnecessary updates (e.g. other entities have requested animations from our WebViewImpl),
    // only proceed if we actually requested an update.
    if (!m_geometryNeedsUpdate)
        return;

    m_geometryNeedsUpdate = false;

    bool hasRenderer = m_node && m_node->renderer();
    const LayoutLayerModelObject* paintInvalidationContainer = hasRenderer ? m_node->renderer()->containerForPaintInvalidation() : 0;
    if (paintInvalidationContainer)
        attachLinkHighlightToCompositingLayer(paintInvalidationContainer);
    if (paintInvalidationContainer && computeHighlightLayerPathAndPosition(paintInvalidationContainer)) {
        // We only need to invalidate the layer if the highlight size has changed, otherwise
        // we can just re-position the layer without needing to repaint.
        m_contentLayer->layer()->invalidate();

        if (m_currentGraphicsLayer)
            m_currentGraphicsLayer->addRepaintRect(FloatRect(layer()->position().x, layer()->position().y, layer()->bounds().width, layer()->bounds().height));
    } else if (!hasRenderer) {
        clearGraphicsLayerLinkHighlightPointer();
        releaseResources();
    }
}

void LinkHighlight::clearCurrentGraphicsLayer()
{
    m_currentGraphicsLayer = 0;
    m_geometryNeedsUpdate = true;
}

void LinkHighlight::invalidate()
{
    // Make sure we update geometry on the next callback from WebViewImpl::layout().
    m_geometryNeedsUpdate = true;
}

WebLayer* LinkHighlight::layer()
{
    return clipLayer();
}

} // namespace blink
<commit_msg>Restore null checks to fix LinkHighlight crashes<commit_after>/*
 * Copyright (C) 2012 Google Inc. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS CONTRIBUTORS 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 "web/LinkHighlight.h"

#include "SkMatrix44.h"
#include "core/dom/Node.h"
#include "core/dom/NodeRenderingTraversal.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/layout/Layer.h"
#include "core/layout/LayoutLayerModelObject.h"
#include "core/layout/LayoutObject.h"
#include "core/layout/compositing/CompositedLayerMapping.h"
#include "core/layout/style/ShadowData.h"
#include "core/rendering/RenderView.h"
#include "platform/graphics/Color.h"
#include "platform/graphics/paint/DisplayItemList.h"
#include "platform/graphics/paint/DrawingRecorder.h"
#include "public/platform/Platform.h"
#include "public/platform/WebCompositorAnimationCurve.h"
#include "public/platform/WebCompositorSupport.h"
#include "public/platform/WebFloatAnimationCurve.h"
#include "public/platform/WebFloatPoint.h"
#include "public/platform/WebRect.h"
#include "public/platform/WebSize.h"
#include "public/web/WebKit.h"
#include "web/WebLocalFrameImpl.h"
#include "web/WebSettingsImpl.h"
#include "web/WebViewImpl.h"
#include "wtf/CurrentTime.h"

namespace blink {

class WebViewImpl;

PassOwnPtr<LinkHighlight> LinkHighlight::create(Node* node, WebViewImpl* owningWebViewImpl)
{
    return adoptPtr(new LinkHighlight(node, owningWebViewImpl));
}

LinkHighlight::LinkHighlight(Node* node, WebViewImpl* owningWebViewImpl)
    : m_node(node)
    , m_owningWebViewImpl(owningWebViewImpl)
    , m_currentGraphicsLayer(0)
    , m_geometryNeedsUpdate(false)
    , m_isAnimating(false)
    , m_startTime(monotonicallyIncreasingTime())
{
    ASSERT(m_node);
    ASSERT(owningWebViewImpl);
    WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport();
    m_contentLayer = adoptPtr(compositorSupport->createContentLayer(this));
    m_clipLayer = adoptPtr(compositorSupport->createLayer());
    m_clipLayer->setTransformOrigin(WebFloatPoint3D());
    m_clipLayer->addChild(m_contentLayer->layer());
    m_contentLayer->layer()->setAnimationDelegate(this);
    m_contentLayer->layer()->setDrawsContent(true);
    m_contentLayer->layer()->setOpacity(1);
    m_geometryNeedsUpdate = true;
    updateGeometry();
}

LinkHighlight::~LinkHighlight()
{
    clearGraphicsLayerLinkHighlightPointer();
    releaseResources();
}

WebContentLayer* LinkHighlight::contentLayer()
{
    return m_contentLayer.get();
}

WebLayer* LinkHighlight::clipLayer()
{
    return m_clipLayer.get();
}

void LinkHighlight::releaseResources()
{
    m_node.clear();
}

void LinkHighlight::attachLinkHighlightToCompositingLayer(const LayoutLayerModelObject* paintInvalidationContainer)
{
    GraphicsLayer* newGraphicsLayer = paintInvalidationContainer->layer()->graphicsLayerBacking();
    // FIXME: There should always be a GraphicsLayer. See crbug.com/431961.
    if (newGraphicsLayer && !newGraphicsLayer->drawsContent())
        newGraphicsLayer = paintInvalidationContainer->layer()->graphicsLayerBackingForScrolling();
    if (!newGraphicsLayer)
        return;

    m_clipLayer->setTransform(SkMatrix44(SkMatrix44::kIdentity_Constructor));

    if (m_currentGraphicsLayer != newGraphicsLayer) {
        if (m_currentGraphicsLayer)
            clearGraphicsLayerLinkHighlightPointer();

        m_currentGraphicsLayer = newGraphicsLayer;
        m_currentGraphicsLayer->addLinkHighlight(this);
    }
}

static void convertTargetSpaceQuadToCompositedLayer(const FloatQuad& targetSpaceQuad, LayoutObject* targetRenderer, const LayoutLayerModelObject* paintInvalidationContainer, FloatQuad& compositedSpaceQuad)
{
    ASSERT(targetRenderer);
    ASSERT(paintInvalidationContainer);
    for (unsigned i = 0; i < 4; ++i) {
        IntPoint point;
        switch (i) {
        case 0: point = roundedIntPoint(targetSpaceQuad.p1()); break;
        case 1: point = roundedIntPoint(targetSpaceQuad.p2()); break;
        case 2: point = roundedIntPoint(targetSpaceQuad.p3()); break;
        case 3: point = roundedIntPoint(targetSpaceQuad.p4()); break;
        }

        // FIXME: this does not need to be absolute, just in the paint invalidation container's space.
        point = targetRenderer->frame()->view()->contentsToWindow(point);
        point = paintInvalidationContainer->frame()->view()->windowToContents(point);
        FloatPoint floatPoint = paintInvalidationContainer->absoluteToLocal(point, UseTransforms);
        Layer::mapPointToPaintBackingCoordinates(paintInvalidationContainer, floatPoint);

        switch (i) {
        case 0: compositedSpaceQuad.setP1(floatPoint); break;
        case 1: compositedSpaceQuad.setP2(floatPoint); break;
        case 2: compositedSpaceQuad.setP3(floatPoint); break;
        case 3: compositedSpaceQuad.setP4(floatPoint); break;
        }
    }
}

static void addQuadToPath(const FloatQuad& quad, Path& path)
{
    // FIXME: Make this create rounded quad-paths, just like the axis-aligned case.
    path.moveTo(quad.p1());
    path.addLineTo(quad.p2());
    path.addLineTo(quad.p3());
    path.addLineTo(quad.p4());
    path.closeSubpath();
}

void LinkHighlight::computeQuads(const Node& node, Vector<FloatQuad>& outQuads) const
{
    if (!node.renderer())
        return;

    LayoutObject* renderer = node.renderer();

    // For inline elements, absoluteQuads will return a line box based on the line-height
    // and font metrics, which is technically incorrect as replaced elements like images
    // should use their intristic height and expand the linebox  as needed. To get an
    // appropriately sized highlight we descend into the children and have them add their
    // boxes.
    if (renderer->isRenderInline()) {
        for (Node* child = NodeRenderingTraversal::firstChild(node); child; child = NodeRenderingTraversal::nextSibling(*child))
            computeQuads(*child, outQuads);
    } else {
        // FIXME: this does not need to be absolute, just in the paint invalidation container's space.
        renderer->absoluteQuads(outQuads);
    }
}

bool LinkHighlight::computeHighlightLayerPathAndPosition(const LayoutLayerModelObject* paintInvalidationContainer)
{
    if (!m_node || !m_node->renderer() || !m_currentGraphicsLayer)
        return false;
    ASSERT(paintInvalidationContainer);

    // FIXME: This is defensive code to avoid crashes such as those described in
    // crbug.com/440887. This should be cleaned up once we fix the root cause of
    // of the paint invalidation container not being composited.
    if (!paintInvalidationContainer->layer()->compositedLayerMapping() && !paintInvalidationContainer->layer()->groupedMapping())
        return false;

    // Get quads for node in absolute coordinates.
    Vector<FloatQuad> quads;
    computeQuads(*m_node, quads);
    ASSERT(quads.size());
    Path newPath;

    FloatPoint positionAdjustForCompositedScrolling = IntPoint(m_currentGraphicsLayer->offsetFromRenderer());

    for (size_t quadIndex = 0; quadIndex < quads.size(); ++quadIndex) {
        FloatQuad absoluteQuad = quads[quadIndex];

        // FIXME: this hack should not be necessary. It's a consequence of the fact that composited layers for scrolling are represented
        // differently in Blink than other composited layers.
        if (paintInvalidationContainer->layer()->needsCompositedScrolling() && m_node->renderer() != paintInvalidationContainer)
            absoluteQuad.move(-positionAdjustForCompositedScrolling.x(), -positionAdjustForCompositedScrolling.y());

        // Transform node quads in target absolute coords to local coordinates in the compositor layer.
        FloatQuad transformedQuad;
        convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->renderer(), paintInvalidationContainer, transformedQuad);

        // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that
        // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage
        // links: these should ideally be merged into a single rect before creating the path, but that's
        // another CL.
        if (quads.size() == 1 && transformedQuad.isRectilinear()
            && !m_owningWebViewImpl->settingsImpl()->mockGestureTapHighlightsEnabled()) {
            FloatSize rectRoundingRadii(3, 3);
            newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii);
        } else
            addQuadToPath(transformedQuad, newPath);
    }

    FloatRect boundingRect = newPath.boundingRect();
    newPath.translate(-toFloatSize(boundingRect.location()));

    bool pathHasChanged = !(newPath == m_path);
    if (pathHasChanged) {
        m_path = newPath;
        m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size());
    }

    m_contentLayer->layer()->setPosition(boundingRect.location());

    return pathHasChanged;
}

void LinkHighlight::paintContents(WebCanvas* canvas, const WebRect& webClipRect, WebContentLayerClient::PaintingControlSetting paintingControl)
{
    if (!m_node || !m_node->renderer())
        return;

    GraphicsContext::DisabledMode disabledMode = GraphicsContext::NothingDisabled;
    if (paintingControl == WebContentLayerClient::DisplayListConstructionDisabled)
        disabledMode = GraphicsContext::FullyDisabled;

    OwnPtr<GraphicsContext> graphicsContext;
    OwnPtr<DisplayItemList> displayItemList;
    if (RuntimeEnabledFeatures::slimmingPaintEnabled()) {
        displayItemList = DisplayItemList::create();
        graphicsContext = adoptPtr(new GraphicsContext(nullptr, displayItemList.get(), disabledMode));
    } else {
        graphicsContext = adoptPtr(new GraphicsContext(canvas, nullptr, disabledMode));
    }

    IntRect clipRect(IntPoint(webClipRect.x, webClipRect.y), IntSize(webClipRect.width, webClipRect.height));
    {
        DrawingRecorder drawingRecorder(graphicsContext.get(), displayItemClient(), DisplayItem::LinkHighlight, clipRect);

        graphicsContext->clip(clipRect);
        graphicsContext->setFillColor(m_node->renderer()->style()->tapHighlightColor());
        graphicsContext->fillPath(m_path);
    }

    if (RuntimeEnabledFeatures::slimmingPaintEnabled()) {
        GraphicsContext canvasGraphicsContext(canvas, nullptr, disabledMode);
        displayItemList->replay(&canvasGraphicsContext);
    }
}

void LinkHighlight::startHighlightAnimationIfNeeded()
{
    if (m_isAnimating)
        return;

    m_isAnimating = true;
    const float startOpacity = 1;
    // FIXME: Should duration be configurable?
    const float fadeDuration = 0.1f;
    const float minPreFadeDuration = 0.1f;

    m_contentLayer->layer()->setOpacity(startOpacity);

    WebCompositorSupport* compositorSupport = Platform::current()->compositorSupport();

    OwnPtr<WebFloatAnimationCurve> curve = adoptPtr(compositorSupport->createFloatAnimationCurve());

    curve->add(WebFloatKeyframe(0, startOpacity));
    // Make sure we have displayed for at least minPreFadeDuration before starting to fade out.
    float extraDurationRequired = std::max(0.f, minPreFadeDuration - static_cast<float>(monotonicallyIncreasingTime() - m_startTime));
    if (extraDurationRequired)
        curve->add(WebFloatKeyframe(extraDurationRequired, startOpacity));
    // For layout tests we don't fade out.
    curve->add(WebFloatKeyframe(fadeDuration + extraDurationRequired, layoutTestMode() ? startOpacity : 0));

    OwnPtr<WebCompositorAnimation> animation = adoptPtr(compositorSupport->createAnimation(*curve, WebCompositorAnimation::TargetPropertyOpacity));

    m_contentLayer->layer()->setDrawsContent(true);
    m_contentLayer->layer()->addAnimation(animation.leakPtr());

    invalidate();
    m_owningWebViewImpl->scheduleAnimation();
}

void LinkHighlight::clearGraphicsLayerLinkHighlightPointer()
{
    if (m_currentGraphicsLayer) {
        m_currentGraphicsLayer->removeLinkHighlight(this);
        m_currentGraphicsLayer = 0;
    }
}

void LinkHighlight::notifyAnimationStarted(double, int)
{
}

void LinkHighlight::notifyAnimationFinished(double, int)
{
    // Since WebViewImpl may hang on to us for a while, make sure we
    // release resources as soon as possible.
    clearGraphicsLayerLinkHighlightPointer();
    releaseResources();
}

void LinkHighlight::updateGeometry()
{
    // To avoid unnecessary updates (e.g. other entities have requested animations from our WebViewImpl),
    // only proceed if we actually requested an update.
    if (!m_geometryNeedsUpdate)
        return;

    m_geometryNeedsUpdate = false;

    bool hasRenderer = m_node && m_node->renderer();
    const LayoutLayerModelObject* paintInvalidationContainer = hasRenderer ? m_node->renderer()->containerForPaintInvalidation() : 0;
    if (paintInvalidationContainer)
        attachLinkHighlightToCompositingLayer(paintInvalidationContainer);
    if (paintInvalidationContainer && computeHighlightLayerPathAndPosition(paintInvalidationContainer)) {
        // We only need to invalidate the layer if the highlight size has changed, otherwise
        // we can just re-position the layer without needing to repaint.
        m_contentLayer->layer()->invalidate();

        if (m_currentGraphicsLayer)
            m_currentGraphicsLayer->addRepaintRect(FloatRect(layer()->position().x, layer()->position().y, layer()->bounds().width, layer()->bounds().height));
    } else if (!hasRenderer) {
        clearGraphicsLayerLinkHighlightPointer();
        releaseResources();
    }
}

void LinkHighlight::clearCurrentGraphicsLayer()
{
    m_currentGraphicsLayer = 0;
    m_geometryNeedsUpdate = true;
}

void LinkHighlight::invalidate()
{
    // Make sure we update geometry on the next callback from WebViewImpl::layout().
    m_geometryNeedsUpdate = true;
}

WebLayer* LinkHighlight::layer()
{
    return clipLayer();
}

} // namespace blink
<|endoftext|>


<commit_before>#ifdef ANDROID

#include "EventLoop.h"
#include "Logger.h"
#include "Scenes/SceneManager.h"
#include "Graphics/GraphicsManager.h"
#include <unistd.h>
#include "Input/InputManager.h"
#include "StarEngine.h"
#include "Sound/AudioManager.h"

namespace star {
	EventLoop * EventLoop::mEventLoop = nullptr;

	EventLoop::~EventLoop()
	{
		delete mTimeManager;
	}

	EventLoop::EventLoop() :
			mMainGameInitialized(false),
			mQuit(false),
			mEnabled(false),
			mMainGame(nullptr),
			mTimeManager(new TimeManager()),
			mApplicationPtr(nullptr)
	{

	}

	void EventLoop::Initialize(android_app * pApplication, BaseGame * pBaseGame)
	{
		mMainGame = pBaseGame;
		mApplicationPtr = pApplication;
		mApplicationPtr->onAppCmd = activityCallback;
		mApplicationPtr->userData = this;
		mContext.mTimeManager = mTimeManager;
		//mApplicationPtr->onAppCmd = activityCallback;
		mApplicationPtr->onInputEvent = inputCallback;
		StarEngine::GetInstance()->SetAndroidApp(mApplicationPtr);
	}

	EventLoop * EventLoop::GetInstance()
	{
		if (mEventLoop == nullptr)
		{
			mEventLoop = new EventLoop();
		}
		return mEventLoop;
	}

	void EventLoop::Run()
	{
		int32 lResult;
		int32 lEvents;
		android_poll_source* lSource;

		app_dummy();

		Logger::GetInstance()->Log(LogLevel::Info,
			_T("Starting EventLoop"), STARENGINE_LOG_TAG);

		while (true)
		{
			mTimeManager->StartMonitoring();
			while ((lResult = ALooper_pollAll(mEnabled ? 0 : -1, NULL, &lEvents,
					(void**) &lSource)) >= 0)
			{
				if (lSource != NULL)
				{
					lSource->process(mApplicationPtr, lSource);
				}

				if (mApplicationPtr->destroyRequested)
				{
					Logger::GetInstance()->Log(LogLevel::Info,
						_T("Exiting Event"), STARENGINE_LOG_TAG);
					mQuit = true;
					mEnabled = false;
					mTimeManager->StopMonitoring();
					return;
				}
			}

			if (mEnabled && !mQuit)
			{
				GraphicsManager::GetInstance()->SetHasWindowChanged(false);
				mMainGame->Update(mContext);
				mMainGame->Draw();
			}
			usleep(100);
			mTimeManager->StopMonitoring();
		}
	}

	void EventLoop::End()
	{
		//[COMMENT] This "delete this" looks very unsafe...
		Logger::GetInstance()->Log(LogLevel::Info, _T("Ending App"), STARENGINE_LOG_TAG);
		mMainGame->End();
		delete InputManager::GetInstance();
		delete this;
		ANativeActivity_finish(mApplicationPtr->activity);
	}

	void EventLoop::activityCallback(android_app* pApplication, int32_t pCommand)
	{
		EventLoop& lEventLoop =
				*reinterpret_cast<EventLoop*>(pApplication->userData);

		switch (pCommand)
		{
		//Gets called first on fresh app launch
		case APP_CMD_INIT_WINDOW:
			if (pApplication->window != nullptr)
			{
				Logger::GetInstance()->Log(LogLevel::Info,
						_T("Eventloop : APP CMD INIT WINDOW"), STARENGINE_LOG_TAG);
				GraphicsManager::GetInstance()->Initialize(pApplication);
				lEventLoop.mQuit = false;
			}
			else
			{
				lEventLoop.mQuit = true;
				ANativeActivity_finish(pApplication->activity);
			}
			GraphicsManager::GetInstance()->SetHasWindowChanged(true);
			break;

			//Gets called second after the window init
		case APP_CMD_GAINED_FOCUS:
		{
			if (!lEventLoop.mMainGameInitialized)
			{
				lEventLoop.mMainGame->Initialize(0, 0);
			}
			lEventLoop.mMainGameInitialized = true;
			lEventLoop.mEnabled = true;
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP CMD GAINED FOXUS, Initited MainGame"),
					STARENGINE_LOG_TAG);
			TextureManager::GetInstance()->ReloadAllTextures();
			SceneManager::GetInstance()->processActivityEvent(pCommand,
					pApplication);
		}
			break;

		case APP_CMD_LOST_FOCUS:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_LOST_FOCUS"),
					STARENGINE_LOG_TAG);
			AudioManager::GetInstance()->PauseAllSounds();
			break;

			//Gets called first when rotating the screen
			//After this the Save State gets called in the scene manager
		case APP_CMD_PAUSE:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP CMD PAUSE"),
					STARENGINE_LOG_TAG);
			lEventLoop.mEnabled = false;
			AudioManager::GetInstance()->PauseAllSounds();
			break;

		case APP_CMD_RESUME:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_RESUME"),
					STARENGINE_LOG_TAG);
			break;

			//Gets called after the pause command
		case APP_CMD_STOP:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_STOP"), STARENGINE_LOG_TAG);
			SceneManager::GetInstance()->processActivityEvent(pCommand,
					pApplication);
			break;

		case APP_CMD_START:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_START"), STARENGINE_LOG_TAG);
			break;

		case APP_CMD_TERM_WINDOW:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_TERM_WINDOW"), STARENGINE_LOG_TAG);
			TextureManager::GetInstance()->EraseAllTextures();
			break;

		case APP_CMD_DESTROY:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_DESTROY"), STARENGINE_LOG_TAG);
			GraphicsManager::GetInstance()->Destroy();

			break;

		case APP_CMD_LOW_MEMORY:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_LOW_MEMORY"), STARENGINE_LOG_TAG);
			break;

		case APP_CMD_CONFIG_CHANGED:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_CONFIG_CHANGED"), STARENGINE_LOG_TAG);
			break;

		default:
			SceneManager::GetInstance()->processActivityEvent(pCommand,
					pApplication);
			break;
		}

	}

	int32 EventLoop::inputCallback(android_app* pApplication, AInputEvent* pEvent)
	{
		return SceneManager::GetInstance()->processInputEvent(pEvent);
	}
}
#endif
<commit_msg>Fixes black images on android on startup<commit_after>#ifdef ANDROID

#include "EventLoop.h"
#include "Logger.h"
#include "Scenes/SceneManager.h"
#include "Graphics/GraphicsManager.h"
#include <unistd.h>
#include "Input/InputManager.h"
#include "StarEngine.h"
#include "Sound/AudioManager.h"

namespace star {
	EventLoop * EventLoop::mEventLoop = nullptr;

	EventLoop::~EventLoop()
	{
		delete mTimeManager;
	}

	EventLoop::EventLoop() :
			mMainGameInitialized(false),
			mQuit(false),
			mEnabled(false),
			mMainGame(nullptr),
			mTimeManager(new TimeManager()),
			mApplicationPtr(nullptr)
	{

	}

	void EventLoop::Initialize(android_app * pApplication, BaseGame * pBaseGame)
	{
		mMainGame = pBaseGame;
		mApplicationPtr = pApplication;
		mApplicationPtr->onAppCmd = activityCallback;
		mApplicationPtr->userData = this;
		mContext.mTimeManager = mTimeManager;
		//mApplicationPtr->onAppCmd = activityCallback;
		mApplicationPtr->onInputEvent = inputCallback;
		StarEngine::GetInstance()->SetAndroidApp(mApplicationPtr);
	}

	EventLoop * EventLoop::GetInstance()
	{
		if (mEventLoop == nullptr)
		{
			mEventLoop = new EventLoop();
		}
		return mEventLoop;
	}

	void EventLoop::Run()
	{
		int32 lResult;
		int32 lEvents;
		android_poll_source* lSource;

		app_dummy();

		Logger::GetInstance()->Log(LogLevel::Info,
			_T("Starting EventLoop"), STARENGINE_LOG_TAG);

		while (true)
		{
			mTimeManager->StartMonitoring();
			while ((lResult = ALooper_pollAll(mEnabled ? 0 : -1, NULL, &lEvents,
					(void**) &lSource)) >= 0)
			{
				if (lSource != NULL)
				{
					lSource->process(mApplicationPtr, lSource);
				}

				if (mApplicationPtr->destroyRequested)
				{
					Logger::GetInstance()->Log(LogLevel::Info,
						_T("Exiting Event"), STARENGINE_LOG_TAG);
					mQuit = true;
					mEnabled = false;
					mTimeManager->StopMonitoring();
					return;
				}
			}

			if (mEnabled && !mQuit)
			{
				GraphicsManager::GetInstance()->SetHasWindowChanged(false);
				mMainGame->Update(mContext);
				mMainGame->Draw();
			}
			usleep(100);
			mTimeManager->StopMonitoring();
		}
	}

	void EventLoop::End()
	{
		//[COMMENT] This "delete this" looks very unsafe...
		Logger::GetInstance()->Log(LogLevel::Info, _T("Ending App"), STARENGINE_LOG_TAG);
		mMainGame->End();
		delete InputManager::GetInstance();
		delete this;
		ANativeActivity_finish(mApplicationPtr->activity);
	}

	void EventLoop::activityCallback(android_app* pApplication, int32_t pCommand)
	{
		EventLoop& lEventLoop =
				*reinterpret_cast<EventLoop*>(pApplication->userData);

		switch (pCommand)
		{
		//Gets called first on fresh app launch
		case APP_CMD_INIT_WINDOW:
			if (pApplication->window != nullptr)
			{
				Logger::GetInstance()->Log(LogLevel::Info,
						_T("Eventloop : APP CMD INIT WINDOW"), STARENGINE_LOG_TAG);
				GraphicsManager::GetInstance()->Initialize(pApplication);
				lEventLoop.mQuit = false;
			}
			else
			{
				lEventLoop.mQuit = true;
				ANativeActivity_finish(pApplication->activity);
			}
			GraphicsManager::GetInstance()->SetHasWindowChanged(true);
			break;

			//Gets called second after the window init
		case APP_CMD_GAINED_FOCUS:
		{
			if (!lEventLoop.mMainGameInitialized)
			{
				lEventLoop.mMainGame->Initialize(0, 0);
			}
			lEventLoop.mMainGameInitialized = true;
			lEventLoop.mEnabled = true;
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP CMD GAINED FOXUS, Initited MainGame"),
					STARENGINE_LOG_TAG);
			//TextureManager::GetInstance()->ReloadAllTextures();
			SceneManager::GetInstance()->processActivityEvent(pCommand,
					pApplication);
		}
			break;

		case APP_CMD_LOST_FOCUS:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_LOST_FOCUS"),
					STARENGINE_LOG_TAG);
			AudioManager::GetInstance()->PauseAllSounds();
			break;

			//Gets called first when rotating the screen
			//After this the Save State gets called in the scene manager
		case APP_CMD_PAUSE:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP CMD PAUSE"),
					STARENGINE_LOG_TAG);
			lEventLoop.mEnabled = false;
			AudioManager::GetInstance()->PauseAllSounds();
			break;

		case APP_CMD_RESUME:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_RESUME"),
					STARENGINE_LOG_TAG);
			break;

			//Gets called after the pause command
		case APP_CMD_STOP:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_STOP"), STARENGINE_LOG_TAG);
			SceneManager::GetInstance()->processActivityEvent(pCommand,
					pApplication);
			break;

		case APP_CMD_START:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_START"), STARENGINE_LOG_TAG);
			break;

		case APP_CMD_TERM_WINDOW:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_TERM_WINDOW"), STARENGINE_LOG_TAG);
			TextureManager::GetInstance()->EraseAllTextures();
			break;

		case APP_CMD_DESTROY:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_DESTROY"), STARENGINE_LOG_TAG);
			GraphicsManager::GetInstance()->Destroy();

			break;

		case APP_CMD_LOW_MEMORY:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_LOW_MEMORY"), STARENGINE_LOG_TAG);
			break;

		case APP_CMD_CONFIG_CHANGED:
			Logger::GetInstance()->Log(LogLevel::Info,
					_T("Eventloop : APP_CMD_CONFIG_CHANGED"), STARENGINE_LOG_TAG);
			break;

		default:
			SceneManager::GetInstance()->processActivityEvent(pCommand,
					pApplication);
			break;
		}

	}

	int32 EventLoop::inputCallback(android_app* pApplication, AInputEvent* pEvent)
	{
		return SceneManager::GetInstance()->processInputEvent(pEvent);
	}
}
#endif
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "util/fixes.h"
#include "dispatcher.h"
#include "combat.h"
#include "poison.h"
#include "condition.h"
#include "damage.h"
#include "d20.h"
#include "float_line.h"
#include "history.h"
#include "gamesystems/objects/objsystem.h"
#include "gamesystems/gamesystems.h"
#include "gamesystems/particlesystems.h"

// Ability Condition Fixes (for buggy abilities, including monster abilities)
class AbilityConditionFixes : public TempleFix {
public:
#define ABFIX(fname) static int fname ## (DispatcherCallbackArgs args);
#define HOOK_ORG(fname) static int (__cdecl* org ##fname)(DispatcherCallbackArgs) = replaceFunction<int(__cdecl)(DispatcherCallbackArgs)>
	static int PoisonedOnBeginRound(DispatcherCallbackArgs args);;
	static int MonsterSplittingHpChange(DispatcherCallbackArgs args);
	static int MonsterOozeSplittingOnDamage(DispatcherCallbackArgs args);
	static int MonsterSubtypeFire(DispatcherCallbackArgs args);
	static int GrappledMoveSpeed(DispatcherCallbackArgs args);
	static int DiplomacySkillSynergy(DispatcherCallbackArgs args);

	void apply() override {

		replaceFunction(0x100CB890, GrappledMoveSpeed); // fixed Grappled when the frog is dead
		{
			SubDispDefNew sdd;
			sdd.dispKey = DK_QUE_Critter_Is_Grappling;
			sdd.dispType = dispTypeD20Query;
			sdd.dispCallback = [](DispatcherCallbackArgs args){
				auto spellId = args.GetCondArg(0);
				SpellPacketBody spPkt(spellId);
				if (!spPkt.caster
					|| objSystem->IsValidHandle(spPkt.caster) && critterSys.IsDeadOrUnconscious(spPkt.caster)){
					conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);
					return 0;
				}

				GET_DISPIO(dispIOTypeQuery, DispIoD20Query);
				dispIo->return_val = 1;
				return 0;
			};

			write(0x102E6158, &sdd, sizeof(sdd));
			write(0x102E616C, &sdd, sizeof(sdd));
			write(0x102E6180, &sdd, sizeof(sdd));
			write(0x102E61A8, &sdd, sizeof(sdd));
			write(0x102E15C0 + offsetof(CondStruct, subDispDefs) + (1) * sizeof(SubDispDefNew), &sdd, sizeof(sdd));

		}

		replaceFunction(0x100FDE00, MonsterSubtypeFire); // fixes 2x damage factor for vulnerability to cold (should be 1.5)

		replaceFunction(0x100EA040, PoisonedOnBeginRound);

		replaceFunction(0x100F7550, MonsterSplittingHpChange);
		replaceFunction(0x100F7490, MonsterOozeSplittingOnDamage);

		replaceFunction(0x100EEE70, DiplomacySkillSynergy); // fixes skill synergy bonuses to diplomacy not stacking

		// fixes animal companion runoff crash (it didn't null the second part of the obj handle stored in args[1,2]
		static void (__cdecl*orgCompanionRunoff)(SubDispNode*, objHndl , objHndl ) = replaceFunction<void(__cdecl)(SubDispNode*, objHndl, objHndl)>(0x100FC3D0, [](SubDispNode* sdn, objHndl owner, objHndl handle){
			orgCompanionRunoff(sdn, owner, handle);
			conds.CondNodeSetArg(sdn->condNode, 2, 0);
		});

		// fixes Opportunist getting an AOO for your own attack...
		HOOK_ORG(OpportuninstBroadcast)(0x100FADF0, [](DispatcherCallbackArgs args)->int
		{
			auto numAvail = args.GetCondArg(0);
			if (numAvail){
				GET_DISPIO(dispIoTypeSendSignal, DispIoD20Signal);
				auto d20a = (D20Actn*)dispIo->data1;
				if (d20a->d20APerformer != args.objHndCaller)
					return orgOpportuninstBroadcast(args);
			}
			return 0;
		});

		// Banshee Charisma Drain (fixes uninitialized values used when saving throw is successful)
		HOOK_ORG(BansheeCharismaDrain)(0x100F67D0, [](DispatcherCallbackArgs args)->int {
			GET_DISPIO(dispIOTypeDamage, DispIoDamage);

			if (dispIo->attackPacket.dispKey < ATTACK_CODE_NATURAL_ATTACK + 1)
				return 0;

			auto attacker = dispIo->attackPacket.attacker;
			auto tgt = dispIo->attackPacket.victim;
			//auto saveRes = damage.SavingThrow(tgt, attacker, 26, SavingThrowType::Fortitude, 0); // looks like by the rules there shouldn't even be a save? (Mon. Man. Ghost entry)

			auto curHp = objects.StatLevelGet(attacker, stat_hp_current);
			auto maxHp = objects.StatLevelGet(attacker, stat_hp_max);
			int tempHpGain = 5;
			auto chaDrainDice = 1;
			if (dispIo->attackPacket.flags & D20CAF_CRITICAL){
				tempHpGain = 10;
				chaDrainDice = 2;
			}
			auto chaDrainAmt = Dice(chaDrainDice, 4, 0).Roll();

			auto hpDam = maxHp - curHp;
			// add Temporary HPs
			if (tempHpGain >= hpDam){
				conds.AddTo(attacker, "Temporary_Hit_Points", {0, 14400, tempHpGain - hpDam});
				tempHpGain -= tempHpGain - hpDam;
			}
			// heal normal damage if applicable
			if (tempHpGain > 0 ){
				damage.Heal(attacker, tgt, Dice(0, 0, tempHpGain), D20A_NONE);
			}
			conds.AddTo(tgt, "Temp_Ability_Loss", {stat_charisma, chaDrainAmt});

			return 0;
		});
	}
} abilityConditionFixes;

int AbilityConditionFixes::PoisonedOnBeginRound(DispatcherCallbackArgs args){
	GET_DISPIO(dispIoTypeSendSignal, DispIoD20Signal);

	// decrement duration
	auto dur = args.GetCondArg(1);
	auto durRem = dur - (int)dispIo->data1;
	if (durRem >= 0){
		args.SetCondArg(1, durRem);
		return 0;
	}

	auto poisonId = args.GetCondArg(0);
	auto &poisonSpecs = temple::GetRef<PoisonSpec[36]>(0x1028C080);
	auto pspec = poisonSpecs[poisonId];

	// make saving throw
	auto dc = pspec.dc;
	if (dc <= 0){
		dc = args.GetCondArg(2);
	}
	if (dc < 0 || dc > 100) // failsafe
		dc = 15;

	if (pspec.delayedEffect == -10 || pspec.delayedEffect == -9) {
		conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);
		return 0;
	}

	// success - remove condition
	if (damage.SavingThrow(args.objHndCaller, objHndl::null, dc, SavingThrowType::Fortitude, D20STF_POISON)){
		conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);
		return 0;
	}

	// failure

	// check delay poison
	if (d20Sys.d20QueryWithData(args.objHndCaller, DK_QUE_Critter_Has_Condition, conds.GetByName("sp-Delay Poison"), 0)){
		floatSys.FloatSpellLine(args.objHndCaller, 20033, FloatLineColor::White);
		conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);
		return 0;
	}

	floatSys.FloatCombatLine(args.objHndCaller, 56);
	histSys.CreateRollHistoryLineFromMesfile(21, args.objHndCaller, objHndl::null); // "X takes poison damage!"
	floatSys.FloatCombatLine(args.objHndCaller, 96);

	auto rollRes = Dice(pspec.delayedDice.count, pspec.delayedDice.sides, pspec.delayedDice.sides).Roll();
	conds.AddTo(args.objHndCaller, "Temp_Ability_Loss", {pspec.delayedEffect + (pspec.delayedEffect < 0 ? 6:0), rollRes});

	if (pspec.delayedSecondEffect != -10){
		rollRes = Dice(pspec.delayedSecDice.count, pspec.delayedSecDice.sides, pspec.delayedSecDice.sides).Roll();
		conds.AddTo(args.objHndCaller, "Temp_Ability_Loss", { pspec.delayedSecondEffect + (pspec.delayedSecondEffect < 0 ? 6 : 0), rollRes });

	}

	conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);
	return 0;
}

int AbilityConditionFixes::MonsterSplittingHpChange(DispatcherCallbackArgs args){
	auto protoId = objSystem->GetProtoId(args.objHndCaller);
	auto obj = objSystem->GetObject(args.objHndCaller);
	auto loc = obj->GetLocationFull();
	auto hpCur = objects.StatLevelGet(args.objHndCaller, stat_hp_current);
	auto baseHp = obj->GetInt32(obj_f_hp_pts);
	auto hpDam = obj->GetInt32(obj_f_hp_damage);

	auto hpMod = (hpCur + hpDam) - baseHp;

	if (hpCur <= 10)
		return 0;

	obj->SetInt32(obj_f_hp_pts, baseHp / 2);
	obj->SetInt32(obj_f_hp_damage, (hpDam + hpMod) / 2);

	auto protoHandle = objSystem->GetProtoHandle(protoId);
	auto newMonster = objSystem->CreateObject(protoHandle, loc.location);
	auto newMonObj = objSystem->GetObject(newMonster);
	newMonObj->SetInt32(obj_f_hp_pts, baseHp / 2);
	newMonObj->SetInt32(obj_f_hp_damage, (hpDam + hpMod) / 2);
	newMonObj->SetInt32(obj_f_critter_flags, newMonObj->GetInt32(obj_f_critter_flags) | OCF_EXPERIENCE_AWARDED);

	gameSystems->GetParticleSys().CreateAtObj("hit-Acid-medium", args.objHndCaller);
	conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);

	return 0;
}

int AbilityConditionFixes::MonsterOozeSplittingOnDamage(DispatcherCallbackArgs args){

	auto curHp = objects.StatLevelGet(args.objHndCaller, stat_hp_current);
	if (curHp <= 10)
		return 0;

	GET_DISPIO(dispIOTypeDamage, DispIoDamage);
	auto isSplitting = false;
	auto protoId = objSystem->GetProtoId(args.objHndCaller);
	if (protoId == 14142 && dispIo->damage.GetOverallDamageByType(DamageType::Electricity) > 0){
		isSplitting = true;
		dispIo->damage.AddModFactor(0.0f, DamageType::Electricity, 132);
	}

	if (dispIo->damage.GetOverallDamageByType(DamageType::Slashing) > 0) {
		isSplitting = true;
		dispIo->damage.AddModFactor(0.0f, DamageType::Slashing, 132);
	}

	if (dispIo->damage.GetOverallDamageByType(DamageType::Piercing) > 0) {
		isSplitting = true;
		dispIo->damage.AddModFactor(0.0f, DamageType::Piercing, 132);
	}

	if (dispIo->damage.GetOverallDamageByType(DamageType::PiercingAndSlashing) > 0) {
		isSplitting = true;
		dispIo->damage.AddModFactor(0.0f, DamageType::PiercingAndSlashing, 132);
	}

	if (isSplitting){
		conds.AddTo(args.objHndCaller, "Monster Splitting", {});
	}

	return 0;
}

int AbilityConditionFixes::MonsterSubtypeFire(DispatcherCallbackArgs args){

	GET_DISPIO(dispIOTypeDamage, DispIoDamage);
	auto immuneDamType = (DamageType)args.GetData1();
	auto vulnerDamType = (DamageType)args.GetData2();

	if (!(dispIo->attackPacket.flags & D20CAF_SAVE_SUCCESSFUL))
		dispIo->damage.AddModFactor(1.5, vulnerDamType, 136); // Vulnerable
	dispIo->damage.AddModFactor(0.0, immuneDamType, 104); // Invulnerable
	return 0;
}

int AbilityConditionFixes::GrappledMoveSpeed(DispatcherCallbackArgs args){
	auto spellId = args.GetCondArg(0);
	SpellPacketBody spPkt(spellId);
	if (!spPkt.caster
		|| critterSys.IsDeadOrUnconscious(spPkt.caster))
		return 0;

	GET_DISPIO(dispIOTypeMoveSpeed, DispIoMoveSpeed);
	dispIo->bonlist->SetOverallCap(1, args.GetData1(), 0, args.GetData2());
	dispIo->bonlist->SetOverallCap(2, args.GetData1(), 0, args.GetData2());

	return 0;
}

int AbilityConditionFixes::DiplomacySkillSynergy(DispatcherCallbackArgs args){
	GET_DISPIO(dispIoTypeObjBonus, DispIoObjBonus);

	if (critterSys.SkillBaseGet(args.objHndCaller, SkillEnum::skill_bluff) >= 5)
		dispIo->bonOut->AddBonus(2, 0, 140);
	if (critterSys.SkillBaseGet(args.objHndCaller, SkillEnum::skill_sense_motive) >= 5)
		dispIo->bonOut->AddBonus(2, 0, 302);
	return 0;
}
<commit_msg>Fixed bugs with Boots of Speed<commit_after>#include "stdafx.h"
#include "util/fixes.h"
#include "dispatcher.h"
#include "combat.h"
#include "poison.h"
#include "condition.h"
#include "damage.h"
#include "d20.h"
#include "float_line.h"
#include "history.h"
#include "gamesystems/objects/objsystem.h"
#include "gamesystems/gamesystems.h"
#include "gamesystems/particlesystems.h"

// Ability Condition Fixes (for buggy abilities, including monster abilities)
class AbilityConditionFixes : public TempleFix {
public:
#define ABFIX(fname) static int fname ## (DispatcherCallbackArgs args);
#define HOOK_ORG(fname) static int (__cdecl* org ##fname)(DispatcherCallbackArgs) = replaceFunction<int(__cdecl)(DispatcherCallbackArgs)>
	static int PoisonedOnBeginRound(DispatcherCallbackArgs args);;
	static int MonsterSplittingHpChange(DispatcherCallbackArgs args);
	static int MonsterOozeSplittingOnDamage(DispatcherCallbackArgs args);
	static int MonsterSubtypeFire(DispatcherCallbackArgs args);
	static int GrappledMoveSpeed(DispatcherCallbackArgs args);
	static int DiplomacySkillSynergy(DispatcherCallbackArgs args);

	static int BootsOfSpeedNewday(DispatcherCallbackArgs args);
	static int BootsOfSpeedBeginRound(DispatcherCallbackArgs args);

	void apply() override {

		replaceFunction(0x100CB890, GrappledMoveSpeed); // fixed Grappled when the frog is dead
		{
			SubDispDefNew sdd;
			sdd.dispKey = DK_QUE_Critter_Is_Grappling;
			sdd.dispType = dispTypeD20Query;
			sdd.dispCallback = [](DispatcherCallbackArgs args){
				auto spellId = args.GetCondArg(0);
				SpellPacketBody spPkt(spellId);
				if (!spPkt.caster
					|| objSystem->IsValidHandle(spPkt.caster) && critterSys.IsDeadOrUnconscious(spPkt.caster)){
					conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);
					return 0;
				}

				GET_DISPIO(dispIOTypeQuery, DispIoD20Query);
				dispIo->return_val = 1;
				return 0;
			};

			write(0x102E6158, &sdd, sizeof(sdd));
			write(0x102E616C, &sdd, sizeof(sdd));
			write(0x102E6180, &sdd, sizeof(sdd));
			write(0x102E61A8, &sdd, sizeof(sdd));
			write(0x102E15C0 + offsetof(CondStruct, subDispDefs) + (1) * sizeof(SubDispDefNew), &sdd, sizeof(sdd));

		}

		replaceFunction(0x10101EB0, BootsOfSpeedNewday);
		replaceFunction(0x101020A0, BootsOfSpeedBeginRound);

		replaceFunction(0x100FDE00, MonsterSubtypeFire); // fixes 2x damage factor for vulnerability to cold (should be 1.5)

		replaceFunction(0x100EA040, PoisonedOnBeginRound);

		replaceFunction(0x100F7550, MonsterSplittingHpChange);
		replaceFunction(0x100F7490, MonsterOozeSplittingOnDamage);

		replaceFunction(0x100EEE70, DiplomacySkillSynergy); // fixes skill synergy bonuses to diplomacy not stacking

		// fixes animal companion runoff crash (it didn't null the second part of the obj handle stored in args[1,2]
		static void (__cdecl*orgCompanionRunoff)(SubDispNode*, objHndl , objHndl ) = replaceFunction<void(__cdecl)(SubDispNode*, objHndl, objHndl)>(0x100FC3D0, [](SubDispNode* sdn, objHndl owner, objHndl handle){
			orgCompanionRunoff(sdn, owner, handle);
			conds.CondNodeSetArg(sdn->condNode, 2, 0);
		});

		// fixes Opportunist getting an AOO for your own attack...
		HOOK_ORG(OpportuninstBroadcast)(0x100FADF0, [](DispatcherCallbackArgs args)->int
		{
			auto numAvail = args.GetCondArg(0);
			if (numAvail){
				GET_DISPIO(dispIoTypeSendSignal, DispIoD20Signal);
				auto d20a = (D20Actn*)dispIo->data1;
				if (d20a->d20APerformer != args.objHndCaller)
					return orgOpportuninstBroadcast(args);
			}
			return 0;
		});

		// Banshee Charisma Drain (fixes uninitialized values used when saving throw is successful)
		HOOK_ORG(BansheeCharismaDrain)(0x100F67D0, [](DispatcherCallbackArgs args)->int {
			GET_DISPIO(dispIOTypeDamage, DispIoDamage);

			if (dispIo->attackPacket.dispKey < ATTACK_CODE_NATURAL_ATTACK + 1)
				return 0;

			auto attacker = dispIo->attackPacket.attacker;
			auto tgt = dispIo->attackPacket.victim;
			//auto saveRes = damage.SavingThrow(tgt, attacker, 26, SavingThrowType::Fortitude, 0); // looks like by the rules there shouldn't even be a save? (Mon. Man. Ghost entry)

			auto curHp = objects.StatLevelGet(attacker, stat_hp_current);
			auto maxHp = objects.StatLevelGet(attacker, stat_hp_max);
			int tempHpGain = 5;
			auto chaDrainDice = 1;
			if (dispIo->attackPacket.flags & D20CAF_CRITICAL){
				tempHpGain = 10;
				chaDrainDice = 2;
			}
			auto chaDrainAmt = Dice(chaDrainDice, 4, 0).Roll();

			auto hpDam = maxHp - curHp;
			// add Temporary HPs
			if (tempHpGain >= hpDam){
				conds.AddTo(attacker, "Temporary_Hit_Points", {0, 14400, tempHpGain - hpDam});
				tempHpGain -= tempHpGain - hpDam;
			}
			// heal normal damage if applicable
			if (tempHpGain > 0 ){
				damage.Heal(attacker, tgt, Dice(0, 0, tempHpGain), D20A_NONE);
			}
			conds.AddTo(tgt, "Temp_Ability_Loss", {stat_charisma, chaDrainAmt});

			return 0;
		});
	}
} abilityConditionFixes;

int AbilityConditionFixes::PoisonedOnBeginRound(DispatcherCallbackArgs args){
	GET_DISPIO(dispIoTypeSendSignal, DispIoD20Signal);

	// decrement duration
	auto dur = args.GetCondArg(1);
	auto durRem = dur - (int)dispIo->data1;
	if (durRem >= 0){
		args.SetCondArg(1, durRem);
		return 0;
	}

	auto poisonId = args.GetCondArg(0);
	auto &poisonSpecs = temple::GetRef<PoisonSpec[36]>(0x1028C080);
	auto pspec = poisonSpecs[poisonId];

	// make saving throw
	auto dc = pspec.dc;
	if (dc <= 0){
		dc = args.GetCondArg(2);
	}
	if (dc < 0 || dc > 100) // failsafe
		dc = 15;

	if (pspec.delayedEffect == -10 || pspec.delayedEffect == -9) {
		conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);
		return 0;
	}

	// success - remove condition
	if (damage.SavingThrow(args.objHndCaller, objHndl::null, dc, SavingThrowType::Fortitude, D20STF_POISON)){
		conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);
		return 0;
	}

	// failure

	// check delay poison
	if (d20Sys.d20QueryWithData(args.objHndCaller, DK_QUE_Critter_Has_Condition, conds.GetByName("sp-Delay Poison"), 0)){
		floatSys.FloatSpellLine(args.objHndCaller, 20033, FloatLineColor::White);
		conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);
		return 0;
	}

	floatSys.FloatCombatLine(args.objHndCaller, 56);
	histSys.CreateRollHistoryLineFromMesfile(21, args.objHndCaller, objHndl::null); // "X takes poison damage!"
	floatSys.FloatCombatLine(args.objHndCaller, 96);

	auto rollRes = Dice(pspec.delayedDice.count, pspec.delayedDice.sides, pspec.delayedDice.sides).Roll();
	conds.AddTo(args.objHndCaller, "Temp_Ability_Loss", {pspec.delayedEffect + (pspec.delayedEffect < 0 ? 6:0), rollRes});

	if (pspec.delayedSecondEffect != -10){
		rollRes = Dice(pspec.delayedSecDice.count, pspec.delayedSecDice.sides, pspec.delayedSecDice.sides).Roll();
		conds.AddTo(args.objHndCaller, "Temp_Ability_Loss", { pspec.delayedSecondEffect + (pspec.delayedSecondEffect < 0 ? 6 : 0), rollRes });

	}

	conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);
	return 0;
}

int AbilityConditionFixes::MonsterSplittingHpChange(DispatcherCallbackArgs args){
	auto protoId = objSystem->GetProtoId(args.objHndCaller);
	auto obj = objSystem->GetObject(args.objHndCaller);
	auto loc = obj->GetLocationFull();
	auto hpCur = objects.StatLevelGet(args.objHndCaller, stat_hp_current);
	auto baseHp = obj->GetInt32(obj_f_hp_pts);
	auto hpDam = obj->GetInt32(obj_f_hp_damage);

	auto hpMod = (hpCur + hpDam) - baseHp;

	if (hpCur <= 10)
		return 0;

	obj->SetInt32(obj_f_hp_pts, baseHp / 2);
	obj->SetInt32(obj_f_hp_damage, (hpDam + hpMod) / 2);

	auto protoHandle = objSystem->GetProtoHandle(protoId);
	auto newMonster = objSystem->CreateObject(protoHandle, loc.location);
	auto newMonObj = objSystem->GetObject(newMonster);
	newMonObj->SetInt32(obj_f_hp_pts, baseHp / 2);
	newMonObj->SetInt32(obj_f_hp_damage, (hpDam + hpMod) / 2);
	newMonObj->SetInt32(obj_f_critter_flags, newMonObj->GetInt32(obj_f_critter_flags) | OCF_EXPERIENCE_AWARDED);

	gameSystems->GetParticleSys().CreateAtObj("hit-Acid-medium", args.objHndCaller);
	conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode);

	return 0;
}

int AbilityConditionFixes::MonsterOozeSplittingOnDamage(DispatcherCallbackArgs args){

	auto curHp = objects.StatLevelGet(args.objHndCaller, stat_hp_current);
	if (curHp <= 10)
		return 0;

	GET_DISPIO(dispIOTypeDamage, DispIoDamage);
	auto isSplitting = false;
	auto protoId = objSystem->GetProtoId(args.objHndCaller);
	if (protoId == 14142 && dispIo->damage.GetOverallDamageByType(DamageType::Electricity) > 0){
		isSplitting = true;
		dispIo->damage.AddModFactor(0.0f, DamageType::Electricity, 132);
	}

	if (dispIo->damage.GetOverallDamageByType(DamageType::Slashing) > 0) {
		isSplitting = true;
		dispIo->damage.AddModFactor(0.0f, DamageType::Slashing, 132);
	}

	if (dispIo->damage.GetOverallDamageByType(DamageType::Piercing) > 0) {
		isSplitting = true;
		dispIo->damage.AddModFactor(0.0f, DamageType::Piercing, 132);
	}

	if (dispIo->damage.GetOverallDamageByType(DamageType::PiercingAndSlashing) > 0) {
		isSplitting = true;
		dispIo->damage.AddModFactor(0.0f, DamageType::PiercingAndSlashing, 132);
	}

	if (isSplitting){
		conds.AddTo(args.objHndCaller, "Monster Splitting", {});
	}

	return 0;
}

int AbilityConditionFixes::MonsterSubtypeFire(DispatcherCallbackArgs args){

	GET_DISPIO(dispIOTypeDamage, DispIoDamage);
	auto immuneDamType = (DamageType)args.GetData1();
	auto vulnerDamType = (DamageType)args.GetData2();

	if (!(dispIo->attackPacket.flags & D20CAF_SAVE_SUCCESSFUL))
		dispIo->damage.AddModFactor(1.5, vulnerDamType, 136); // Vulnerable
	dispIo->damage.AddModFactor(0.0, immuneDamType, 104); // Invulnerable
	return 0;
}

int AbilityConditionFixes::GrappledMoveSpeed(DispatcherCallbackArgs args){
	auto spellId = args.GetCondArg(0);
	SpellPacketBody spPkt(spellId);
	if (!spPkt.caster
		|| critterSys.IsDeadOrUnconscious(spPkt.caster))
		return 0;

	GET_DISPIO(dispIOTypeMoveSpeed, DispIoMoveSpeed);
	dispIo->bonlist->SetOverallCap(1, args.GetData1(), 0, args.GetData2());
	dispIo->bonlist->SetOverallCap(2, args.GetData1(), 0, args.GetData2());

	return 0;
}

int AbilityConditionFixes::DiplomacySkillSynergy(DispatcherCallbackArgs args){
	GET_DISPIO(dispIoTypeObjBonus, DispIoObjBonus);

	if (critterSys.SkillBaseGet(args.objHndCaller, SkillEnum::skill_bluff) >= 5)
		dispIo->bonOut->AddBonus(2, 0, 140);
	if (critterSys.SkillBaseGet(args.objHndCaller, SkillEnum::skill_sense_motive) >= 5)
		dispIo->bonOut->AddBonus(2, 0, 302);
	return 0;
}

int AbilityConditionFixes::BootsOfSpeedNewday(DispatcherCallbackArgs args){
	auto arg1 = args.GetCondArg(1);
	args.SetCondArg(0, arg1);
	return 0;
}

int AbilityConditionFixes::BootsOfSpeedBeginRound(DispatcherCallbackArgs args){

	auto roundsRem = args.GetCondArg(0);
	auto isOn = args.GetCondArg(3);
	if (!isOn)
		return 0;
	GET_DISPIO(dispIoTypeSendSignal, DispIoD20Signal);
	int roundsNew = roundsRem - (int)dispIo->data1;
	if (roundsNew >= 0){
		args.SetCondArg(0, roundsNew);
		return 1;
	}
	// conds.ConditionRemove(args.objHndCaller, args.subDispNode->condNode); // this was a bug in vanilla! should just reset the isOn arg, as below
	args.SetCondArg(3, 0);

	auto partsysId = args.GetCondArg(4);
	gameSystems->GetParticleSys().End(partsysId);
	return 0;
}
<|endoftext|>


<commit_before>#include "Polymer.h"

#include<cmath>
#include<iostream>
#include<stdlib.h>
#include<vector>
#include<algorithm>
#include "consts.h"
using namespace consts;
using namespace std;

void Polymer::initiate_monomers_random() {
	double av_velocity = 0.0;

	for (auto& m : monomers) {
		m.velocity = drand48() - 0.5;
		av_velocity += m.velocity;
	}
	av_velocity /= monomers.size();
	for (auto& m : monomers) m.velocity -= av_velocity;
	update_ekin();
	double scale_factor = sqrt(temp()*monomers.size() / ekin*0.5*ref_k);
	for (auto& m : monomers) m.velocity *= scale_factor;
}

void Polymer::initiate_monomers_one() { //erstes Monomer großteil der Energie
	double max_vel = sqrt((monomers.size() - 1)*ref_k*temp() / monomer_mass);
	double speed = -max_vel / (monomers.size() - 1);

	for (auto& m : monomers) m.velocity = speed;
	monomers[0].velocity = max_vel;
}

Polymer::Polymer(int length, double temperature) :
epot(0),
monomer_mass(1. / length),
monomers(std::vector<Monomer>(length, Monomer(0.0, 0.))) {
	temp(temperature);
}

Polymer::~Polymer() {}

double Polymer::temp() { return m_temp; }

void Polymer::temp(double temperature) {
	m_temp = temperature;
	m_feder_konst = monomer_mass * pow(monomers.size() * m_temp * ref_k / ref_hbar, 2);
}

std::ostream & Polymer::print(std::ostream &os) const {
	for (auto& m : monomers) {
		print_m(m, os);
		os << std::endl;
	}
	return os;
}

double Polymer::force(double r) { return -r*m_feder_konst; }

void Polymer::update_forces() {
        double force_buf = 0;
        auto mi=monomers.begin(), mj=monomers.begin();
        mj->force=0;

        for (mi=monomers.begin(), mj=mi, ++mj ; mi!=monomers.end() ; ++mi, ++mj) {
                if ( mj==monomers.end() ) mj = monomers.begin();
                else mj->force=0;

                force_buf = force(*mi - *mj);
                mi->force += force_buf;
                mj->force -= force_buf;
        }
}

double Polymer::update_ekin(){
	ekin = 0.0;
	for (auto& m : monomers) ekin += m.velocity*m.velocity;
	ekin *= monomer_mass*0.5;
	return ekin;
}

double Polymer::calculate_temp() const {
	double av_velocity = 0, av_energy = 0;

	for (auto& m : monomers) av_velocity += m.velocity;
	av_velocity /= monomers.size();
	for (auto& m : monomers) av_energy += pow(m.velocity - av_velocity, 2);
	av_energy *= monomer_mass / 2 / monomers.size();
	return av_energy / (0.5*ref_k);
}
<commit_msg>Revert "Optimieren update_force"<commit_after>#include "Polymer.h"

#include<cmath>
#include<iostream>
#include<stdlib.h>
#include<vector>
#include<algorithm>
#include "consts.h"
using namespace consts;
using namespace std;

void Polymer::initiate_monomers_random() {
	double av_velocity = 0.0;
	for (auto& m : monomers) {
		m.velocity = drand48() - 0.5;
		av_velocity += m.velocity;
	}
	av_velocity /= monomers.size();
	for (auto& m : monomers) m.velocity -= av_velocity;
	update_ekin();
	double scale_factor = sqrt(temp()*monomers.size() / ekin*0.5*ref_k);
	for (auto& m : monomers) m.velocity *= scale_factor;
}

void Polymer::initiate_monomers_one() { //erstes Monomer großteil der Energie
	double max_vel = sqrt((monomers.size() - 1)*ref_k*temp() / monomer_mass);
	double speed = -max_vel / (monomers.size() - 1);
	for (auto& m : monomers) m.velocity = speed;
	monomers[0].velocity = max_vel;
}

Polymer::Polymer(int length, double temperature) :
epot(0),
monomer_mass(1. / length),
monomers(std::vector<Monomer>(length, Monomer(0.0, 0.))) {
	temp(temperature);
}

Polymer::~Polymer() {}

double Polymer::temp() { return m_temp; }

void Polymer::temp(double temperature) {
	m_temp = temperature;
	m_feder_konst = monomer_mass * pow(monomers.size() * m_temp * ref_k / ref_hbar, 2);
}

std::ostream & Polymer::print(std::ostream &os) const {
	for (auto& m : monomers) {
		print_m(m, os);
		os << std::endl;
	}
	return os;
}

double Polymer::force(double r) { return -r*m_feder_konst; }

void Polymer::update_forces() {
	//wahrscheinlich durch Optimierung der nächsten Schleife unnötig
	for (auto& m : monomers) m.force = 0;
	unsigned int size = monomers.size();
	for (unsigned int i = 1; i <= size; i++) {
		double force_buf = force(monomers[i - 1] - monomers[i % size]);
		monomers[i - 1].force += force_buf; //bin mir da mit dem Vorzeichen nicht sicher!
		monomers[i % size].force -= force_buf;
	}
}

double Polymer::update_ekin(){
	ekin = 0.0;
	for (auto& m : monomers) ekin += m.velocity*m.velocity; //mass = 1
	ekin *= monomer_mass*0.5;
	return ekin;
}

double Polymer::calculate_temp() const {
	double av_velocity = 0, av_energy = 0;
	for (auto& m : monomers) av_velocity += m.velocity;
	av_velocity /= monomers.size();
	for (auto& m : monomers) av_energy += pow(m.velocity - av_velocity, 2);
	av_energy *= monomer_mass / 2 / monomers.size();
	return av_energy / (0.5*ref_k);
}
<|endoftext|>


<commit_before>// source changed by mrn@paus.ch/ max rheiner
// original source: henryj@paradise.net.nz

#include <iostream.h>
#include <stdlib.h> // exit()

#ifdef __APPLE_CC__
  #include <GLUT/glut.h>
#else
  #include <GL/glut.h>
#endif

#include "FTGLOutlineFont.h"
#include "FTGLPolygonFont.h"
#include "FTGLBitmapFont.h"
#ifndef FTGL_NO_TEXTURE_FONTS
#include "FTGLTextureFont.h"
#endif
#include "FTGLPixmapFont.h"

static FTFont* fonts[5];
static int width;
static int height;

#ifdef __linux__
const char* DEFAULT_FONT = "/usr/share/fonts/truetype/arial.ttf";
#else
#ifdef __APPLE_CC__
const char* DEFAULT_FONT = "/Users/henry/Development/PROJECTS/FTGL/ftglcvs/FTGL/demo/arial.ttf";
#else
#ifdef WIN32
const char* DEFAULT_FONT = "C:\\WINNT\\Fonts\\arial.ttf";
#else
const char* DEFAULT_FONT = "arial.ttf";
#endif
#endif
#endif

int file_exists( const char * filename );

void draw_scene();

void
my_init( const char* font_filename )
{
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    fonts[0] = new FTGLOutlineFont;
    fonts[1] = new FTGLPolygonFont;
#ifndef FTGL_NO_TEXTURE_FONTS
    fonts[2] = new FTGLTextureFont;
#endif
    fonts[3] = new FTGLBitmapFont;
    fonts[4] = new FTGLPixmapFont;
    for (int i=0; i< 5; i++) {
#ifdef FTGL_NO_TEXTURE_FONTS
    if (i == 2) continue;
#endif
        if (!fonts[i]->Open(font_filename)) {
            cerr << "ERROR: Unable to open file " << font_filename << "\n";
        }
        else {
			cout << "Reading font " << i << " from " << font_filename << endl;

            int point_size = 24;
            if (!fonts[i]->FaceSize(point_size)) {
                cerr << "ERROR: Unable to set font face size " << point_size << "\n";
            }

            // Try to load AFM font metrics
            const char* ext = strrchr(font_filename, '.');
            if (ext && !strcmp(ext, ".pfb"))
            {
            char *metrics = new char[strlen(font_filename)];
            strncpy(metrics, font_filename, ext - font_filename);
            strcpy(metrics + (ext - font_filename), ".afm");
            if (file_exists(metrics))
              {
              cout << "Attaching font metrics from " << metrics << endl;
              fonts[i]->Attach(metrics);
              }
            }
        }
    }
}

static void
do_ortho()
{
  int w;
  int h;
  GLdouble size;
  GLdouble aspect;

  w = width;
  h = height;
  aspect = (GLdouble)w / (GLdouble)h;

  // Use the whole window.
  glViewport(0, 0, w, h);

  // We are going to do some 2-D orthographic drawing.
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  size = (GLdouble)((w >= h) ? w : h) / 2.0;
  if (w <= h) {
    aspect = (GLdouble)h/(GLdouble)w;
    glOrtho(-size, size, -size*aspect, size*aspect,
            -100000.0, 100000.0);
  }
  else {
    aspect = (GLdouble)w/(GLdouble)h;
    glOrtho(-size*aspect, size*aspect, -size, size,
            -100000.0, 100000.0);
  }

  // Make the world and window coordinates coincide so that 1.0 in
  // model space equals one pixel in window space.
  glScaled(aspect, aspect, 1.0);

   // Now determine where to draw things.
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

#ifndef GLUTCALLBACK
#define GLUTCALLBACK
#endif

extern "C" {

void
GLUTCALLBACK my_display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    draw_scene();

    glutSwapBuffers();
}

void
GLUTCALLBACK my_reshape(int w, int h)
{
  width = w;
  height = h;

  do_ortho( );
}

void
GLUTCALLBACK my_handle_key(unsigned char key, int, int)
{
   switch (key) {

	   //!!ELLERS
   case 'q':   // Esc or 'q' Quits the program.
   case 27:
	   {
       for (int i=0; i<5; i++) {
#ifdef FTGL_NO_TEXTURE_FONTS
       if (i == 2) continue;
#endif
           if (fonts[i]) {
               delete fonts[i];
               fonts[i] = 0;
           }
       }
      exit(1);
	   }
      break;

   default:
      break;
   }
}

} // End of extern C

void
draw_scene()
{
   /* Set up some strings with the characters to draw. */
   unsigned int count = 0;
   char string[8][256];
   int i;
   for (i=1; i < 32; i++) { /* Skip zero - it's the null terminator! */
      string[0][count] = i;
      count++;
   }
   string[0][count] = '\0';

   count = 0;
   for (i=32; i < 64; i++) {
      string[1][count] = i;
      count++;
   }
   string[1][count] = '\0';

   count = 0;
   for (i=64; i < 96; i++) {
      string[2][count] = i;
      count++;
   }
   string[2][count] = '\0';

   count = 0;
   for (i=96; i < 128; i++) {
      string[3][count] = i;
      count++;
   }
   string[3][count] = '\0';

   count = 0;
   for (i=128; i < 160; i++) {
      string[4][count] = i;
      count++;
   }
   string[4][count] = '\0';

   count = 0;
   for (i=160; i < 192; i++) {
      string[5][count] = i;
      count++;
   }
   string[5][count] = '\0';

   count = 0;
   for (i=192; i < 224; i++) {
      string[6][count] = i;
      count++;
   }
   string[6][count] = '\0';

   count = 0;
   for (i=224; i < 256; i++) {
      string[7][count] = i;
      count++;
   }
   string[7][count] = '\0';

   glColor3f(1.0, 1.0, 1.0);

   for (int font = 0; font < 5; font++) {
       GLfloat x = -250.0;
       GLfloat y;
       GLfloat yild = 20.0;
       for (int j=0; j<4; j++) {
           y = 275.0-font*120.0-j*yild;
           if (font >= 3) {
               glRasterPos2f(x, y);
               fonts[font]->render(string[j]);
           }
           else {
#ifdef FTGL_NO_TEXTURE_FONTS
           if (font == 2) continue;
#endif
               if (font == 2) {
                   glEnable(GL_TEXTURE_2D);
                   glEnable(GL_BLEND);
                   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
               }
               glPushMatrix(); {
                   glTranslatef(x, y, 0.0);
                   fonts[font]->render(string[j]);
               } glPopMatrix();
               if (font == 2) {
                   glDisable(GL_TEXTURE_2D);
                   glDisable(GL_BLEND);
               }
           }
       }
   }
}

int
file_exists( const char * filename )
{
	FILE * fp = fopen( filename, "r" );

	if ( fp == NULL )
	{
		// That fopen failed does _not_ definitely mean the file isn't there
		// but for now this is ok
		return 0;
	}
	fclose( fp );
	return 1;
}

void
usage( const char * program )
{
	cerr << "Usage: " << program << " <filename.ttf>\n" << endl;
}

int
main(int argc, char **argv)
{
	//!!ELLERS  -- cleaned up
	const char * filename;

	glutInitWindowSize(600, 600);
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE);

	glutCreateWindow("FTGL demo");

	if ( argc >= 2 )
	{
		if ( !file_exists( argv[ 1 ] ))
		{
			usage( argv[ 0 ]);
			cerr << "Couldn't open file '" << argv[ 1 ] << "'" << endl;
			exit( -1 );
		}
		filename = argv[ 1 ];
	}
	else
	{
		// try a default font
		filename = DEFAULT_FONT;

		if ( !file_exists( filename ))
		{
			usage( argv[ 0 ]);
			cerr << "Couldn't open default file '" << filename << "'" << endl;
			exit( -1 );
		}
	}

	my_init( filename );

	glutDisplayFunc(my_display);
	glutReshapeFunc(my_reshape);
	glutKeyboardFunc(my_handle_key);

	glutMainLoop();
	return 0;
}

<commit_msg>ENH: add option to test different size<commit_after>// source changed by mrn@paus.ch/ max rheiner
// original source: henryj@paradise.net.nz

#include <iostream.h>
#include <stdlib.h> // exit()

#ifdef __APPLE_CC__
  #include <GLUT/glut.h>
#else
  #include <GL/glut.h>
#endif

#include "FTGLOutlineFont.h"
#include "FTGLPolygonFont.h"
#include "FTGLBitmapFont.h"
#ifndef FTGL_NO_TEXTURE_FONTS
#include "FTGLTextureFont.h"
#endif
#include "FTGLPixmapFont.h"

static FTFont* fonts[5];
static int width;
static int height;

static int point_size = 24;

#ifdef __linux__
const char* DEFAULT_FONT = "/usr/share/fonts/truetype/arial.ttf";
#else
#ifdef __APPLE_CC__
const char* DEFAULT_FONT = "/Users/henry/Development/PROJECTS/FTGL/ftglcvs/FTGL/demo/arial.ttf";
#else
#ifdef WIN32
const char* DEFAULT_FONT = "C:\\WINNT\\Fonts\\arial.ttf";
#else
const char* DEFAULT_FONT = "arial.ttf";
#endif
#endif
#endif

int file_exists( const char * filename );

void draw_scene();

void
my_init( const char* font_filename )
{
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    fonts[0] = new FTGLOutlineFont;
    fonts[1] = new FTGLPolygonFont;
#ifndef FTGL_NO_TEXTURE_FONTS
    fonts[2] = new FTGLTextureFont;
#endif
    fonts[3] = new FTGLBitmapFont;
    fonts[4] = new FTGLPixmapFont;
    for (int i=0; i< 5; i++) {
#ifdef FTGL_NO_TEXTURE_FONTS
    if (i == 2) continue;
#endif
        if (!fonts[i]->Open(font_filename)) {
            cerr << "ERROR: Unable to open file " << font_filename << "\n";
        }
        else {
			cout << "Reading font " << i << " from " << font_filename << endl;

            if (!fonts[i]->FaceSize(point_size)) {
                cerr << "ERROR: Unable to set font face size " << point_size << "\n";
            }

            // Try to load AFM font metrics
            const char* ext = strrchr(font_filename, '.');
            if (ext && !strcmp(ext, ".pfb"))
            {
            char *metrics = new char[strlen(font_filename)];
            strncpy(metrics, font_filename, ext - font_filename);
            strcpy(metrics + (ext - font_filename), ".afm");
            if (file_exists(metrics))
              {
              cout << "Attaching font metrics from " << metrics << endl;
              fonts[i]->Attach(metrics);
              }
            }
        }
    }
}

static void
do_ortho()
{
  int w;
  int h;
  GLdouble size;
  GLdouble aspect;

  w = width;
  h = height;
  aspect = (GLdouble)w / (GLdouble)h;

  // Use the whole window.
  glViewport(0, 0, w, h);

  // We are going to do some 2-D orthographic drawing.
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  size = (GLdouble)((w >= h) ? w : h) / 2.0;
  if (w <= h) {
    aspect = (GLdouble)h/(GLdouble)w;
    glOrtho(-size, size, -size*aspect, size*aspect,
            -100000.0, 100000.0);
  }
  else {
    aspect = (GLdouble)w/(GLdouble)h;
    glOrtho(-size*aspect, size*aspect, -size, size,
            -100000.0, 100000.0);
  }

  // Make the world and window coordinates coincide so that 1.0 in
  // model space equals one pixel in window space.
  glScaled(aspect, aspect, 1.0);

   // Now determine where to draw things.
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

#ifndef GLUTCALLBACK
#define GLUTCALLBACK
#endif

extern "C" {

void
GLUTCALLBACK my_display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    draw_scene();

    glutSwapBuffers();
}

void
GLUTCALLBACK my_reshape(int w, int h)
{
  width = w;
  height = h;

  do_ortho( );
}

void
GLUTCALLBACK my_handle_key(unsigned char key, int, int)
{
   switch (key) {

	   //!!ELLERS
   case 'q':   // Esc or 'q' Quits the program.
   case 27:
	   {
       for (int i=0; i<5; i++) {
#ifdef FTGL_NO_TEXTURE_FONTS
       if (i == 2) continue;
#endif
           if (fonts[i]) {
               delete fonts[i];
               fonts[i] = 0;
           }
       }
      exit(1);
	   }
      break;

   default:
      break;
   }
}

} // End of extern C

void
draw_scene()
{
   /* Set up some strings with the characters to draw. */
   unsigned int count = 0;
   char string[8][256];
   int i;
   for (i=1; i < 32; i++) { /* Skip zero - it's the null terminator! */
      string[0][count] = i;
      count++;
   }
   string[0][count] = '\0';

   count = 0;
   for (i=32; i < 64; i++) {
      string[1][count] = i;
      count++;
   }
   string[1][count] = '\0';

   count = 0;
   for (i=64; i < 96; i++) {
      string[2][count] = i;
      count++;
   }
   string[2][count] = '\0';

   count = 0;
   for (i=96; i < 128; i++) {
      string[3][count] = i;
      count++;
   }
   string[3][count] = '\0';

   count = 0;
   for (i=128; i < 160; i++) {
      string[4][count] = i;
      count++;
   }
   string[4][count] = '\0';

   count = 0;
   for (i=160; i < 192; i++) {
      string[5][count] = i;
      count++;
   }
   string[5][count] = '\0';

   count = 0;
   for (i=192; i < 224; i++) {
      string[6][count] = i;
      count++;
   }
   string[6][count] = '\0';

   count = 0;
   for (i=224; i < 256; i++) {
      string[7][count] = i;
      count++;
   }
   string[7][count] = '\0';

   glColor3f(1.0, 1.0, 1.0);

   for (int font = 0; font < 5; font++) {
       GLfloat x = -250.0;
       GLfloat y;
       GLfloat yild = 20.0;
       for (int j=0; j<4; j++) {
           y = 275.0-font*120.0-j*yild;
           if (font >= 3) {
               glRasterPos2f(x, y);
               fonts[font]->render(string[j]);
           }
           else {
#ifdef FTGL_NO_TEXTURE_FONTS
           if (font == 2) continue;
#endif
               if (font == 2) {
                   glEnable(GL_TEXTURE_2D);
                   glEnable(GL_BLEND);
                   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
               }
               glPushMatrix(); {
                   glTranslatef(x, y, 0.0);
                   fonts[font]->render(string[j]);
               } glPopMatrix();
               if (font == 2) {
                   glDisable(GL_TEXTURE_2D);
                   glDisable(GL_BLEND);
               }
           }
       }
   }
}

int
file_exists( const char * filename )
{
	FILE * fp = fopen( filename, "r" );

	if ( fp == NULL )
	{
		// That fopen failed does _not_ definitely mean the file isn't there
		// but for now this is ok
		return 0;
	}
	fclose( fp );
	return 1;
}

void
usage( const char * program )
{
	cerr << "Usage: " << program << " <filename.ttf>\n" << endl;
}

int
main(int argc, char **argv)
{
	//!!ELLERS  -- cleaned up
	const char * filename;

	glutInitWindowSize(600, 600);
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE);

	glutCreateWindow("FTGL demo");

	if ( argc >= 2 )
	{
		if ( !file_exists( argv[ 1 ] ))
		{
			usage( argv[ 0 ]);
			cerr << "Couldn't open file '" << argv[ 1 ] << "'" << endl;
			exit( -1 );
		}
		filename = argv[ 1 ];

        if ( argc >= 3 )
          {
          point_size = atoi(argv[2]);
          }
	}
	else
	{
		// try a default font
		filename = DEFAULT_FONT;

		if ( !file_exists( filename ))
		{
			usage( argv[ 0 ]);
			cerr << "Couldn't open default file '" << filename << "'" << endl;
			exit( -1 );
		}
	}

	my_init( filename );

	glutDisplayFunc(my_display);
	glutReshapeFunc(my_reshape);
	glutKeyboardFunc(my_handle_key);

	glutMainLoop();
	return 0;
}

<|endoftext|>


<commit_before>#pragma once

#include <list>
#include <memory>
#include "Vector3D.hpp"
#include "Ray.hpp"

namespace RealRT
{
    class Shape;
    class Light;

    class RTEngine
    {
    public:
        static const int EyeDepth = 5;	// Distance from the origin to put the eye point on the Z-axis
        static const int MaxTraceIterations = 6; // Maximum number of reflection/refractions
        static const int LogicalSpaceSize = 20; // Target size for the logical space dimensions

        RTEngine(int width, int height);

        void Resize(int width, int height);

        template <class TraceStrategy, class RenderStrategy>
        void Render(void)
        {
            TraceStrategy trace(_World);
            RenderStrategy render(_ScreenWidth, _ScreenHeight);

            Vector3D eyeLoc = {0.0, 0.0, -EyeDepth};

            render([&trace, &eyeLoc, this](int i, int j)
            {
                double x, y;
                _ScreenToLogical(i, j, x, y);

                Vector3D screenPosition = {x, y, 0.0};

                Ray principleRay(eyeLoc, screenPosition - eyeLoc);

                Vector3D pixelColor = trace(principleRay);

                Vector3D suppressedColor = pixelColor.Clip(1.0);

                int red = suppressedColor.I() * 255;
                int green = suppressedColor.J() * 255;
                int blue = suppressedColor.K() * 255;

                _Screen[(j * _ScreenWidth + i) * 3] = red;
                _Screen[(j * _ScreenWidth + i) * 3 + 1] = green;
                _Screen[(j * _ScreenWidth + i) * 3 + 2] = blue;
            });
        }

        template <class T>
        void AddShape(T obj)
        {
            std::shared_ptr<Shape> shapePtr(new T(obj));

            _Shapes.push_back(shapePtr);
        }

        template <class T>
        void RemoveShape(T obj)
        {
            _Shapes.remove_if([&obj](std::shared_ptr<Shape> el)
            {
                std::shared_ptr<T> testEl = std::dynamic_pointer_cast<T>(el);

                if(testEl != nullptr)
                    return *(testEl) == obj;
                else
                    return false;
            });
        }

        template <class T>
        void AddLight(T obj)
        {
            std::shared_ptr<Light> lightPtr(new T(obj));

            _Lights.push_back(lightPtr);
        }

        template <class T>
        void RemoveLight(T obj)
        {
            _Lights.remove_if([&obj](std::shared_ptr<Light> el)
            {
                std::shared_ptr<T> testEl = std::dynamic_pointer_cast<T>(el);

                if(testEl != nullptr)
                    return *(testEl) == obj;
                else
                    return false;
            });
        }

        int ScreenWidth(void) const;
        int ScreenHeight(void) const;

        unsigned char *Screen(void) const;

    private:

        void _ScreenToLogical(const int i, const int j, double &x, double &y) const;

        std::list<std::shared_ptr<Shape>> _Shapes;
        std::list<std::share_ptr<Light>> _Lights;

        double _LogicalWidth = 20;
        double _LogicalHeight = 20;

        int _ScreenWidth;
        int _ScreenHeight;

        std::unique_ptr<unsigned char[]> _Screen;
    };
}
<commit_msg>Updated engine to pass lights to the trace strategy<commit_after>#pragma once

#include <list>
#include <memory>
#include "Vector3D.hpp"
#include "Ray.hpp"

namespace RealRT
{
    class Shape;
    class Light;

    class RTEngine
    {
    public:
        static const int EyeDepth = 5;	// Distance from the origin to put the eye point on the Z-axis
        static const int MaxTraceIterations = 6; // Maximum number of reflection/refractions
        static const int LogicalSpaceSize = 20; // Target size for the logical space dimensions

        RTEngine(int width, int height);

        void Resize(int width, int height);

        template <class TraceStrategy, class RenderStrategy>
        void Render(void)
        {
            TraceStrategy trace(_Shapes, _Lights);
            RenderStrategy render(_ScreenWidth, _ScreenHeight);

            Vector3D eyeLoc = {0.0, 0.0, -EyeDepth};

            render([&trace, &eyeLoc, this](int i, int j)
            {
                double x, y;
                _ScreenToLogical(i, j, x, y);

                Vector3D screenPosition = {x, y, 0.0};

                Ray principleRay(eyeLoc, screenPosition - eyeLoc);

                Vector3D pixelColor = trace(principleRay);

                Vector3D suppressedColor = pixelColor.Clip(1.0);

                int red = suppressedColor.I() * 255;
                int green = suppressedColor.J() * 255;
                int blue = suppressedColor.K() * 255;

                _Screen[(j * _ScreenWidth + i) * 3] = red;
                _Screen[(j * _ScreenWidth + i) * 3 + 1] = green;
                _Screen[(j * _ScreenWidth + i) * 3 + 2] = blue;
            });
        }

        template <class T>
        void AddShape(T obj)
        {
            std::shared_ptr<Shape> shapePtr(new T(obj));

            _Shapes.push_back(shapePtr);
        }

        template <class T>
        void RemoveShape(T obj)
        {
            _Shapes.remove_if([&obj](std::shared_ptr<Shape> el)
            {
                std::shared_ptr<T> testEl = std::dynamic_pointer_cast<T>(el);

                if(testEl != nullptr)
                    return *(testEl) == obj;
                else
                    return false;
            });
        }

        template <class T>
        void AddLight(T obj)
        {
            std::shared_ptr<Light> lightPtr(new T(obj));

            _Lights.push_back(lightPtr);
        }

        template <class T>
        void RemoveLight(T obj)
        {
            _Lights.remove_if([&obj](std::shared_ptr<Light> el)
            {
                std::shared_ptr<T> testEl = std::dynamic_pointer_cast<T>(el);

                if(testEl != nullptr)
                    return *(testEl) == obj;
                else
                    return false;
            });
        }

        int ScreenWidth(void) const;
        int ScreenHeight(void) const;

        unsigned char *Screen(void) const;

    private:

        void _ScreenToLogical(const int i, const int j, double &x, double &y) const;

        std::list<std::shared_ptr<Shape>> _Shapes;
        std::list<std::share_ptr<Light>> _Lights;

        double _LogicalWidth = 20;
        double _LogicalHeight = 20;

        int _ScreenWidth;
        int _ScreenHeight;

        std::unique_ptr<unsigned char[]> _Screen;
    };
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2008-2018 the Urho3D project.
//
// 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 "../Precompiled.h"

#include "../Core/CoreEvents.h"
#include "../Core/Profiler.h"

#include <ctime>

#ifdef _WIN32
#include <windows.h>
#include <mmsystem.h>
#elif __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif

#include "../DebugNew.h"

namespace Urho3D
{

bool HiresTimer::supported(false);
long long HiresTimer::frequency(1000);

Time::Time(Context* context) :
    Object(context),
    frameNumber_(0),
    timeStep_(0.0f),
    timerPeriod_(0)
{
#ifdef _WIN32
    LARGE_INTEGER frequency;
    if (QueryPerformanceFrequency(&frequency))
    {
        HiresTimer::frequency = frequency.QuadPart;
        HiresTimer::supported = true;
    }
#else
    HiresTimer::frequency = 1000000;
    HiresTimer::supported = true;
#endif
}

Time::~Time()
{
    SetTimerPeriod(0);
}

static unsigned Tick()
{
#ifdef _WIN32
    return (unsigned)GetTickCount();
#elif __EMSCRIPTEN__
    return (unsigned)emscripten_get_now();
#else
    struct timeval time;
    gettimeofday(&time, nullptr);
    return (unsigned)(time.tv_sec * 1000 + time.tv_usec / 1000);
#endif
}

static long long HiresTick()
{
#ifdef _WIN32
    if (HiresTimer::IsSupported())
    {
        LARGE_INTEGER counter;
        QueryPerformanceCounter(&counter);
        return counter.QuadPart;
    }
    else
        return GetTickCount();
#elif __EMSCRIPTEN__
    return (unsigned)(emscripten_get_now()*1000.0);
#else
    struct timeval time;
    gettimeofday(&time, nullptr);
    return time.tv_sec * 1000000LL + time.tv_usec;
#endif
}

void Time::BeginFrame(float timeStep)
{
    ++frameNumber_;
    if (!frameNumber_)
        ++frameNumber_;

    timeStep_ = timeStep;

    {
        URHO3D_PROFILE(BeginFrame);

        // Frame begin event
        using namespace BeginFrame;

        VariantMap& eventData = GetEventDataMap();
        eventData[P_FRAMENUMBER] = frameNumber_;
        eventData[P_TIMESTEP] = timeStep_;
        SendEvent(E_BEGINFRAME, eventData);
    }
}

void Time::EndFrame()
{
    {
        URHO3D_PROFILE(EndFrame);

        // Frame end event
        SendEvent(E_ENDFRAME);
    }
}

void Time::SetTimerPeriod(unsigned mSec)
{
#ifdef _WIN32
    if (timerPeriod_ > 0)
        timeEndPeriod(timerPeriod_);

    timerPeriod_ = mSec;

    if (timerPeriod_ > 0)
        timeBeginPeriod(timerPeriod_);
#endif
}

float Time::GetElapsedTime()
{
    return elapsedTime_.GetMSec(false) / 1000.0f;
}

unsigned Time::GetSystemTime()
{
    return Tick();
}

unsigned Time::GetTimeSinceEpoch()
{
    return (unsigned)time(nullptr);
}

String Time::GetTimeStamp()
{
    time_t sysTime;
    time(&sysTime);
    const char* dateTime = ctime(&sysTime);
    return String(dateTime).Replaced("\n", "");
}

void Time::Sleep(unsigned mSec)
{
#ifdef _WIN32
    ::Sleep(mSec);
#else
    timespec time;
    time.tv_sec = mSec / 1000;
    time.tv_nsec = (mSec % 1000) * 1000000;
    nanosleep(&time, nullptr);
#endif
}

float Time::GetFramesPerSecond() const
{
    return 1.0f / timeStep_;
}

Timer::Timer()
{
    Reset();
}

unsigned Timer::GetMSec(bool reset)
{
    unsigned currentTime = Tick();
    unsigned elapsedTime = currentTime - startTime_;
    if (reset)
        startTime_ = currentTime;

    return elapsedTime;
}

void Timer::Reset()
{
    startTime_ = Tick();
}

HiresTimer::HiresTimer()
{
    Reset();
}

long long HiresTimer::GetUSec(bool reset)
{
    long long currentTime = HiresTick();
    long long elapsedTime = currentTime - startTime_;

    // Correct for possible weirdness with changing internal frequency
    if (elapsedTime < 0)
        elapsedTime = 0;

    if (reset)
        startTime_ = currentTime;

    return (elapsedTime * 1000000LL) / frequency;
}

void HiresTimer::Reset()
{
    startTime_ = HiresTick();
}

}
<commit_msg>Logging: Time::GetTimeStamp() returns timestamp string in "YYYY-MM-DD hh:mm" format.<commit_after>//
// Copyright (c) 2008-2018 the Urho3D project.
//
// 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 "../Precompiled.h"

#include "../Core/CoreEvents.h"
#include "../Core/Profiler.h"

#include <ctime>

#ifdef _WIN32
#include <windows.h>
#include <mmsystem.h>
#elif __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif

#include "../DebugNew.h"

namespace Urho3D
{

bool HiresTimer::supported(false);
long long HiresTimer::frequency(1000);

Time::Time(Context* context) :
    Object(context),
    frameNumber_(0),
    timeStep_(0.0f),
    timerPeriod_(0)
{
#ifdef _WIN32
    LARGE_INTEGER frequency;
    if (QueryPerformanceFrequency(&frequency))
    {
        HiresTimer::frequency = frequency.QuadPart;
        HiresTimer::supported = true;
    }
#else
    HiresTimer::frequency = 1000000;
    HiresTimer::supported = true;
#endif
}

Time::~Time()
{
    SetTimerPeriod(0);
}

static unsigned Tick()
{
#ifdef _WIN32
    return (unsigned)GetTickCount();
#elif __EMSCRIPTEN__
    return (unsigned)emscripten_get_now();
#else
    struct timeval time;
    gettimeofday(&time, nullptr);
    return (unsigned)(time.tv_sec * 1000 + time.tv_usec / 1000);
#endif
}

static long long HiresTick()
{
#ifdef _WIN32
    if (HiresTimer::IsSupported())
    {
        LARGE_INTEGER counter;
        QueryPerformanceCounter(&counter);
        return counter.QuadPart;
    }
    else
        return GetTickCount();
#elif __EMSCRIPTEN__
    return (unsigned)(emscripten_get_now()*1000.0);
#else
    struct timeval time;
    gettimeofday(&time, nullptr);
    return time.tv_sec * 1000000LL + time.tv_usec;
#endif
}

void Time::BeginFrame(float timeStep)
{
    ++frameNumber_;
    if (!frameNumber_)
        ++frameNumber_;

    timeStep_ = timeStep;

    {
        URHO3D_PROFILE(BeginFrame);

        // Frame begin event
        using namespace BeginFrame;

        VariantMap& eventData = GetEventDataMap();
        eventData[P_FRAMENUMBER] = frameNumber_;
        eventData[P_TIMESTEP] = timeStep_;
        SendEvent(E_BEGINFRAME, eventData);
    }
}

void Time::EndFrame()
{
    {
        URHO3D_PROFILE(EndFrame);

        // Frame end event
        SendEvent(E_ENDFRAME);
    }
}

void Time::SetTimerPeriod(unsigned mSec)
{
#ifdef _WIN32
    if (timerPeriod_ > 0)
        timeEndPeriod(timerPeriod_);

    timerPeriod_ = mSec;

    if (timerPeriod_ > 0)
        timeBeginPeriod(timerPeriod_);
#endif
}

float Time::GetElapsedTime()
{
    return elapsedTime_.GetMSec(false) / 1000.0f;
}

unsigned Time::GetSystemTime()
{
    return Tick();
}

unsigned Time::GetTimeSinceEpoch()
{
    return (unsigned)time(nullptr);
}

String Time::GetTimeStamp()
{
    char dateTime[20];
    time_t sysTime;
    time(&sysTime);
    tm* timeInfo = localtime(&sysTime);
    strftime(dateTime, sizeof(dateTime), "%F %T", timeInfo);
    return dateTime;
}

void Time::Sleep(unsigned mSec)
{
#ifdef _WIN32
    ::Sleep(mSec);
#else
    timespec time;
    time.tv_sec = mSec / 1000;
    time.tv_nsec = (mSec % 1000) * 1000000;
    nanosleep(&time, nullptr);
#endif
}

float Time::GetFramesPerSecond() const
{
    return 1.0f / timeStep_;
}

Timer::Timer()
{
    Reset();
}

unsigned Timer::GetMSec(bool reset)
{
    unsigned currentTime = Tick();
    unsigned elapsedTime = currentTime - startTime_;
    if (reset)
        startTime_ = currentTime;

    return elapsedTime;
}

void Timer::Reset()
{
    startTime_ = Tick();
}

HiresTimer::HiresTimer()
{
    Reset();
}

long long HiresTimer::GetUSec(bool reset)
{
    long long currentTime = HiresTick();
    long long elapsedTime = currentTime - startTime_;

    // Correct for possible weirdness with changing internal frequency
    if (elapsedTime < 0)
        elapsedTime = 0;

    if (reset)
        startTime_ = currentTime;

    return (elapsedTime * 1000000LL) / frequency;
}

void HiresTimer::Reset()
{
    startTime_ = HiresTick();
}

}
<|endoftext|>