<commit_before>// Copyright 2016 Raphael Poncet.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//      http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cassert>
#include <memory>

// Dummy value for padded areas of storage (for debug).
const RealT PADDING_FILL = - 3.1415926;
// Dummy value for areas of storage (for detecting non-properly initialized data).
const RealT INIT_FILL = 3.1415926;

#include "common.hpp"
#include "multidimensional_storage.hpp"

static void ValidateInput(int n1, int n2, int n3, int n4, int padding) {

  assert(0 < n1);
  assert(0 < n2);
  assert(0 < n3);
  assert(0 < n4);
  assert(0 <= padding);

}

MultiDimensionalStorage4D::MultiDimensionalStorage4D(int n1, int n2, int n3, int n4):
  m_n_fast(n1), m_n2(n2), m_n3(n3), m_n_slow(n4), m_padding(0), m_data(NULL) {

  ValidateInput(m_n_fast, m_n2, m_n3, m_n_slow, m_padding);

}

  MultiDimensionalStorage4D::MultiDimensionalStorage4D(int n1, int n2, int n3, int n4, int padding):
    m_n_fast(n1), m_n2(n2), m_n3(n3), m_n_slow(n4), m_padding(padding), m_data(NULL) {

    ValidateInput(m_n_fast, m_n2, m_n3, m_n_slow, m_padding);

}

void MultiDimensionalStorage4D::Allocate() {

  LOG_DEBUG << "Allocating 4D storage with "
	    << "[n_fast(+padding), n2, n3, n_slow] = "
	    << "[" << m_n_fast << "(+" << m_padding << "), "
    	    << m_n2 << ", "
	    << m_n3 << ", "
	    << m_n_slow << "]...";

  const size_t nb_elements = (m_n_fast + m_padding) * m_n2 * m_n3 * m_n_slow;
  const size_t size_in_bytes = nb_elements * sizeof(RealT);

  m_data = (RealT*)malloc(size_in_bytes);

  LOG_DEBUG << "Allocation done, memory used = "
	    << static_cast<double>(size_in_bytes / (1024.0 * 1024.0))
	    << " MBytes.";

  LOG_DEBUG << "Setting storage...";

  for (size_t i = 0; i < nb_elements; ++i)
    m_data[i] = INIT_FILL;

  // Now, Fill only the padded parts of the data. They should be left
  // untouched by the code.
  const int n_fast = m_n_fast;
  const int n_fast_pad = n_fast + m_padding;
  const int n2 = m_n2;
  const int n3 = m_n3;
  const int n_slow = m_n_slow;

  for (int islow = 0; islow < n_slow; ++islow) {
    for (int i3 = 0; i3 < n3; ++i3) {
      for (int i2 = 0; i2 < n2; ++i2) {
	for (int ifast = n_fast; ifast < n_fast_pad; ++ifast) {

	  const size_t index =
	    n3 * n2 * n_fast_pad * islow + n2 * n_fast_pad * i3 + n_fast_pad * i2 + ifast;

	  m_data[index] = PADDING_FILL;

	}
      }
    }
  }

  LOG_DEBUG << "Setting storage done.";

}

void MultiDimensionalStorage4D::DeAllocate() {

  LOG_DEBUG << "Deallocating 4D storage...";

  if (m_data != NULL)
    free(m_data);

  LOG_DEBUG << "Deallocation done.";

}

void MultiDimensionalStorage4D::Validate() {

  LOG_DEBUG << "Validating 4D storage...";

  size_t error_count = 0;

  const int n_fast = m_n_fast;
  const int n_fast_pad = n_fast + m_padding;
  const int n2 = m_n2;
  const int n3 = m_n3;
  const int n_slow = m_n_slow;

  // Padded area should be left untouched by the code.
  for (int islow = 0; islow < n_slow; ++islow) {
    for (int i3 = 0; i3 < n3; ++i3) {
      for (int i2 = 0; i2 < n2; ++i2) {
	for (int ifast = n_fast; ifast < n_fast_pad; ++ifast) {

	const size_t index =
	  n3 * n2 * n_fast_pad * islow + n2 * n_fast_pad * i3 + n_fast_pad * i2 + ifast;

	if (m_data[index] != PADDING_FILL)
	  error_count += 1;

	}
      }
    }
  }

  if (error_count != 0) {

    LOG_ERROR << "in MultiDimensional4D instance, "
	      << error_count << " elements in padded area have been touched.";

    std::abort();

  }

  LOG_DEBUG << "Validating 4D storage done.";

}
<commit_msg>Add line spacing to log file<commit_after>// Copyright 2016 Raphael Poncet.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//      http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cassert>
#include <memory>

// Dummy value for padded areas of storage (for debug).
const RealT PADDING_FILL = - 3.1415926;
// Dummy value for areas of storage (for detecting non-properly initialized data).
const RealT INIT_FILL = 3.1415926;

#include "common.hpp"
#include "multidimensional_storage.hpp"

static void ValidateInput(int n1, int n2, int n3, int n4, int padding) {

  assert(0 < n1);
  assert(0 < n2);
  assert(0 < n3);
  assert(0 < n4);
  assert(0 <= padding);

}

MultiDimensionalStorage4D::MultiDimensionalStorage4D(int n1, int n2, int n3, int n4):
  m_n_fast(n1), m_n2(n2), m_n3(n3), m_n_slow(n4), m_padding(0), m_data(NULL) {

  ValidateInput(m_n_fast, m_n2, m_n3, m_n_slow, m_padding);

}

  MultiDimensionalStorage4D::MultiDimensionalStorage4D(int n1, int n2, int n3, int n4, int padding):
    m_n_fast(n1), m_n2(n2), m_n3(n3), m_n_slow(n4), m_padding(padding), m_data(NULL) {

    ValidateInput(m_n_fast, m_n2, m_n3, m_n_slow, m_padding);

}

void MultiDimensionalStorage4D::Allocate() {

  LOG_DEBUG << "Allocating 4D storage with "
	    << "[n_fast(+padding), n2, n3, n_slow] = "
	    << "[" << m_n_fast << "(+" << m_padding << "), "
    	    << m_n2 << ", "
	    << m_n3 << ", "
	    << m_n_slow << "]...";

  const size_t nb_elements = (m_n_fast + m_padding) * m_n2 * m_n3 * m_n_slow;
  const size_t size_in_bytes = nb_elements * sizeof(RealT);

  m_data = (RealT*)malloc(size_in_bytes);

  LOG_DEBUG << "Allocation done, memory used = "
	    << static_cast<double>(size_in_bytes / (1024.0 * 1024.0))
	    << " MBytes."
	    << "\n";

  LOG_DEBUG << "Setting storage...";

  for (size_t i = 0; i < nb_elements; ++i)
    m_data[i] = INIT_FILL;

  // Now, Fill only the padded parts of the data. They should be left
  // untouched by the code.
  const int n_fast = m_n_fast;
  const int n_fast_pad = n_fast + m_padding;
  const int n2 = m_n2;
  const int n3 = m_n3;
  const int n_slow = m_n_slow;

  for (int islow = 0; islow < n_slow; ++islow) {
    for (int i3 = 0; i3 < n3; ++i3) {
      for (int i2 = 0; i2 < n2; ++i2) {
	for (int ifast = n_fast; ifast < n_fast_pad; ++ifast) {

	  const size_t index =
	    n3 * n2 * n_fast_pad * islow + n2 * n_fast_pad * i3 + n_fast_pad * i2 + ifast;

	  m_data[index] = PADDING_FILL;

	}
      }
    }
  }

  LOG_DEBUG << "Setting storage done."
	    << "\n";

}

void MultiDimensionalStorage4D::DeAllocate() {

  LOG_DEBUG << "Deallocating 4D storage...";

  if (m_data != NULL)
    free(m_data);

  LOG_DEBUG << "Deallocation done."
	    << "\n";

}

void MultiDimensionalStorage4D::Validate() {

  LOG_DEBUG << "Validating 4D storage...";

  size_t error_count = 0;

  const int n_fast = m_n_fast;
  const int n_fast_pad = n_fast + m_padding;
  const int n2 = m_n2;
  const int n3 = m_n3;
  const int n_slow = m_n_slow;

  // Padded area should be left untouched by the code.
  for (int islow = 0; islow < n_slow; ++islow) {
    for (int i3 = 0; i3 < n3; ++i3) {
      for (int i2 = 0; i2 < n2; ++i2) {
	for (int ifast = n_fast; ifast < n_fast_pad; ++ifast) {

	const size_t index =
	  n3 * n2 * n_fast_pad * islow + n2 * n_fast_pad * i3 + n_fast_pad * i2 + ifast;

	if (m_data[index] != PADDING_FILL)
	  error_count += 1;

	}
      }
    }
  }

  if (error_count != 0) {

    LOG_ERROR << "in MultiDimensional4D instance, "
	      << error_count << " elements in padded area have been touched.";

    std::abort();

  }

  LOG_DEBUG << "Validating 4D storage done."
	    << "\n";

}
<|endoftext|>


<commit_before>// For conditions of distribution and use, see copyright notice in license.txt

#include "StableHeaders.h"
#include "DebugOperatorNew.h"
#include "MemoryLeakCheck.h"
#include "RexMovementInput.h"
#include "InputEvents.h"
#include "RexTypes.h"
#include "InputAPI.h"
#include "EventManager.h"

#include <QApplication>

namespace RexLogic
{

RexMovementInput::RexMovementInput(Foundation::Framework *framework_)
{
    framework = framework_;

    // Create a new input context that this object will use to fetch the avatar and camera input from.
    input = framework->Input()->RegisterInputContext("RexAvatarInput", 100);

    // To be sure that Qt doesn't play tricks on us and miss a mouse release when we're in FPS mode,
    // grab the mouse movement input over Qt.
    input->SetTakeMouseEventsOverQt(true);

    // Listen on both key and mouse input signals.
    connect(input.get(), SIGNAL(KeyEventReceived(KeyEvent *)), this, SLOT(HandleKeyEvent(KeyEvent *)));
    connect(input.get(), SIGNAL(MouseEventReceived(MouseEvent *)), this, SLOT(HandleMouseEvent(MouseEvent *)));
}

// Either sends an input event press or release, depending on the key event type.
void SendPressOrRelease(EventManagerPtr eventMgr, const KeyEvent *key, int eventID)
{
    // In the AvatarControllable and CameraControllable framework, each input event id that denotes a press
    // also has a corresponding release event, which has an ID one larger than the press event.
    if (key->eventType == KeyEvent::KeyPressed)
        eventMgr->SendEvent("Input", eventID, 0);
    else if (key->eventType == KeyEvent::KeyReleased)
        eventMgr->SendEvent("Input", eventID+1, 0);
}

void RexMovementInput::HandleKeyEvent(KeyEvent *key)
{
    // This function handles received input events and translates them to the "traditional"-style
    // Naali input events. New modules should really prefer using an InputContext of their
    // own to read input data, or use the QtInputService API directly.

    InputAPI *inputService = framework->Input();

    EventManagerPtr eventMgr = framework->GetEventManager();

    ///\todo Accessing the key bindings by string names is a bit silly, since for each key event, this
    /// generates a string compare. Now for every key, we do this for every key we want to act on.
    /// For optimization we can prehash the strings, but that will occur later when we profile performance
    /// issues here.
    PROFILE(RexMovement_HandleKeyEvent);

    ///\bug Qt does not have a separate key code for keypad minus, but instead uses "Qt::Key_Minus | Qt::KeypadModifier". However,
    /// this causes it to fail when showing it in portable text mode in the key bindings editor, i.e.
    /// QKeySequence(Qt::Key_Minus | Qt::KeypadModifier | Qt::ControlModifier).toString(QKeySequence::NativeText); fails.

    // As a workaround, the following two keys are hardcoded to keypad plus. See http://bugreports.qt.nokia.com/browse/QTBUG-2913
    // and http://qt.nokia.com/developer/task-tracker/index_html?method=entry&id=229868

    const QKeySequence zoomIn = inputService->KeyBinding("Avatar.ZoomIn", Qt::Key_Minus | Qt::ControlModifier);
    const QKeySequence zoomOut = inputService->KeyBinding("Avatar.ZoomOut", Qt::Key_Plus | Qt::ControlModifier);

    // For the zoom in and out keys, key repeats trigger continuous actions.
    if ((key->KeyWithModifier() & ~Qt::KeypadModifier) == zoomIn) SendPressOrRelease(eventMgr, key, InputEvents::ZOOM_IN_PRESSED);
    if ((key->KeyWithModifier() & ~Qt::KeypadModifier) == zoomOut) SendPressOrRelease(eventMgr, key, InputEvents::ZOOM_OUT_PRESSED);

    // For the following keys, we ignore all key presses that are repeats.
    if (key->eventType == KeyEvent::KeyPressed && key->keyPressCount > 1)
        return;

    const QKeySequence walkForward =   inputService->KeyBinding("Avatar.WalkForward", Qt::Key_W);
    const QKeySequence walkBackward =  inputService->KeyBinding("Avatar.WalkBack", Qt::Key_S);
    const QKeySequence walkForward2 =  inputService->KeyBinding("Avatar.WalkForward2", Qt::Key_Up);
    const QKeySequence walkBackward2 = inputService->KeyBinding("Avatar.WalkBack2", Qt::Key_Down);
    const QKeySequence strafeLeft =  inputService->KeyBinding("Avatar.StrafeLeft", Qt::Key_A);
    const QKeySequence strafeRight = inputService->KeyBinding("Avatar.StrafeRight", Qt::Key_D);
    const QKeySequence rotateLeft =  inputService->KeyBinding("Avatar.RotateLeft", Qt::Key_Left);
    const QKeySequence rotateRight = inputService->KeyBinding("Avatar.RotateRight", Qt::Key_Right);
    const QKeySequence up =   inputService->KeyBinding("Avatar.Up", Qt::Key_Space); // Jump or fly up, depending on whether in fly mode or walk mode.
    const QKeySequence up2 =   inputService->KeyBinding("Avatar.Up2", Qt::Key_PageUp); // Jump or fly up, depending on whether in fly mode or walk mode.
    const QKeySequence down = inputService->KeyBinding("Avatar.Down", /*Qt::Key_Control*/Qt::Key_C); // Crouch or fly down, depending on whether in fly mode or walk mode.
    const QKeySequence down2 = inputService->KeyBinding("Avatar.Down2", /*Qt::Key_Control*/Qt::Key_PageDown); // Crouch or fly down, depending on whether in fly mode or walk mode.
    const QKeySequence flyModeToggle =    inputService->KeyBinding("Avatar.ToggleFly", Qt::Key_F);
    const QKeySequence cameraModeToggle = inputService->KeyBinding("Avatar.ToggleCameraMode", Qt::Key_Tab);
    const QKeySequence cameraModeTripod = inputService->KeyBinding("Avatar.TripodCameraMode", Qt::Key_T);

    // The following code defines the keyboard actions that are available for the avatar/freelookcamera system.
    if (key->keyCode == walkForward || key->keyCode == walkForward2) SendPressOrRelease(eventMgr, key, InputEvents::MOVE_FORWARD_PRESSED);
    if (key->keyCode == walkBackward || key->keyCode == walkBackward2) SendPressOrRelease(eventMgr, key, InputEvents::MOVE_BACK_PRESSED);
    if (key->keyCode == strafeLeft) SendPressOrRelease(eventMgr, key, InputEvents::MOVE_LEFT_PRESSED);
    if (key->keyCode == strafeRight) SendPressOrRelease(eventMgr, key, InputEvents::MOVE_RIGHT_PRESSED);
    if (key->keyCode == rotateLeft)  SendPressOrRelease(eventMgr, key, InputEvents::ROTATE_LEFT_PRESSED);
    if (key->keyCode == rotateRight) SendPressOrRelease(eventMgr, key, InputEvents::ROTATE_RIGHT_PRESSED);
    if (key->keyCode == up || key->keyCode == up2)   SendPressOrRelease(eventMgr, key, InputEvents::MOVE_UP_PRESSED);
    if (key->keyCode == down || key->keyCode == down2) SendPressOrRelease(eventMgr, key, InputEvents::MOVE_DOWN_PRESSED);
    if (key->keyCode == flyModeToggle) SendPressOrRelease(eventMgr, key, InputEvents::TOGGLE_FLYMODE);
    if (key->keyCode == cameraModeToggle && key->eventType == KeyEvent::KeyPressed)
    {
        if (key->keyCode == Qt::Key_Tab)
            key->handled = true; // Suppress Qt from moving the focus to a widget.
        input->ReleaseAllKeys();
        eventMgr->SendEvent("Input", InputEvents::SWITCH_CAMERA_STATE, 0);
    }
    if (key->keyCode == cameraModeTripod && key->eventType == KeyEvent::KeyPressed)
    {
        if (key->keyCode == Qt::Key_T)
            key->handled = true; // Suppress Qt from moving the focus to a widget.
        input->ReleaseAllKeys();
		eventMgr->SendEvent("Input", InputEvents::CAMERA_TRIPOD, 0);
    }
    if (key->keyCode == Qt::Key_Alt && key->eventType == KeyEvent::KeyReleased)
    {
        eventMgr->SendEvent("Input", InputEvents::ALT_LEFTCLICK_REL, 0);
    }
}

void RexMovementInput::HandleMouseEvent(MouseEvent *mouse)
{
    EventManagerPtr eventMgr = framework->GetEventManager();

    // This function handles received input events and translates them to the "traditional"-style
    // Naali input events. New modules should really prefer using an InputContext of their
    // own to read input data, or use the QtInputService API directly.

    // We pass this event struct forward to Naali event tree in most cases above,
    // so fill it here already.
    InputEvents::Movement movement;
    movement.x_.abs_ = mouse->x;
    movement.y_.abs_ = mouse->y;
    movement.z_.abs_ = mouse->z;
    movement.x_.rel_ = mouse->relativeX;
    movement.y_.rel_ = mouse->relativeY;
    movement.z_.rel_ = mouse->relativeZ;

    // This bool remembers whether RexMovementInput hid the mouse cursor - in that case RexMovementInput is responsible for restoring it.
    static bool weControlMouseCursor = false;

    switch(mouse->eventType)
    {
    case MouseEvent::MousePressed:
        if (!mouse->itemUnderMouse)
        {
            // Left mouse button press produces click events on world objects (prims, mostly)
            if (mouse->button == MouseEvent::LeftButton)
                eventMgr->SendEvent("Input", InputEvents::INWORLD_CLICK, &movement);

            if(QApplication::keyboardModifiers() == Qt::AltModifier)
            {
                eventMgr->SendEvent("Input", InputEvents::ALT_LEFTCLICK, &movement);
            }

            // When we start a right mouse button drag, hide the mouse cursor to enter relative mode
            // mouse input.
            if (mouse->button == MouseEvent::RightButton)
                if (framework->Input()->IsMouseCursorVisible())
                {
                    framework->Input()->SetMouseCursorVisible(false);
                    weControlMouseCursor = true;
                }
                else
                    weControlMouseCursor = false;
        }
        break;
    case MouseEvent::MouseReleased:
        // Coming out of a right mouse button drag, restore the mouse cursor to visible state.
        if (mouse->button == MouseEvent::RightButton && weControlMouseCursor)
        {
            framework->Input()->SetMouseCursorVisible(true);
            weControlMouseCursor = false;
        }
        break;
    case MouseEvent::MouseMove:
        if (mouse->IsRightButtonDown() && !framework->Input()->IsMouseCursorVisible()) // When RMB is down, post the Naali MOUSELOOK, which rotates the avatar/camera.
        {
           eventMgr->SendEvent("Input", InputEvents::MOUSELOOK, &movement);
           mouse->handled = true; // Mouse is in RMB mouselook mode, suppress others from getting the move event.
        }
        else if (mouse->IsLeftButtonDown())
            eventMgr->SendEvent("Input", InputEvents::MOUSEDRAG, &movement);
        break;
    case MouseEvent::MouseScroll:
    {
        InputEvents::SingleAxisMovement singleAxis;
        singleAxis.z_.screen_ = 0;
        singleAxis.z_.abs_ = 0;
        singleAxis.z_.rel_ = mouse->relativeZ;
        eventMgr->SendEvent("Input", InputEvents::SCROLL, &singleAxis);

        // Mark this event as handled. Suppresses Qt from getting it. Otherwise mouse-scrolling over an
        // unactivated Qt widget will cause keyboard focus to go to it, which stops all other scene input.
        ///\todo Due to this, if you have a 2D webview/media url window open, you have to first left-click on it
        /// to give keyboard focus to it, after which the mouse wheel will start scrolling the webview window.
        /// Would be nice to somehow detect which windows are interested in mouse scroll events, and give them priority.
        if (!mouse->itemUnderMouse)
            mouse->handled = true;
        break;
    }
    case MouseEvent::MouseDoubleClicked:
    {
        eventMgr->SendEvent("Input", InputEvents::MOUSE_DOUBLECLICK, &movement);
        break;
    }
    }
}

} // ~RexLogic
<commit_msg>Fixed releasing of mouse input when interacting with a QML application that runs on the server.<commit_after>// For conditions of distribution and use, see copyright notice in license.txt

#include "StableHeaders.h"
#include "DebugOperatorNew.h"
#include "MemoryLeakCheck.h"
#include "RexMovementInput.h"
#include "InputEvents.h"
#include "RexTypes.h"
#include "InputAPI.h"
#include "EventManager.h"

#include <QApplication>

namespace RexLogic
{

RexMovementInput::RexMovementInput(Foundation::Framework *framework_)
{
    framework = framework_;

    // Create a new input context that this object will use to fetch the avatar and camera input from.
    input = framework->Input()->RegisterInputContext("RexAvatarInput", 100);

    // To be sure that Qt doesn't play tricks on us and miss a mouse release when we're in FPS mode,
    // grab the mouse movement input over Qt.
    input->SetTakeMouseEventsOverQt(true);

    // Listen on both key and mouse input signals.
    connect(input.get(), SIGNAL(KeyEventReceived(KeyEvent *)), this, SLOT(HandleKeyEvent(KeyEvent *)));
    connect(input.get(), SIGNAL(MouseEventReceived(MouseEvent *)), this, SLOT(HandleMouseEvent(MouseEvent *)));
}

// Either sends an input event press or release, depending on the key event type.
void SendPressOrRelease(EventManagerPtr eventMgr, const KeyEvent *key, int eventID)
{
    // In the AvatarControllable and CameraControllable framework, each input event id that denotes a press
    // also has a corresponding release event, which has an ID one larger than the press event.
    if (key->eventType == KeyEvent::KeyPressed)
        eventMgr->SendEvent("Input", eventID, 0);
    else if (key->eventType == KeyEvent::KeyReleased)
        eventMgr->SendEvent("Input", eventID+1, 0);
}

void RexMovementInput::HandleKeyEvent(KeyEvent *key)
{
    // This function handles received input events and translates them to the "traditional"-style
    // Naali input events. New modules should really prefer using an InputContext of their
    // own to read input data, or use the QtInputService API directly.

    InputAPI *inputService = framework->Input();

    EventManagerPtr eventMgr = framework->GetEventManager();

    ///\todo Accessing the key bindings by string names is a bit silly, since for each key event, this
    /// generates a string compare. Now for every key, we do this for every key we want to act on.
    /// For optimization we can prehash the strings, but that will occur later when we profile performance
    /// issues here.
    PROFILE(RexMovement_HandleKeyEvent);

    ///\bug Qt does not have a separate key code for keypad minus, but instead uses "Qt::Key_Minus | Qt::KeypadModifier". However,
    /// this causes it to fail when showing it in portable text mode in the key bindings editor, i.e.
    /// QKeySequence(Qt::Key_Minus | Qt::KeypadModifier | Qt::ControlModifier).toString(QKeySequence::NativeText); fails.

    // As a workaround, the following two keys are hardcoded to keypad plus. See http://bugreports.qt.nokia.com/browse/QTBUG-2913
    // and http://qt.nokia.com/developer/task-tracker/index_html?method=entry&id=229868

    const QKeySequence zoomIn = inputService->KeyBinding("Avatar.ZoomIn", Qt::Key_Minus | Qt::ControlModifier);
    const QKeySequence zoomOut = inputService->KeyBinding("Avatar.ZoomOut", Qt::Key_Plus | Qt::ControlModifier);

    // For the zoom in and out keys, key repeats trigger continuous actions.
    if ((key->KeyWithModifier() & ~Qt::KeypadModifier) == zoomIn) SendPressOrRelease(eventMgr, key, InputEvents::ZOOM_IN_PRESSED);
    if ((key->KeyWithModifier() & ~Qt::KeypadModifier) == zoomOut) SendPressOrRelease(eventMgr, key, InputEvents::ZOOM_OUT_PRESSED);

    // For the following keys, we ignore all key presses that are repeats.
    if (key->eventType == KeyEvent::KeyPressed && key->keyPressCount > 1)
        return;

    const QKeySequence walkForward =   inputService->KeyBinding("Avatar.WalkForward", Qt::Key_W);
    const QKeySequence walkBackward =  inputService->KeyBinding("Avatar.WalkBack", Qt::Key_S);
    const QKeySequence walkForward2 =  inputService->KeyBinding("Avatar.WalkForward2", Qt::Key_Up);
    const QKeySequence walkBackward2 = inputService->KeyBinding("Avatar.WalkBack2", Qt::Key_Down);
    const QKeySequence strafeLeft =  inputService->KeyBinding("Avatar.StrafeLeft", Qt::Key_A);
    const QKeySequence strafeRight = inputService->KeyBinding("Avatar.StrafeRight", Qt::Key_D);
    const QKeySequence rotateLeft =  inputService->KeyBinding("Avatar.RotateLeft", Qt::Key_Left);
    const QKeySequence rotateRight = inputService->KeyBinding("Avatar.RotateRight", Qt::Key_Right);
    const QKeySequence up =   inputService->KeyBinding("Avatar.Up", Qt::Key_Space); // Jump or fly up, depending on whether in fly mode or walk mode.
    const QKeySequence up2 =   inputService->KeyBinding("Avatar.Up2", Qt::Key_PageUp); // Jump or fly up, depending on whether in fly mode or walk mode.
    const QKeySequence down = inputService->KeyBinding("Avatar.Down", /*Qt::Key_Control*/Qt::Key_C); // Crouch or fly down, depending on whether in fly mode or walk mode.
    const QKeySequence down2 = inputService->KeyBinding("Avatar.Down2", /*Qt::Key_Control*/Qt::Key_PageDown); // Crouch or fly down, depending on whether in fly mode or walk mode.
    const QKeySequence flyModeToggle =    inputService->KeyBinding("Avatar.ToggleFly", Qt::Key_F);
    const QKeySequence cameraModeToggle = inputService->KeyBinding("Avatar.ToggleCameraMode", Qt::Key_Tab);
    const QKeySequence cameraModeTripod = inputService->KeyBinding("Avatar.TripodCameraMode", Qt::Key_T);

    // The following code defines the keyboard actions that are available for the avatar/freelookcamera system.
    if (key->keyCode == walkForward || key->keyCode == walkForward2) SendPressOrRelease(eventMgr, key, InputEvents::MOVE_FORWARD_PRESSED);
    if (key->keyCode == walkBackward || key->keyCode == walkBackward2) SendPressOrRelease(eventMgr, key, InputEvents::MOVE_BACK_PRESSED);
    if (key->keyCode == strafeLeft) SendPressOrRelease(eventMgr, key, InputEvents::MOVE_LEFT_PRESSED);
    if (key->keyCode == strafeRight) SendPressOrRelease(eventMgr, key, InputEvents::MOVE_RIGHT_PRESSED);
    if (key->keyCode == rotateLeft)  SendPressOrRelease(eventMgr, key, InputEvents::ROTATE_LEFT_PRESSED);
    if (key->keyCode == rotateRight) SendPressOrRelease(eventMgr, key, InputEvents::ROTATE_RIGHT_PRESSED);
    if (key->keyCode == up || key->keyCode == up2)   SendPressOrRelease(eventMgr, key, InputEvents::MOVE_UP_PRESSED);
    if (key->keyCode == down || key->keyCode == down2) SendPressOrRelease(eventMgr, key, InputEvents::MOVE_DOWN_PRESSED);
    if (key->keyCode == flyModeToggle) SendPressOrRelease(eventMgr, key, InputEvents::TOGGLE_FLYMODE);
    if (key->keyCode == cameraModeToggle && key->eventType == KeyEvent::KeyPressed)
    {
        if (key->keyCode == Qt::Key_Tab)
            key->handled = true; // Suppress Qt from moving the focus to a widget.
        input->ReleaseAllKeys();
        eventMgr->SendEvent("Input", InputEvents::SWITCH_CAMERA_STATE, 0);
    }
    if (key->keyCode == cameraModeTripod && key->eventType == KeyEvent::KeyPressed)
    {
        if (key->keyCode == Qt::Key_T)
            key->handled = true; // Suppress Qt from moving the focus to a widget.
        input->ReleaseAllKeys();
		eventMgr->SendEvent("Input", InputEvents::CAMERA_TRIPOD, 0);
    }
    if (key->keyCode == Qt::Key_Alt && key->eventType == KeyEvent::KeyReleased)
    {
        eventMgr->SendEvent("Input", InputEvents::ALT_LEFTCLICK_REL, 0);
    }
}

void RexMovementInput::HandleMouseEvent(MouseEvent *mouse)
{
    EventManagerPtr eventMgr = framework->GetEventManager();

    // This function handles received input events and translates them to the "traditional"-style
    // Naali input events. New modules should really prefer using an InputContext of their
    // own to read input data, or use the QtInputService API directly.

    // We pass this event struct forward to Naali event tree in most cases above,
    // so fill it here already.
    InputEvents::Movement movement;
    movement.x_.abs_ = mouse->x;
    movement.y_.abs_ = mouse->y;
    movement.z_.abs_ = mouse->z;
    movement.x_.rel_ = mouse->relativeX;
    movement.y_.rel_ = mouse->relativeY;
    movement.z_.rel_ = mouse->relativeZ;

    // This bool remembers whether RexMovementInput hid the mouse cursor - in that case RexMovementInput is responsible for restoring it.
    static bool weControlMouseCursor = false;

    switch(mouse->eventType)
    {
    case MouseEvent::MousePressed:
        if (!mouse->itemUnderMouse)
        {
            // Left mouse button press produces click events on world objects (prims, mostly)
            if (mouse->button == MouseEvent::LeftButton)
                eventMgr->SendEvent("Input", InputEvents::INWORLD_CLICK, &movement);

            if(QApplication::keyboardModifiers() == Qt::AltModifier)
            {
                eventMgr->SendEvent("Input", InputEvents::ALT_LEFTCLICK, &movement);
            }

            // When we start a right mouse button drag, hide the mouse cursor to enter relative mode
            // mouse input.
            if (mouse->button == MouseEvent::RightButton)
                if (framework->Input()->IsMouseCursorVisible())
                {
                    framework->Input()->SetMouseCursorVisible(false);
                    weControlMouseCursor = true;
                }
                else
                    weControlMouseCursor = false;
        }
        break;
    case MouseEvent::MouseReleased:
        // Coming out of a right mouse button drag, restore the mouse cursor to visible state.
        if (mouse->button == MouseEvent::RightButton && weControlMouseCursor)
        {
            framework->Input()->SetMouseCursorVisible(true);
            weControlMouseCursor = false;
        }
        break;
    case MouseEvent::MouseMove:
        if (weControlMouseCursor && !mouse->IsRightButtonDown() && !framework->Input()->IsMouseCursorVisible())
        {
            framework->Input()->SetMouseCursorVisible(true);
            weControlMouseCursor = false;
        }
        if (mouse->IsRightButtonDown() && !framework->Input()->IsMouseCursorVisible()) // When RMB is down, post the Naali MOUSELOOK, which rotates the avatar/camera.
        {
           eventMgr->SendEvent("Input", InputEvents::MOUSELOOK, &movement);
           mouse->handled = true; // Mouse is in RMB mouselook mode, suppress others from getting the move event.
        }
        else if (mouse->IsLeftButtonDown())
            eventMgr->SendEvent("Input", InputEvents::MOUSEDRAG, &movement);
        break;
    case MouseEvent::MouseScroll:
    {
        InputEvents::SingleAxisMovement singleAxis;
        singleAxis.z_.screen_ = 0;
        singleAxis.z_.abs_ = 0;
        singleAxis.z_.rel_ = mouse->relativeZ;
        eventMgr->SendEvent("Input", InputEvents::SCROLL, &singleAxis);

        // Mark this event as handled. Suppresses Qt from getting it. Otherwise mouse-scrolling over an
        // unactivated Qt widget will cause keyboard focus to go to it, which stops all other scene input.
        ///\todo Due to this, if you have a 2D webview/media url window open, you have to first left-click on it
        /// to give keyboard focus to it, after which the mouse wheel will start scrolling the webview window.
        /// Would be nice to somehow detect which windows are interested in mouse scroll events, and give them priority.
        if (!mouse->itemUnderMouse)
            mouse->handled = true;
        break;
    }
    case MouseEvent::MouseDoubleClicked:
    {
        eventMgr->SendEvent("Input", InputEvents::MOUSE_DOUBLECLICK, &movement);
        break;
    }
    }
}

} // ~RexLogic
<|endoftext|>


<commit_before>#include <Font.h>
#include <Utils.h>
#include <Surface.h>
#include <map>

#if defined(HX_WINDOWS) || defined(HX_MACOS) || defined(HX_LINUX)
// Include neko glue....
#define NEKO_COMPATIBLE
#endif
#include <nme/NmeCffi.h>
#include <Utils.h>

namespace nme
{

bool gNmeNativeFonts = true;

// --- CFFI font delegates to haxe to get the glyphs -----

static int _id_bold;
static int _id_name;
static int _id_italic;
static int _id_height;
static int _id_ascent;
static int _id_descent;
static int _id_isRGB;

static int _id_getGlyphInfo;
static int _id_renderGlyphInternal;
static int _id_width;
static int _id_advance;
static int _id_offsetX;
static int _id_offsetY;

class CFFIFont : public FontFace
{
public:
   CFFIFont(value inHandle) : mHandle(inHandle)
   {
      mAscent = val_number( val_field(inHandle, _id_ascent) );
      mDescent = val_number( val_field(inHandle, _id_descent) );
      mHeight = val_number( val_field(inHandle, _id_height) );
      mIsRGB = val_bool( val_field(inHandle, _id_isRGB) );
   }

   bool WantRGB() { return true; }

   bool GetGlyphInfo(int inChar, int &outW, int &outH, int &outAdvance,
                           int &outOx, int &outOy)
   {
      value result = val_ocall1( mHandle.get(), _id_getGlyphInfo, alloc_int(inChar) );
      if (!val_is_null(result))
      {
         outW = val_number( val_field(result, _id_width) );
         outH = val_number( val_field(result, _id_height) );
         outAdvance = (int)(val_number( val_field(result, _id_advance) )) << 6;
         outOx = val_number( val_field(result, _id_offsetX) );
         outOy = val_number( val_field(result, _id_offsetY) );
         return true;
      }
      return false;
   }

   void RenderGlyph(int inChar,const RenderTarget &outTarget)
   {
      value glyphBmp = val_ocall1(mHandle.get(), _id_renderGlyphInternal, alloc_int(inChar) );
      Surface *surface = 0;
      if (AbstractToObject(glyphBmp,surface) )
      {
         surface->BlitTo(outTarget, Rect(0,0,surface->Width(), surface->Height()),
                         outTarget.mRect.x, outTarget.mRect.y, bmNormal, 0 );
      }
   }

   void UpdateMetrics(TextLineMetrics &ioMetrics)
   {
      ioMetrics.ascent = std::max( ioMetrics.ascent, (float)mAscent);
      ioMetrics.descent = std::max( ioMetrics.descent, (float)mDescent);
      ioMetrics.height = std::max( ioMetrics.height, (float)mHeight);
   }
   int Height()
   {
      return mHeight;
   }

   AutoGCRoot mHandle;
   float mAscent;
   float mDescent;
   int   mHeight;
   bool  mIsRGB;
};

AutoGCRoot *sCFFIFontFactory = 0;

value nme_font_set_factory(value inFactory)
{
   if (!sCFFIFontFactory)
   {
      sCFFIFontFactory = new AutoGCRoot(inFactory);
      _id_bold = val_id("bold");
      _id_name = val_id("name");
      _id_italic = val_id("italic");
      _id_height = val_id("height");
      _id_ascent = val_id("ascent");
      _id_descent = val_id("descent");
      _id_isRGB = val_id("isRGB");

      _id_getGlyphInfo = val_id("getGlyphInfo");
      _id_renderGlyphInternal = val_id("renderGlyphInternal");
      _id_width = val_id("width");
      _id_advance = val_id("advance");
      _id_offsetX = val_id("offset_x");
      _id_offsetY = val_id("offsetY");
   }
   return alloc_null();
}

DEFINE_PRIM(nme_font_set_factory,1)

FontFace *FontFace::CreateCFFIFont(const TextFormat &inFormat,double inScale)
{
   if (!sCFFIFontFactory)
      return 0;

   value format = alloc_empty_object();
   alloc_field(format, _id_bold, alloc_bool( inFormat.bold ) );
   alloc_field(format, _id_name, alloc_wstring( inFormat.font.Get().c_str() ) );
   alloc_field(format, _id_italic, alloc_bool( inFormat.italic ) );
   alloc_field(format, _id_height, alloc_float( (int )(inFormat.size*inScale + 0.5) ) );

   value result = val_call1( sCFFIFontFactory->get(), format );

   if (val_is_null(result))
      return 0;

   return new CFFIFont(result);
}

// --- Font ----------------------------------------------------------------

Font::Font(FontFace *inFace, int inPixelHeight, bool inInitRef) :
     Object(inInitRef), mFace(inFace), mPixelHeight(inPixelHeight)
{
   mCurrentSheet = -1;
}

Font::~Font()
{
   for(int i=0;i<mSheets.size();i++)
      mSheets[i]->DecRef();
   if (mFace) delete mFace;
}

Tile Font::GetGlyph(int inCharacter,int &outAdvance)
{
   bool use_default = false;
   Glyph &glyph = inCharacter < 128 ? mGlyph[inCharacter] : mExtendedGlyph[inCharacter];
   if (glyph.sheet<0)
   {
      int gw,gh,adv,ox,oy;
      bool ok = mFace->GetGlyphInfo(inCharacter,gw,gh,adv,ox,oy);
      if (!ok)
      {
         if (inCharacter=='?')
         {
            gw = mPixelHeight;
            gh = mPixelHeight;
            ox = oy = 0;
            adv = mPixelHeight<<6;
            use_default = true;
         }
         else
         {
            Tile result = GetGlyph('?',outAdvance);
            glyph = mGlyph['?'];
            return result;
         }
      }

      int orig_w = gw;
      int orig_h = gh;

      while(1)
      {
         // Allocate new sheet?
         if (mCurrentSheet<0)
         {
            int rows = mPixelHeight > 127 ? 1 : mPixelHeight > 63 ? 2 : mPixelHeight>31 ? 4 : 5;
            int h = 4;
            while(h<orig_h*rows)
               h*=2;
            int w = h;
            while(w<orig_w)
               w*=2;
            PixelFormat pf = mFace->WantRGB() ? pfARGB : pfAlpha;
            Tilesheet *sheet = new Tilesheet(w,h,pf,true);
            sheet->GetSurface().Clear(0);
            mCurrentSheet = mSheets.size();
            mSheets.push_back(sheet);
         }

         int tid = mSheets[mCurrentSheet]->AllocRect(gw,gh,ox,oy,true);
         if (tid>=0)
         {
            glyph.sheet = mCurrentSheet;
            glyph.tile = tid;
            glyph.advance = adv;
            break;
         }

         // Need new sheet...
         mCurrentSheet = -1;
      }
      // Now fill rect...
      Tile tile = mSheets[glyph.sheet]->GetTile(glyph.tile);
      // SharpenText(bitmap);
      RenderTarget target = tile.mSurface->BeginRender(tile.mRect);
      if (use_default)
      {
         for(int y=0; y<target.mRect.h; y++)
         {
            uint8  *dest = (uint8 *)target.Row(y + target.mRect.y) + target.mRect.x;
            for(int x=0; x<target.mRect.w; x++)
               *dest++ = 0xff;
         }
      }
      else
         mFace->RenderGlyph(inCharacter,target);

      tile.mSurface->EndRender();
      outAdvance = glyph.advance;
      return tile;
   }

   outAdvance = glyph.advance;
   return mSheets[glyph.sheet]->GetTile(glyph.tile);
}

void  Font::UpdateMetrics(TextLineMetrics &ioMetrics)
{
   if (mFace)
      mFace->UpdateMetrics(ioMetrics);
}

int Font::Height()
{
   if (!mFace) return 12;
   return mFace->Height();
}

// --- CharGroup ---------------------------------------------

void  CharGroup::UpdateMetrics(TextLineMetrics &ioMetrics)
{
   if (mFont)
      mFont->UpdateMetrics(ioMetrics);
}

double CharGroup::Height(double inFontToLocal)
  { return inFontToLocal * (mFont ? mFont->Height() : 12); }

// --- Create font from TextFormat ----------------------------------------------------

struct FontInfo
{
   FontInfo(const TextFormat &inFormat,double inScale)
   {
      allowNative = gNmeNativeFonts;
      name = inFormat.font;
      height = (int )(inFormat.size*inScale + 0.5);
      flags = 0;
      if (inFormat.bold)
         flags |= ffBold;
      if (inFormat.italic)
         flags |= ffItalic;
      if (inFormat.underline)
         flags |= ffUnderline;
   }

   bool operator<(const FontInfo &inRHS) const
   {
      if (allowNative != inRHS.allowNative) return allowNative;
      if (name < inRHS.name) return true;
      if (name > inRHS.name) return false;
      if (height < inRHS.height) return true;
      if (height > inRHS.height) return false;
      return flags < inRHS.flags;
   }
   WString      name;
   bool         allowNative;
   int          height;
   unsigned int flags;
};

typedef std::map<FontInfo, Font *> FontMap;
FontMap sgFontMap;
typedef std::map<std::string, AutoGCRoot *> FontBytesMap;
FontBytesMap sgRegisteredFonts;

Font *Font::Create(TextFormat &inFormat,double inScale,bool inNative,bool inInitRef)
{
   bool native = inNative && gNmeNativeFonts;

   FontInfo info(inFormat,inScale);

   Font *font = 0;
   FontMap::iterator fit = sgFontMap.find(info);
   if (fit!=sgFontMap.end())
   {
      font = fit->second;
      if (inInitRef)
         font->IncRef();
      return font;
   }

   std::string fontName(WideToUTF8(inFormat.font));
   std::string seekName = fontName;

   FontFace *face = 0;

   for(int pass=0;pass<2;pass++)
   {
      if (pass==1)
      {
         if (!strcasecmp(fontName.c_str(),"times.ttf") ||
             !strcasecmp(fontName.c_str(),"times"))
            seekName = "_serif";
         else if (!strcasecmp(fontName.c_str(),"arial.ttf") ||
                  !strcasecmp(fontName.c_str(),"arial") ||
                  !strcasecmp(fontName.c_str(),"sans-serif") )
            seekName = "_sans";
         else if (!strcasecmp(fontName.c_str(),"_typewriter") ||
                  !strcasecmp(fontName.c_str(),"courier.ttf") ||
                  !strcasecmp(fontName.c_str(),"courier"))
            seekName = "_monospace";
         else
            break;
      }

      AutoGCRoot *bytes = 0;
      FontBytesMap::iterator fbit = sgRegisteredFonts.find(seekName);

      if (fbit!=sgRegisteredFonts.end())
      {
         bytes = fbit->second;
         //printf("Registered!\n");
      }

      if (!bytes)
      {
         ByteArray resource(seekName.c_str());
         if (resource.Ok())
         {
            sgRegisteredFonts[fontName] = new AutoGCRoot( resource.mValue );
            fbit = sgRegisteredFonts.find(fontName);
            bytes = fbit->second;
          //  printf("Found!\n");
         }
         //else
         //   printf("No resource\n");
      }

      if (bytes)
        face = FontFace::CreateFreeType(inFormat,inScale,bytes);

      if (pass>0 || face)
         break;

      if ( (face = FontFace::CreateCFFIFont(inFormat,inScale)) )
         break;

      if (native)
         if ( (face = FontFace::CreateNative(inFormat,inScale)) )
            break;

      if ( (face = FontFace::CreateFreeType(inFormat,inScale,NULL)) )
         break;

      if (!native)
         if ( (face = FontFace::CreateNative(inFormat,inScale)) )
            break;
   }

   if (!face)
       return 0;

   font =  new Font(face,info.height,inInitRef);
   // Store for Ron ...
   font->IncRef();
   sgFontMap[info] = font;

   // Clear out any old fonts
   for (FontMap::iterator fit = sgFontMap.begin(); fit!=sgFontMap.end();)
   {
      if (fit->second->GetRefCount()==1)
      {
         fit->second->DecRef();
         FontMap::iterator next = fit;
         next++;
         sgFontMap.erase(fit);
         fit = next;
      }
      else
         ++fit;
   }

   return font;
}

value nme_font_register_font(value inFontName, value inBytes)
{
   AutoGCRoot *bytes = new AutoGCRoot(inBytes);
   sgRegisteredFonts[std::string(val_string(inFontName))] = bytes;
   return alloc_null();
}
DEFINE_PRIM(nme_font_register_font,2)

value nme_font_get_use_native()
{
   return alloc_bool(gNmeNativeFonts);
}
DEFINE_PRIM(nme_font_get_use_native,0)

value nme_font_set_use_native(value inUse)
{
   gNmeNativeFonts = val_bool(inUse);
   return inUse;
}
DEFINE_PRIM(nme_font_set_use_native,1)

} // end namespace nme

<commit_msg>Look for font resources first when resolving by name<commit_after>#include <Font.h>
#include <Utils.h>
#include <Surface.h>
#include <map>

#if defined(HX_WINDOWS) || defined(HX_MACOS) || defined(HX_LINUX)
// Include neko glue....
#define NEKO_COMPATIBLE
#endif
#include <nme/NmeCffi.h>
#include <Utils.h>

namespace nme
{

bool gNmeNativeFonts = true;

// --- CFFI font delegates to haxe to get the glyphs -----

static int _id_bold;
static int _id_name;
static int _id_italic;
static int _id_height;
static int _id_ascent;
static int _id_descent;
static int _id_isRGB;

static int _id_getGlyphInfo;
static int _id_renderGlyphInternal;
static int _id_width;
static int _id_advance;
static int _id_offsetX;
static int _id_offsetY;

class CFFIFont : public FontFace
{
public:
   CFFIFont(value inHandle) : mHandle(inHandle)
   {
      mAscent = val_number( val_field(inHandle, _id_ascent) );
      mDescent = val_number( val_field(inHandle, _id_descent) );
      mHeight = val_number( val_field(inHandle, _id_height) );
      mIsRGB = val_bool( val_field(inHandle, _id_isRGB) );
   }

   bool WantRGB() { return true; }

   bool GetGlyphInfo(int inChar, int &outW, int &outH, int &outAdvance,
                           int &outOx, int &outOy)
   {
      value result = val_ocall1( mHandle.get(), _id_getGlyphInfo, alloc_int(inChar) );
      if (!val_is_null(result))
      {
         outW = val_number( val_field(result, _id_width) );
         outH = val_number( val_field(result, _id_height) );
         outAdvance = (int)(val_number( val_field(result, _id_advance) )) << 6;
         outOx = val_number( val_field(result, _id_offsetX) );
         outOy = val_number( val_field(result, _id_offsetY) );
         return true;
      }
      return false;
   }

   void RenderGlyph(int inChar,const RenderTarget &outTarget)
   {
      value glyphBmp = val_ocall1(mHandle.get(), _id_renderGlyphInternal, alloc_int(inChar) );
      Surface *surface = 0;
      if (AbstractToObject(glyphBmp,surface) )
      {
         surface->BlitTo(outTarget, Rect(0,0,surface->Width(), surface->Height()),
                         outTarget.mRect.x, outTarget.mRect.y, bmNormal, 0 );
      }
   }

   void UpdateMetrics(TextLineMetrics &ioMetrics)
   {
      ioMetrics.ascent = std::max( ioMetrics.ascent, (float)mAscent);
      ioMetrics.descent = std::max( ioMetrics.descent, (float)mDescent);
      ioMetrics.height = std::max( ioMetrics.height, (float)mHeight);
   }
   int Height()
   {
      return mHeight;
   }

   AutoGCRoot mHandle;
   float mAscent;
   float mDescent;
   int   mHeight;
   bool  mIsRGB;
};

AutoGCRoot *sCFFIFontFactory = 0;

value nme_font_set_factory(value inFactory)
{
   if (!sCFFIFontFactory)
   {
      sCFFIFontFactory = new AutoGCRoot(inFactory);
      _id_bold = val_id("bold");
      _id_name = val_id("name");
      _id_italic = val_id("italic");
      _id_height = val_id("height");
      _id_ascent = val_id("ascent");
      _id_descent = val_id("descent");
      _id_isRGB = val_id("isRGB");

      _id_getGlyphInfo = val_id("getGlyphInfo");
      _id_renderGlyphInternal = val_id("renderGlyphInternal");
      _id_width = val_id("width");
      _id_advance = val_id("advance");
      _id_offsetX = val_id("offset_x");
      _id_offsetY = val_id("offsetY");
   }
   return alloc_null();
}

DEFINE_PRIM(nme_font_set_factory,1)

FontFace *FontFace::CreateCFFIFont(const TextFormat &inFormat,double inScale)
{
   if (!sCFFIFontFactory)
      return 0;

   value format = alloc_empty_object();
   alloc_field(format, _id_bold, alloc_bool( inFormat.bold ) );
   alloc_field(format, _id_name, alloc_wstring( inFormat.font.Get().c_str() ) );
   alloc_field(format, _id_italic, alloc_bool( inFormat.italic ) );
   alloc_field(format, _id_height, alloc_float( (int )(inFormat.size*inScale + 0.5) ) );

   value result = val_call1( sCFFIFontFactory->get(), format );

   if (val_is_null(result))
      return 0;

   return new CFFIFont(result);
}

// --- Font ----------------------------------------------------------------

Font::Font(FontFace *inFace, int inPixelHeight, bool inInitRef) :
     Object(inInitRef), mFace(inFace), mPixelHeight(inPixelHeight)
{
   mCurrentSheet = -1;
}

Font::~Font()
{
   for(int i=0;i<mSheets.size();i++)
      mSheets[i]->DecRef();
   if (mFace) delete mFace;
}

Tile Font::GetGlyph(int inCharacter,int &outAdvance)
{
   bool use_default = false;
   Glyph &glyph = inCharacter < 128 ? mGlyph[inCharacter] : mExtendedGlyph[inCharacter];
   if (glyph.sheet<0)
   {
      int gw,gh,adv,ox,oy;
      bool ok = mFace->GetGlyphInfo(inCharacter,gw,gh,adv,ox,oy);
      if (!ok)
      {
         if (inCharacter=='?')
         {
            gw = mPixelHeight;
            gh = mPixelHeight;
            ox = oy = 0;
            adv = mPixelHeight<<6;
            use_default = true;
         }
         else
         {
            Tile result = GetGlyph('?',outAdvance);
            glyph = mGlyph['?'];
            return result;
         }
      }

      int orig_w = gw;
      int orig_h = gh;

      while(1)
      {
         // Allocate new sheet?
         if (mCurrentSheet<0)
         {
            int rows = mPixelHeight > 127 ? 1 : mPixelHeight > 63 ? 2 : mPixelHeight>31 ? 4 : 5;
            int h = 4;
            while(h<orig_h*rows)
               h*=2;
            int w = h;
            while(w<orig_w)
               w*=2;
            PixelFormat pf = mFace->WantRGB() ? pfARGB : pfAlpha;
            Tilesheet *sheet = new Tilesheet(w,h,pf,true);
            sheet->GetSurface().Clear(0);
            mCurrentSheet = mSheets.size();
            mSheets.push_back(sheet);
         }

         int tid = mSheets[mCurrentSheet]->AllocRect(gw,gh,ox,oy,true);
         if (tid>=0)
         {
            glyph.sheet = mCurrentSheet;
            glyph.tile = tid;
            glyph.advance = adv;
            break;
         }

         // Need new sheet...
         mCurrentSheet = -1;
      }
      // Now fill rect...
      Tile tile = mSheets[glyph.sheet]->GetTile(glyph.tile);
      // SharpenText(bitmap);
      RenderTarget target = tile.mSurface->BeginRender(tile.mRect);
      if (use_default)
      {
         for(int y=0; y<target.mRect.h; y++)
         {
            uint8  *dest = (uint8 *)target.Row(y + target.mRect.y) + target.mRect.x;
            for(int x=0; x<target.mRect.w; x++)
               *dest++ = 0xff;
         }
      }
      else
         mFace->RenderGlyph(inCharacter,target);

      tile.mSurface->EndRender();
      outAdvance = glyph.advance;
      return tile;
   }

   outAdvance = glyph.advance;
   return mSheets[glyph.sheet]->GetTile(glyph.tile);
}

void  Font::UpdateMetrics(TextLineMetrics &ioMetrics)
{
   if (mFace)
      mFace->UpdateMetrics(ioMetrics);
}

int Font::Height()
{
   if (!mFace) return 12;
   return mFace->Height();
}

// --- CharGroup ---------------------------------------------

void  CharGroup::UpdateMetrics(TextLineMetrics &ioMetrics)
{
   if (mFont)
      mFont->UpdateMetrics(ioMetrics);
}

double CharGroup::Height(double inFontToLocal)
  { return inFontToLocal * (mFont ? mFont->Height() : 12); }

// --- Create font from TextFormat ----------------------------------------------------

struct FontInfo
{
   FontInfo(const TextFormat &inFormat,double inScale)
   {
      allowNative = gNmeNativeFonts;
      name = inFormat.font;
      height = (int )(inFormat.size*inScale + 0.5);
      flags = 0;
      if (inFormat.bold)
         flags |= ffBold;
      if (inFormat.italic)
         flags |= ffItalic;
      if (inFormat.underline)
         flags |= ffUnderline;
   }

   bool operator<(const FontInfo &inRHS) const
   {
      if (allowNative != inRHS.allowNative) return allowNative;
      if (name < inRHS.name) return true;
      if (name > inRHS.name) return false;
      if (height < inRHS.height) return true;
      if (height > inRHS.height) return false;
      return flags < inRHS.flags;
   }
   WString      name;
   bool         allowNative;
   int          height;
   unsigned int flags;
};

typedef std::map<FontInfo, Font *> FontMap;
FontMap sgFontMap;
typedef std::map<std::string, AutoGCRoot *> FontBytesMap;
FontBytesMap sgRegisteredFonts;

Font *Font::Create(TextFormat &inFormat,double inScale,bool inNative,bool inInitRef)
{
   bool native = inNative && gNmeNativeFonts;

   FontInfo info(inFormat,inScale);

   Font *font = 0;
   FontMap::iterator fit = sgFontMap.find(info);
   if (fit!=sgFontMap.end())
   {
      font = fit->second;
      if (inInitRef)
         font->IncRef();
      return font;
   }

   std::string fontName(WideToUTF8(inFormat.font));
   std::string seekName = fontName;

   FontFace *face = 0;

   for(int pass=0;pass<2;pass++)
   {
      if (pass==1)
      {
         if (!strcasecmp(fontName.c_str(),"times.ttf") ||
             !strcasecmp(fontName.c_str(),"times"))
            seekName = "_serif";
         else if (!strcasecmp(fontName.c_str(),"arial.ttf") ||
                  !strcasecmp(fontName.c_str(),"arial") ||
                  !strcasecmp(fontName.c_str(),"sans-serif") )
            seekName = "_sans";
         else if (!strcasecmp(fontName.c_str(),"_typewriter") ||
                  !strcasecmp(fontName.c_str(),"courier.ttf") ||
                  !strcasecmp(fontName.c_str(),"courier"))
            seekName = "_monospace";
         else
            break;
      }

      AutoGCRoot *bytes = 0;
      FontBytesMap::iterator fbit = sgRegisteredFonts.find(seekName);

      if (fbit!=sgRegisteredFonts.end())
      {
         bytes = fbit->second;
         //printf("Registered!\n");
      }

      if (!bytes)
      {
         ByteArray resource(seekName.c_str());
         if (resource.Ok())
         {
            sgRegisteredFonts[fontName] = new AutoGCRoot( resource.mValue );
            fbit = sgRegisteredFonts.find(fontName);
            bytes = fbit->second;
          //  printf("Found!\n");
         }
         //else
         //   printf("No resource\n");
      }

      if (bytes)
      {
         face = FontFace::CreateFreeType(inFormat,inScale,bytes);
         if (face)
            break;
      }
   }

   if (!face)
      face = FontFace::CreateCFFIFont(inFormat,inScale);

   if (native && !face)
      face = FontFace::CreateNative(inFormat,inScale);

   if (!face)
      face = FontFace::CreateFreeType(inFormat,inScale,NULL);

   if (!native && face)
      face = FontFace::CreateNative(inFormat,inScale);

   if (!face)
       return 0;

   font =  new Font(face,info.height,inInitRef);
   // Store for Ron ...
   font->IncRef();
   sgFontMap[info] = font;

   // Clear out any old fonts
   for (FontMap::iterator fit = sgFontMap.begin(); fit!=sgFontMap.end();)
   {
      if (fit->second->GetRefCount()==1)
      {
         fit->second->DecRef();
         FontMap::iterator next = fit;
         next++;
         sgFontMap.erase(fit);
         fit = next;
      }
      else
         ++fit;
   }

   return font;
}

value nme_font_register_font(value inFontName, value inBytes)
{
   AutoGCRoot *bytes = new AutoGCRoot(inBytes);
   sgRegisteredFonts[std::string(val_string(inFontName))] = bytes;
   return alloc_null();
}
DEFINE_PRIM(nme_font_register_font,2)

value nme_font_get_use_native()
{
   return alloc_bool(gNmeNativeFonts);
}
DEFINE_PRIM(nme_font_get_use_native,0)

value nme_font_set_use_native(value inUse)
{
   gNmeNativeFonts = val_bool(inUse);
   return inUse;
}
DEFINE_PRIM(nme_font_set_use_native,1)

} // end namespace nme

<|endoftext|>


<commit_before>#ifndef ALX_TIMER_HPP
#define ALX_TIMER_HPP

#include "EventSource.hpp"

namespace alx {

/**
    Shared-based wrapper around ALLEGRO_TIMER.
 */
class Timer : public Shared<ALLEGRO_TIMER> {
public:
    /**
        constructor from Allegro object.
        @param object allegro object.
        @param managed if true, the object will be deleted automatically when its last reference will be deleted.
     */
    Timer(ALLEGRO_TIMER *object, bool managed = true) : Shared(object, managed, al_destroy_timer, [](ALLEGRO_TIMER *){}) {
    }

    /**
        Creates a timer.
        @param speedSecs speed, in seconds.
     */
    Timer(double speedSecs) : Shared(al_create_timer(speedSecs), al_destroy_timer) {
    }

    /**
        Checks if the timer is started.
        @return true if started.
     */
    bool isStarted() const {
        return al_get_timer_started(get());
    }

    /**
        Returns the timer count.
        @return the timer count.
     */
    int64_t getCount() const {
        return al_get_timer_count(get());
    }

    /**
        Returns the timer speed.
        @return the timer speed in seconds.
     */
    double getSpeed() const {
        return al_get_timer_speed(get());
    }

    /**
        Returns the event source of the timer.
        @return the event source of the timer.
     */
    EventSource getEventSource() const {
        return al_get_timer_event_source(get());
    }

    /**
        Returns the event source of the timer.
        @return the event source of the timer.
     */
    operator EventSource () const {
        return getEventSource();
    }

    /**
        Start the timer.
     */
    void start() {
        al_start_timer(get());
    }

    /**
        Stops the timer.
     */
    void stop() {
        al_stop_timer(get());
    }

    /**
        Sets the timer count.
        @param count new count.
     */
    void setCount(int64_t count) {
        al_set_timer_count(get(), count);
    }

    /**
        Adds the given count to the timer.
        @param count count to add.
     */
    void addCount(int64_t count) {
        al_add_timer_count(get(), count);
    }

    /**
        Sets the timer speed.
        @param speedSecs the timer speed, in seconds.
     */
    void setSpeed(double speedSecs) {
        al_set_timer_speed(get(), speedSecs);
    }
};

} //namespace alx

#endif //ALX_TIMER_HPP
<commit_msg>Fixed timer getEventSource.<commit_after>#ifndef ALX_TIMER_HPP
#define ALX_TIMER_HPP

#include "EventSource.hpp"

namespace alx {

/**
    Shared-based wrapper around ALLEGRO_TIMER.
 */
class Timer : public Shared<ALLEGRO_TIMER> {
public:
    /**
        constructor from Allegro object.
        @param object allegro object.
        @param managed if true, the object will be deleted automatically when its last reference will be deleted.
     */
    Timer(ALLEGRO_TIMER *object, bool managed = true) : Shared(object, managed, al_destroy_timer, [](ALLEGRO_TIMER *){}) {
    }

    /**
        Creates a timer.
        @param speedSecs speed, in seconds.
     */
    Timer(double speedSecs) : Shared(al_create_timer(speedSecs), al_destroy_timer) {
    }

    /**
        Checks if the timer is started.
        @return true if started.
     */
    bool isStarted() const {
        return al_get_timer_started(get());
    }

    /**
        Returns the timer count.
        @return the timer count.
     */
    int64_t getCount() const {
        return al_get_timer_count(get());
    }

    /**
        Returns the timer speed.
        @return the timer speed in seconds.
     */
    double getSpeed() const {
        return al_get_timer_speed(get());
    }

    /**
        Returns the event source of the timer.
        @return the event source of the timer.
     */
    EventSource getEventSource() const {
        return EventSource(al_get_timer_event_source(get()), false);
    }

    /**
        Returns the event source of the timer.
        @return the event source of the timer.
     */
    operator EventSource () const {
        return getEventSource();
    }

    /**
        Start the timer.
     */
    void start() {
        al_start_timer(get());
    }

    /**
        Stops the timer.
     */
    void stop() {
        al_stop_timer(get());
    }

    /**
        Sets the timer count.
        @param count new count.
     */
    void setCount(int64_t count) {
        al_set_timer_count(get(), count);
    }

    /**
        Adds the given count to the timer.
        @param count count to add.
     */
    void addCount(int64_t count) {
        al_add_timer_count(get(), count);
    }

    /**
        Sets the timer speed.
        @param speedSecs the timer speed, in seconds.
     */
    void setSpeed(double speedSecs) {
        al_set_timer_speed(get(), speedSecs);
    }
};

} //namespace alx

#endif //ALX_TIMER_HPP
<|endoftext|>


<commit_before>#include  "cook_torrance.h"

CookTorrance::CookTorrance( double m,
                            SurfaceSampler::SurfaceSamplerUniquePtr surface_sampler,
                            Fresnel::FresnelUniquePtr fresnel ) :
        BxDF( std::move( surface_sampler ) ),
        m_{ m },
        fresnel_( std::move( fresnel ) )
{}

glm::dvec3 CookTorrance::fr( const glm::dvec3 &w_i,
                             const glm::dvec3 &w_r ) const
{
    // Since the microfacet normal may not be aligned with the average surface normal,
    // it can be the case that the generated w_r might point into the average surface
    // (i.e., it's 'y' coordinate may be negative).
    // In this case, it is not possible to sample the Cook-Torrance BRDF because it is
    // defined only for hemisphere above the intersection point. Thus, in these cases,
    // teh BRDF simply return 0.
    // -- That problem, and teh adopted solution, are mentioned in the Eric Veach's
    // thesis, in the Secton about shading normals.
    if ( w_r.y < 0.0 )
        return glm::dvec3{ 0.0, 0.0, 0.0 };

    glm::dvec3 h = glm::normalize( w_i + w_r );
    double nh = std::abs( h.y );
    double nv = std::abs( w_i.y );
    double nl = std::abs( w_r.y );
    double vh = std::abs( glm::dot( w_i, h ) );
    double lh = std::abs( glm::dot( w_r, h ) );

    // Beckmann normal distribution function
    double nh_2 = nh * nh;
    double m_2 = m_ * m_;
    double d1 = 1.0 / ( M_PI * m_2 * pow( nh, 4.0 ) );
    double d2 = ( nh_2 - 1.0 ) / ( m_2 * nh_2 );
    double d = d1 * exp( d2 );

    // Cook-Torrance geometric attenuation
    double g1 = 2.0 * nh / vh;
    double g = std::min( 1.0, std::min( g1 * nv, g1 * nl ) );

    // Fresnel term
    glm::dvec3 f = fresnel_->value( lh );

    glm::dvec3 rough_specular_term = ( f * d * g ) / ( 4.0 * nv * nl );

    return ( 1.0 / surface_sampler_->getProbability( w_i, w_r ) ) * rough_specular_term;
}

glm::dvec3 CookTorrance::getNewDirection( const glm::dvec3 &w_i ) const
{
    return surface_sampler_->getSample( w_i );
}

<commit_msg>Removed a pow functon call from Cook-Torrance BRDF.<commit_after>#include  "cook_torrance.h"

CookTorrance::CookTorrance( double m,
                            SurfaceSampler::SurfaceSamplerUniquePtr surface_sampler,
                            Fresnel::FresnelUniquePtr fresnel ) :
        BxDF( std::move( surface_sampler ) ),
        m_{ m },
        fresnel_( std::move( fresnel ) )
{}

glm::dvec3 CookTorrance::fr( const glm::dvec3 &w_i,
                             const glm::dvec3 &w_r ) const
{
    // Since the microfacet normal may not be aligned with the average surface normal,
    // it can be the case that the generated w_r might point into the average surface
    // (i.e., in terms of a local coordinate system, it's 'y' coordinate may be negative).
    // In this case, it is not possible to sample the Cook-Torrance BRDF because it is
    // defined only for hemisphere above the intersection point. Thus, in these cases,
    // the BRDF simply return 0.
    // -- That problem, and teh adopted solution, are mentioned in the Eric Veach's
    // thesis, in the Secton about shading normals.
    if ( w_r.y < 0.0 )
        return glm::dvec3{ 0.0, 0.0, 0.0 };

    glm::dvec3 h = glm::normalize( w_i + w_r );
    double nh = std::abs( h.y );
    double nv = std::abs( w_i.y );
    double nl = std::abs( w_r.y );
    double vh = std::abs( glm::dot( w_i, h ) );
    double lh = std::abs( glm::dot( w_r, h ) );

    // Beckmann normal distribution function
    double nh_2 = nh * nh;
    double m_2 = m_ * m_;
    double d1 = 1.0 / ( M_PI * m_2 * nh_2 * nh_2 );
    double d2 = ( nh_2 - 1.0 ) / ( m_2 * nh_2 );
    double d = d1 * exp( d2 );

    // Cook-Torrance geometric attenuation
    double g1 = 2.0 * nh / vh;
    double g = std::min( 1.0, std::min( g1 * nv, g1 * nl ) );

    // Fresnel term
    glm::dvec3 f = fresnel_->value( lh );

    glm::dvec3 rough_specular_term = ( f * d * g ) / ( 4.0 * nv * nl );

    return ( 1.0 / surface_sampler_->getProbability( w_i, w_r ) ) * rough_specular_term;
}

glm::dvec3 CookTorrance::getNewDirection( const glm::dvec3 &w_i ) const
{
    return surface_sampler_->getSample( w_i );
}

<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2019, John Haddon. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//      * Redistributions of source code must retain the above
//        copyright notice, this list of conditions and the following
//        disclaimer.
//
//      * 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.
//
//      * Neither the name of John Haddon nor the names of
//        any other contributors to this software may be used to endorse or
//        promote products derived from this software without specific prior
//        written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT OWNER OR
//  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 "GafferUI/AnnotationsGadget.h"

#include "GafferUI/GraphGadget.h"
#include "GafferUI/ImageGadget.h"
#include "GafferUI/NodeGadget.h"
#include "GafferUI/Style.h"

#include "Gaffer/Metadata.h"
#include "Gaffer/MetadataAlgo.h"

#include "boost/algorithm/string/predicate.hpp"
#include "boost/bind.hpp"
#include "boost/bind/placeholders.hpp"

using namespace GafferUI;
using namespace Gaffer;
using namespace IECore;
using namespace Imath;
using namespace std;

//////////////////////////////////////////////////////////////////////////
// Internal utilities
//////////////////////////////////////////////////////////////////////////

namespace
{

Box2f nodeFrame( const NodeGadget *nodeGadget )
{
	const Box3f b = nodeGadget->transformedBound( nullptr );
	return Box2f(
		V2f( b.min.x, b.min.y ),
		V2f( b.max.x, b.max.y )
	);
}

IECoreGL::Texture *bookmarkTexture()
{
	static IECoreGL::TexturePtr bookmarkTexture;

	if( !bookmarkTexture )
	{
		bookmarkTexture = ImageGadget::textureLoader()->load( "bookmarkStar2.png" );

		IECoreGL::Texture::ScopedBinding binding( *bookmarkTexture );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
	}
	return bookmarkTexture.get();
}

IECoreGL::Texture *numericBookmarkTexture()
{
	static IECoreGL::TexturePtr numericBookmarkTexture;

	if( !numericBookmarkTexture )
	{
		numericBookmarkTexture = ImageGadget::textureLoader()->load( "bookmarkStar.png" );

		IECoreGL::Texture::ScopedBinding binding( *numericBookmarkTexture );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
	}
	return numericBookmarkTexture.get();
}

float luminance( const Color3f &c )
{
	return c.dot( V3f( 0.2126, 0.7152, 0.0722 ) );
}

string wrap( const std::string &text, size_t maxLineLength )
{
	string result;

	using Tokenizer = boost::tokenizer<boost::char_separator<char>>;
	boost::char_separator<char> separator( "", " \n" );
	Tokenizer tokenizer( text, separator );

	size_t lineLength = 0;
	for( const auto &s : tokenizer )
	{
		if( s == "\n" )
		{
			result += s;
			lineLength = 0;
		}
		else if( lineLength == 0 || lineLength + s.size() < maxLineLength )
		{
			result += s;
			lineLength += s.size();
		}
		else
		{
			result += "\n" + s;
			lineLength = s.size();
		}
	}

	return result;
}

float g_offset = 0.5;
float g_borderWidth = 0.5;
float g_spacing = 0.25;

} // namespace

//////////////////////////////////////////////////////////////////////////
// AnnotationsGadget
//////////////////////////////////////////////////////////////////////////

GAFFER_GRAPHCOMPONENT_DEFINE_TYPE( AnnotationsGadget );

AnnotationsGadget::AnnotationsGadget()
	:	Gadget( "AnnotationsGadget" )
{
	Metadata::nodeValueChangedSignal().connect(
		boost::bind( &AnnotationsGadget::nodeMetadataChanged, this, ::_1, ::_2, ::_3 )
	);
}

AnnotationsGadget::~AnnotationsGadget()
{
}

bool AnnotationsGadget::acceptsParent( const GraphComponent *potentialParent ) const
{
	return runTimeCast<const GraphGadget>( potentialParent );
}

void AnnotationsGadget::parentChanging( Gaffer::GraphComponent *newParent )
{
	m_annotations.clear();
	m_graphGadgetChildAddedConnection.disconnect();
	m_graphGadgetChildRemovedConnection.disconnect();
	if( newParent )
	{
		m_graphGadgetChildAddedConnection = newParent->childAddedSignal().connect(
			boost::bind( &AnnotationsGadget::graphGadgetChildAdded, this, ::_2 )
		);
		m_graphGadgetChildRemovedConnection = newParent->childRemovedSignal().connect(
			boost::bind( &AnnotationsGadget::graphGadgetChildRemoved, this, ::_2 )
		);
	}
}

void AnnotationsGadget::doRenderLayer( Layer layer, const Style *style ) const
{
	if( layer != GraphLayer::Overlay )
	{
		return;
	}

	vector<string> names;
	for( auto &ga : m_annotations )
	{
		const Node *node = ga.first->node();
		Annotations &annotations = ga.second;

		if( annotations.dirty )
		{
			annotations.renderable = false;

			annotations.bookmarked = Gaffer::MetadataAlgo::getBookmarked( node );
			annotations.renderable |= annotations.bookmarked;

			if( int bookmark = MetadataAlgo::numericBookmark( node ) )
			{
				annotations.numericBookmark = std::to_string( bookmark );
				annotations.renderable = true;
			}
			else
			{
				annotations.numericBookmark = InternedString();
			}

			annotations.standardAnnotations.clear();
			names.clear();
			MetadataAlgo::annotations( node, names );
			for( const auto &name : names )
			{
				annotations.standardAnnotations.push_back(
					MetadataAlgo::getAnnotation( node, name, /* inheritTemplate = */ true )
				);
				// Word wrap. It might be preferable to do this during
				// rendering, but we have no way of querying the extent of
				// `Style::renderWrappedText()`.
				annotations.standardAnnotations.back().textData = new StringData(
					wrap( annotations.standardAnnotations.back().text(), 60 )
				);
			}
			annotations.renderable |= (bool)annotations.standardAnnotations.size();

			annotations.dirty = false;
		}

		if( !annotations.renderable )
		{
			continue;
		}

		const Box2f b = nodeFrame( ga.first );
		if( annotations.bookmarked )
		{
			style->renderImage( Box2f( V2f( b.min.x - 1.0, b.max.y - 1.0 ), V2f( b.min.x + 1.0, b.max.y + 1.0 ) ), bookmarkTexture() );
		}

		if( annotations.numericBookmark.string().size() )
		{
			if( !annotations.bookmarked )
			{
				style->renderImage( Box2f( V2f( b.min.x - 1.0, b.max.y - 1.0 ), V2f( b.min.x + 1.0, b.max.y + 1.0 ) ), numericBookmarkTexture() );
			}

			const Box3f textBounds = style->textBound( Style::LabelText, annotations.numericBookmark.string() );

			const Imath::Color4f textColor( 1.0f );
			glPushMatrix();
				IECoreGL::glTranslate( V2f( b.min.x + 1.0 - textBounds.size().x * 0.5, b.max.y - textBounds.size().y * 0.5 - 0.7 ) );
				style->renderText( Style::BodyText, annotations.numericBookmark.string(), Style::NormalState, &textColor );
			glPopMatrix();
		}

		if( annotations.standardAnnotations.size() )
		{
			// Baseline for `characterBound` and `textBound` is at `y == 0`.
			const Box3f characterBound = style->characterBound( Style::BodyText );

			glPushMatrix();
			IECoreGL::glTranslate( V2f( b.max.x + g_offset + g_borderWidth, b.max.y ) );

			const Color4f darkGrey( 0.1, 0.1, 0.1, 1.0 );
			const Color4f midGrey( 0.65, 0.65, 0.65, 1.0 );
			float previousHeight = 0;
			for( const auto &a : annotations.standardAnnotations )
			{
				// Translate down to put the text baseline where we want it.
				float yOffset = g_borderWidth + characterBound.max.y;
				if( &a != &annotations.standardAnnotations.front() )
				{
					yOffset += previousHeight + g_borderWidth + g_spacing;
				}
				IECoreGL::glTranslate( V2f( 0, -yOffset ) );

				// Draw frame and text. `textBounds` is not ideal for use as a
				// frame, as it provides an exact bound that changes in `y`
				// depending on the existence of ascenders and/or descenders.
				// We work around this by using `characterBound.max.y` for our
				// `max`, but without reverse engineering the line spacing etc in
				// `style`, we can't do much about `min`.
				/// \todo We're using similar workarounds in TextGadget, so we
				/// should probably improve `Style` and `IECoreScene::Font`
				/// instead.
				const Box3f textBounds = style->textBound( Style::BodyText, a.text() );

				/// \todo We're using `renderNodeFrame()` because it's the only way we can specify a colour,
				/// but really we want `renderFrame()` to provide that option. Or we could consider having
				/// explicit annotation rendering methods in the Style class.
				style->renderNodeFrame(
					Box2f( V2f( 0, textBounds.min.y ), V2f( textBounds.max.x, characterBound.max.y ) ),
					g_borderWidth, Style::NormalState,
					&a.color()
				);
				style->renderText(
					Style::BodyText, a.text(), Style::NormalState,
					luminance( a.color() ) > 0.4 ? &darkGrey : &midGrey
				);
				previousHeight = -textBounds.min.y;
			}
			glPopMatrix();
		}
	}
}

GraphGadget *AnnotationsGadget::graphGadget()
{
	return parent<GraphGadget>();
}

const GraphGadget *AnnotationsGadget::graphGadget() const
{
	return parent<GraphGadget>();
}

void AnnotationsGadget::graphGadgetChildAdded( GraphComponent *child )
{
	if( NodeGadget *nodeGadget = runTimeCast<NodeGadget>( child ) )
	{
		m_annotations[nodeGadget] = Annotations();
	}
}

void AnnotationsGadget::graphGadgetChildRemoved( const GraphComponent *child )
{
	if( const NodeGadget *nodeGadget = runTimeCast<const NodeGadget>( child ) )
	{
		m_annotations.erase( nodeGadget );
	}
}

void AnnotationsGadget::nodeMetadataChanged( IECore::TypeId nodeTypeId, IECore::InternedString key, Gaffer::Node *node )
{
	if( !node )
	{
		// We only expect annotations to be registered
		// as per-instance metadate.
		return;
	}

	if(
		!MetadataAlgo::bookmarkedAffectedByChange( key ) &&
		!MetadataAlgo::numericBookmarkAffectedByChange( key ) &&
		!boost::starts_with( key.c_str(), "annotation:" )
	)
	{
		return;
	}

	if( auto gadget = graphGadget()->nodeGadget( node ) )
	{
		auto it = m_annotations.find( gadget );
		assert( it != m_annotations.end() );
		it->second.dirty = true;
		dirty( DirtyType::Render );
	}
}
<commit_msg>AnnotationsGadget : Use `MetadataAlgo::annotationsAffectedByChange()`<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2019, John Haddon. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//      * Redistributions of source code must retain the above
//        copyright notice, this list of conditions and the following
//        disclaimer.
//
//      * 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.
//
//      * Neither the name of John Haddon nor the names of
//        any other contributors to this software may be used to endorse or
//        promote products derived from this software without specific prior
//        written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT OWNER OR
//  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 "GafferUI/AnnotationsGadget.h"

#include "GafferUI/GraphGadget.h"
#include "GafferUI/ImageGadget.h"
#include "GafferUI/NodeGadget.h"
#include "GafferUI/Style.h"

#include "Gaffer/Metadata.h"
#include "Gaffer/MetadataAlgo.h"

#include "boost/algorithm/string/predicate.hpp"
#include "boost/bind.hpp"
#include "boost/bind/placeholders.hpp"

using namespace GafferUI;
using namespace Gaffer;
using namespace IECore;
using namespace Imath;
using namespace std;

//////////////////////////////////////////////////////////////////////////
// Internal utilities
//////////////////////////////////////////////////////////////////////////

namespace
{

Box2f nodeFrame( const NodeGadget *nodeGadget )
{
	const Box3f b = nodeGadget->transformedBound( nullptr );
	return Box2f(
		V2f( b.min.x, b.min.y ),
		V2f( b.max.x, b.max.y )
	);
}

IECoreGL::Texture *bookmarkTexture()
{
	static IECoreGL::TexturePtr bookmarkTexture;

	if( !bookmarkTexture )
	{
		bookmarkTexture = ImageGadget::textureLoader()->load( "bookmarkStar2.png" );

		IECoreGL::Texture::ScopedBinding binding( *bookmarkTexture );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
	}
	return bookmarkTexture.get();
}

IECoreGL::Texture *numericBookmarkTexture()
{
	static IECoreGL::TexturePtr numericBookmarkTexture;

	if( !numericBookmarkTexture )
	{
		numericBookmarkTexture = ImageGadget::textureLoader()->load( "bookmarkStar.png" );

		IECoreGL::Texture::ScopedBinding binding( *numericBookmarkTexture );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
	}
	return numericBookmarkTexture.get();
}

float luminance( const Color3f &c )
{
	return c.dot( V3f( 0.2126, 0.7152, 0.0722 ) );
}

string wrap( const std::string &text, size_t maxLineLength )
{
	string result;

	using Tokenizer = boost::tokenizer<boost::char_separator<char>>;
	boost::char_separator<char> separator( "", " \n" );
	Tokenizer tokenizer( text, separator );

	size_t lineLength = 0;
	for( const auto &s : tokenizer )
	{
		if( s == "\n" )
		{
			result += s;
			lineLength = 0;
		}
		else if( lineLength == 0 || lineLength + s.size() < maxLineLength )
		{
			result += s;
			lineLength += s.size();
		}
		else
		{
			result += "\n" + s;
			lineLength = s.size();
		}
	}

	return result;
}

float g_offset = 0.5;
float g_borderWidth = 0.5;
float g_spacing = 0.25;

} // namespace

//////////////////////////////////////////////////////////////////////////
// AnnotationsGadget
//////////////////////////////////////////////////////////////////////////

GAFFER_GRAPHCOMPONENT_DEFINE_TYPE( AnnotationsGadget );

AnnotationsGadget::AnnotationsGadget()
	:	Gadget( "AnnotationsGadget" )
{
	Metadata::nodeValueChangedSignal().connect(
		boost::bind( &AnnotationsGadget::nodeMetadataChanged, this, ::_1, ::_2, ::_3 )
	);
}

AnnotationsGadget::~AnnotationsGadget()
{
}

bool AnnotationsGadget::acceptsParent( const GraphComponent *potentialParent ) const
{
	return runTimeCast<const GraphGadget>( potentialParent );
}

void AnnotationsGadget::parentChanging( Gaffer::GraphComponent *newParent )
{
	m_annotations.clear();
	m_graphGadgetChildAddedConnection.disconnect();
	m_graphGadgetChildRemovedConnection.disconnect();
	if( newParent )
	{
		m_graphGadgetChildAddedConnection = newParent->childAddedSignal().connect(
			boost::bind( &AnnotationsGadget::graphGadgetChildAdded, this, ::_2 )
		);
		m_graphGadgetChildRemovedConnection = newParent->childRemovedSignal().connect(
			boost::bind( &AnnotationsGadget::graphGadgetChildRemoved, this, ::_2 )
		);
	}
}

void AnnotationsGadget::doRenderLayer( Layer layer, const Style *style ) const
{
	if( layer != GraphLayer::Overlay )
	{
		return;
	}

	vector<string> names;
	for( auto &ga : m_annotations )
	{
		const Node *node = ga.first->node();
		Annotations &annotations = ga.second;

		if( annotations.dirty )
		{
			annotations.renderable = false;

			annotations.bookmarked = Gaffer::MetadataAlgo::getBookmarked( node );
			annotations.renderable |= annotations.bookmarked;

			if( int bookmark = MetadataAlgo::numericBookmark( node ) )
			{
				annotations.numericBookmark = std::to_string( bookmark );
				annotations.renderable = true;
			}
			else
			{
				annotations.numericBookmark = InternedString();
			}

			annotations.standardAnnotations.clear();
			names.clear();
			MetadataAlgo::annotations( node, names );
			for( const auto &name : names )
			{
				annotations.standardAnnotations.push_back(
					MetadataAlgo::getAnnotation( node, name, /* inheritTemplate = */ true )
				);
				// Word wrap. It might be preferable to do this during
				// rendering, but we have no way of querying the extent of
				// `Style::renderWrappedText()`.
				annotations.standardAnnotations.back().textData = new StringData(
					wrap( annotations.standardAnnotations.back().text(), 60 )
				);
			}
			annotations.renderable |= (bool)annotations.standardAnnotations.size();

			annotations.dirty = false;
		}

		if( !annotations.renderable )
		{
			continue;
		}

		const Box2f b = nodeFrame( ga.first );
		if( annotations.bookmarked )
		{
			style->renderImage( Box2f( V2f( b.min.x - 1.0, b.max.y - 1.0 ), V2f( b.min.x + 1.0, b.max.y + 1.0 ) ), bookmarkTexture() );
		}

		if( annotations.numericBookmark.string().size() )
		{
			if( !annotations.bookmarked )
			{
				style->renderImage( Box2f( V2f( b.min.x - 1.0, b.max.y - 1.0 ), V2f( b.min.x + 1.0, b.max.y + 1.0 ) ), numericBookmarkTexture() );
			}

			const Box3f textBounds = style->textBound( Style::LabelText, annotations.numericBookmark.string() );

			const Imath::Color4f textColor( 1.0f );
			glPushMatrix();
				IECoreGL::glTranslate( V2f( b.min.x + 1.0 - textBounds.size().x * 0.5, b.max.y - textBounds.size().y * 0.5 - 0.7 ) );
				style->renderText( Style::BodyText, annotations.numericBookmark.string(), Style::NormalState, &textColor );
			glPopMatrix();
		}

		if( annotations.standardAnnotations.size() )
		{
			// Baseline for `characterBound` and `textBound` is at `y == 0`.
			const Box3f characterBound = style->characterBound( Style::BodyText );

			glPushMatrix();
			IECoreGL::glTranslate( V2f( b.max.x + g_offset + g_borderWidth, b.max.y ) );

			const Color4f darkGrey( 0.1, 0.1, 0.1, 1.0 );
			const Color4f midGrey( 0.65, 0.65, 0.65, 1.0 );
			float previousHeight = 0;
			for( const auto &a : annotations.standardAnnotations )
			{
				// Translate down to put the text baseline where we want it.
				float yOffset = g_borderWidth + characterBound.max.y;
				if( &a != &annotations.standardAnnotations.front() )
				{
					yOffset += previousHeight + g_borderWidth + g_spacing;
				}
				IECoreGL::glTranslate( V2f( 0, -yOffset ) );

				// Draw frame and text. `textBounds` is not ideal for use as a
				// frame, as it provides an exact bound that changes in `y`
				// depending on the existence of ascenders and/or descenders.
				// We work around this by using `characterBound.max.y` for our
				// `max`, but without reverse engineering the line spacing etc in
				// `style`, we can't do much about `min`.
				/// \todo We're using similar workarounds in TextGadget, so we
				/// should probably improve `Style` and `IECoreScene::Font`
				/// instead.
				const Box3f textBounds = style->textBound( Style::BodyText, a.text() );

				/// \todo We're using `renderNodeFrame()` because it's the only way we can specify a colour,
				/// but really we want `renderFrame()` to provide that option. Or we could consider having
				/// explicit annotation rendering methods in the Style class.
				style->renderNodeFrame(
					Box2f( V2f( 0, textBounds.min.y ), V2f( textBounds.max.x, characterBound.max.y ) ),
					g_borderWidth, Style::NormalState,
					&a.color()
				);
				style->renderText(
					Style::BodyText, a.text(), Style::NormalState,
					luminance( a.color() ) > 0.4 ? &darkGrey : &midGrey
				);
				previousHeight = -textBounds.min.y;
			}
			glPopMatrix();
		}
	}
}

GraphGadget *AnnotationsGadget::graphGadget()
{
	return parent<GraphGadget>();
}

const GraphGadget *AnnotationsGadget::graphGadget() const
{
	return parent<GraphGadget>();
}

void AnnotationsGadget::graphGadgetChildAdded( GraphComponent *child )
{
	if( NodeGadget *nodeGadget = runTimeCast<NodeGadget>( child ) )
	{
		m_annotations[nodeGadget] = Annotations();
	}
}

void AnnotationsGadget::graphGadgetChildRemoved( const GraphComponent *child )
{
	if( const NodeGadget *nodeGadget = runTimeCast<const NodeGadget>( child ) )
	{
		m_annotations.erase( nodeGadget );
	}
}

void AnnotationsGadget::nodeMetadataChanged( IECore::TypeId nodeTypeId, IECore::InternedString key, Gaffer::Node *node )
{
	if( !node )
	{
		// We only expect annotations to be registered
		// as per-instance metadate.
		return;
	}

	if(
		!MetadataAlgo::bookmarkedAffectedByChange( key ) &&
		!MetadataAlgo::numericBookmarkAffectedByChange( key ) &&
		!MetadataAlgo::annotationsAffectedByChange( key )
	)
	{
		return;
	}

	if( auto gadget = graphGadget()->nodeGadget( node ) )
	{
		auto it = m_annotations.find( gadget );
		assert( it != m_annotations.end() );
		it->second.dirty = true;
		dirty( DirtyType::Render );
	}
}
<|endoftext|>


<commit_before>#pragma once

#include "ObjectId.hpp"
#include <memory>
#include <string>
#include "Engine.h"

namespace Core
{
	class Object final
	{
	public:
		Object(ObjectId id, Engine* engine)
			: _id(id), _engine(engine)
		{

		}

		/* Child */
		void AddChild(Object& child)
		{
		}

		void HasChild(Object& child)
		{

		}

		void DeleteChild(Object& child)
		{

		}

		void FindChild(Object& child)
		{

		}

		void GetChild(uint index)
		{

		}

		/* Component */
		template <class Component>
		Component& AddComponent()
		{
			return _engine->GetComponentSystem().Add<Component>(_id);
		}

		template <class Component>
		void DeleteComponet()
		{
			_engine->GetComponentSystem().Delete<Component>(_id);
		}

		template <class Component>
		bool HasComponent() const
		{
			return _engine->GetComponentSystem().Has<Component>(_id);
		}

	public:
		GET_SET_ACCESSOR(Name, const std::string&, _name);
		GET_CONST_ACCESSOR(Id, const ObjectId&, _id);

	private:
		ObjectId				_id;
		std::string				_name = "";
		Engine*					_engine;
	};
}<commit_msg>Object - 간단하게 몇몇 기능 구현, AddComponent, AddChild 등등<commit_after>#pragma once

#include "ObjectId.hpp"
#include <memory>
#include <string>
#include <cassert>

#include "MainSystemHeader.h"

namespace Core
{
	class Object final
	{
	public:
		Object(ObjectId id, DefaultComponentSystem& dcs, TransformPool& tfp)
			: _id(id), _componentSystem(&dcs), _transformPool(&tfp)
		{
			_transformPool->Add(id);
		}

		~Object()
		{
			_transformPool->Delete(_id);
		}

		/* Child */
		void AddChild(Object& child)
		{
			ObjectId childId = child.GetId();
			auto found = _transformPool->GetIndexer().Has(childId);
			assert(found);

			_transformPool->Find(_id)->AddChild(childId);
		}

		bool HasChild(const Object& child) const
		{
			return _transformPool->Find(_id)->HasChild(child.GetId());
		}

		void DeleteChild(Object& child)
		{
			_transformPool->Find(_id)->DeleteChild(child.GetId());
		}

		auto FindChild(Object& child)
		{
			return HasChild(child) ? _transformPool->Find(child.GetId()) : nullptr;
		}

		auto GetChild(uint index)
		{
			ObjectId id = _transformPool->Find(_id)->GetChild(index);
			return _transformPool->Find(id);
		}

		/* Component */
		template <class Component>
		Component& AddComponent()
		{
			return _componentSystem->Add<Component>(_id);
		}

		template <class Component>
		void DeleteComponet()
		{
			_componentSystem->Delete<Component>(_id);
		}

		template <class Component>
		bool HasComponent() const
		{
			return _componentSystem->Has<Component>(_id);
		}

	public:
		GET_SET_ACCESSOR(Name, const std::string&, _name);
		GET_CONST_ACCESSOR(Id, ObjectId, _id);

	private:
		ObjectId				_id;
		std::string				_name = "";

		DefaultComponentSystem*	_componentSystem;
		TransformPool*			_transformPool;
	};
}<|endoftext|>


<commit_before>// Ouzel by Elviss Strazdins

#ifndef OUZEL_MATH_SIZE_HPP
#define OUZEL_MATH_SIZE_HPP

#include <array>
#include <cstddef>
#include <type_traits>
#include "Vector.hpp"

namespace ouzel
{
    template <typename T, std::size_t N> class Size final
    {
    public:
        std::array<T, N> v{};

        constexpr Size() noexcept {}

        template <typename ...A>
        explicit constexpr Size(const A... args) noexcept:
            v{args...}
        {
        }

        template <auto X = N, std::size_t M, std::enable_if_t<(X != M)>* = nullptr>
        explicit Size(const Size<T, M>& size) noexcept
        {
            for (std::size_t i = 0; i < N && i < M; ++i)
                v[i] = size.v[i];
        }

        explicit constexpr Size(const Vector<T, N>& vec) noexcept:
            v{vec.v}
        {
        }

        auto& operator[](const std::size_t index) noexcept { return v[index]; }
        constexpr auto operator[](const std::size_t index) const noexcept { return v[index]; }

        template <auto X = N, std::enable_if_t<(X >= 1)>* = nullptr>
        auto& width() noexcept { return v[0]; }

        template <auto X = N, std::enable_if_t<(X >= 1)>* = nullptr>
        constexpr auto width() const noexcept { return v[0]; }

        template <auto X = N, std::enable_if_t<(X >= 2)>* = nullptr>
        auto& height() noexcept { return v[1]; }

        template <auto X = N, std::enable_if_t<(X >= 2)>* = nullptr>
        constexpr auto height() const noexcept { return v[1]; }

        template <auto X = N, std::enable_if_t<(X >= 3)>* = nullptr>
        auto& depth() noexcept { return v[2]; }

        template <auto X = N, std::enable_if_t<(X >= 3)>* = nullptr>
        constexpr auto depth() const noexcept { return v[2]; }

        void scale(const Vector<T, N>& scale) noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                v[i] *= scale.v[i];
        }

        const Size operator+(const Size& size) const noexcept
        {
            Size result = *this;
            for (std::size_t i = 0; i < N; ++i)
                result.v[i] += size.v[i];
            return result;
        }

        Size& operator+=(const Size& size) noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                v[i] += size.v[i];
            return *this;
        }

        const Size operator-(const Size& size) const noexcept
        {
            Size result = *this;
            for (std::size_t i = 0; i < N; ++i)
                result.v[i] -= size.v[i];
            return result;
        }

        Size& operator-=(const Size& size) noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                v[i] -= size.v[i];
            return *this;
        }

        const Size operator-() const noexcept
        {
            Size result = *this;
            for (T& c : result.v)
                c = -c;
            return result;
        }

        const Size operator*(const T scalar) const noexcept
        {
            Size result = *this;
            for (T& c : result.v)
                c *= scalar;
            return result;
        }

        Size& operator*=(const T scalar) noexcept
        {
            for (T& c : v)
                c *= scalar;
            return *this;
        }

        const Size operator/(const T scalar) const noexcept
        {
            Size result = *this;
            for (T& c : result.v)
                c /= scalar;
            return result;
        }

        Size& operator/=(const T scalar) noexcept
        {
            for (T& c : v)
                c /= scalar;
            return *this;
        }

        bool operator<(const Size& size) const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
            {
                if (v[i] < size.v[i]) return true;
                else if (size.v[i] < v[i]) return false;
            }

            return false;
        }

        bool operator==(const Size& size) const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (v[i] != size.v[i]) return false;
            return true;
        }

        bool operator!=(const Size& size) const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (v[i] != size.v[i]) return true;
            return false;
        }

        auto isZero() const noexcept
        {
            for (const T& c : v)
                if (c != T(0)) return false;
            return true;
        }

        T volume() const noexcept
        {
            T result = 0;
            for (const T& c : v)
                result *= c;
            return result;
        }
    };

    template <typename T, std::size_t N>
    const Size<T, N> operator*(const Size<T, N>& size, const Vector<T, N>& v) noexcept
    {
        Size<T, N> result = size;
        for (std::size_t i = 0; i < N; ++i)
            result.v[i] *= v.v[i];
        return result;
    }

    template <typename T, std::size_t N>
    const Size<T, N> operator/(const Size<T, N>& size, const Vector<T, N>& v) noexcept
    {
        Size<T, N> result = size;
        for (std::size_t i = 0; i < N; ++i)
            result.v[i] /= v.v[i];
        return result;
    }
}

#endif // OUZEL_MATH_SIZE_HPP
<commit_msg>Use auto<commit_after>// Ouzel by Elviss Strazdins

#ifndef OUZEL_MATH_SIZE_HPP
#define OUZEL_MATH_SIZE_HPP

#include <array>
#include <cstddef>
#include <type_traits>
#include "Vector.hpp"

namespace ouzel
{
    template <typename T, std::size_t N> class Size final
    {
    public:
        std::array<T, N> v{};

        constexpr Size() noexcept {}

        template <typename ...A>
        explicit constexpr Size(const A... args) noexcept:
            v{args...}
        {
        }

        template <auto X = N, std::size_t M, std::enable_if_t<(X != M)>* = nullptr>
        explicit Size(const Size<T, M>& size) noexcept
        {
            for (std::size_t i = 0; i < N && i < M; ++i)
                v[i] = size.v[i];
        }

        explicit constexpr Size(const Vector<T, N>& vec) noexcept:
            v{vec.v}
        {
        }

        auto& operator[](const std::size_t index) noexcept { return v[index]; }
        constexpr auto operator[](const std::size_t index) const noexcept { return v[index]; }

        template <auto X = N, std::enable_if_t<(X >= 1)>* = nullptr>
        auto& width() noexcept { return v[0]; }

        template <auto X = N, std::enable_if_t<(X >= 1)>* = nullptr>
        constexpr auto width() const noexcept { return v[0]; }

        template <auto X = N, std::enable_if_t<(X >= 2)>* = nullptr>
        auto& height() noexcept { return v[1]; }

        template <auto X = N, std::enable_if_t<(X >= 2)>* = nullptr>
        constexpr auto height() const noexcept { return v[1]; }

        template <auto X = N, std::enable_if_t<(X >= 3)>* = nullptr>
        auto& depth() noexcept { return v[2]; }

        template <auto X = N, std::enable_if_t<(X >= 3)>* = nullptr>
        constexpr auto depth() const noexcept { return v[2]; }

        void scale(const Vector<T, N>& scale) noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                v[i] *= scale.v[i];
        }

        const Size operator+(const Size& size) const noexcept
        {
            Size result = *this;
            for (std::size_t i = 0; i < N; ++i)
                result.v[i] += size.v[i];
            return result;
        }

        Size& operator+=(const Size& size) noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                v[i] += size.v[i];
            return *this;
        }

        const Size operator-(const Size& size) const noexcept
        {
            Size result = *this;
            for (std::size_t i = 0; i < N; ++i)
                result.v[i] -= size.v[i];
            return result;
        }

        Size& operator-=(const Size& size) noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                v[i] -= size.v[i];
            return *this;
        }

        const Size operator-() const noexcept
        {
            Size result = *this;
            for (T& c : result.v)
                c = -c;
            return result;
        }

        const Size operator*(const T scalar) const noexcept
        {
            Size result = *this;
            for (T& c : result.v)
                c *= scalar;
            return result;
        }

        Size& operator*=(const T scalar) noexcept
        {
            for (T& c : v)
                c *= scalar;
            return *this;
        }

        const Size operator/(const T scalar) const noexcept
        {
            Size result = *this;
            for (T& c : result.v)
                c /= scalar;
            return result;
        }

        Size& operator/=(const T scalar) noexcept
        {
            for (T& c : v)
                c /= scalar;
            return *this;
        }

        bool operator<(const Size& size) const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (v[i] < size.v[i]) return true;
                else if (size.v[i] < v[i]) return false;

            return false;
        }

        bool operator==(const Size& size) const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (v[i] != size.v[i]) return false;
            return true;
        }

        bool operator!=(const Size& size) const noexcept
        {
            for (std::size_t i = 0; i < N; ++i)
                if (v[i] != size.v[i]) return true;
            return false;
        }

        auto isZero() const noexcept
        {
            for (const T& c : v)
                if (c != T(0)) return false;
            return true;
        }

        T volume() const noexcept
        {
            T result = 0;
            for (const T& c : v)
                result *= c;
            return result;
        }
    };

    template <typename T, std::size_t N>
    const Size<T, N> operator*(const Size<T, N>& size, const Vector<T, N>& v) noexcept
    {
        auto result = size;
        for (std::size_t i = 0; i < N; ++i)
            result.v[i] *= v.v[i];
        return result;
    }

    template <typename T, std::size_t N>
    const Size<T, N> operator/(const Size<T, N>& size, const Vector<T, N>& v) noexcept
    {
        auto result = size;
        for (std::size_t i = 0; i < N; ++i)
            result.v[i] /= v.v[i];
        return result;
    }
}

#endif // OUZEL_MATH_SIZE_HPP
<|endoftext|>