<commit_before>/*
 * A Viewer Object
 * Copyright © 2010, Théo de la Hogue
 *
 * License: This code is licensed under the terms of the "New BSD License"
 * http://creativecommons.org/licenses/BSD/
 */

#include "TTViewer.h"

#define thisTTClass			TTViewer
#define thisTTClassName		"Viewer"
#define thisTTClassTags		"viewer"

TT_MODULAR_CONSTRUCTOR,
mAddress(kTTAdrsEmpty),
mDescription(kTTSymEmpty),
mType(kTTSymEmpty),
mTag(kTTSymEmpty),
mHighlight(NO),
mFreeze(NO),
mDataspace(kTTSym_none),
mDataspaceUnit(kTTSym_none),
mDataspaceConverter(NULL),
mDataspaceObserver(NULL),
mDataspaceUnitObserver(NULL),
mEnable(YES),
mReturnedValue(kTTValNONE),
mReceiver(NULL),
mSender(NULL),
mReturnValueCallback(NULL)
{
	if(arguments.getSize() == 1)
		arguments.get(0, (TTPtr*)&mReturnValueCallback);

	addAttributeWithSetter(Address, kTypeSymbol);
	addAttribute(Description, kTypeSymbol);
	addAttribute(Type, kTypeSymbol);
	addAttribute(Tag, kTypeSymbol);
	addAttribute(Highlight, kTypeBoolean);
	addAttributeWithSetter(Freeze, kTypeBoolean);

	addAttribute(Dataspace, kTypeSymbol);
	addAttributeProperty(Dataspace, readOnly, YES);
	addAttributeWithSetter(DataspaceUnit, kTypeSymbol);

	addAttributeWithSetter(Enable, kTypeBoolean);

	addAttributeWithSetter(ReturnedValue, kTypeLocalValue);
	addAttributeProperty(ReturnedValue, readOnly, YES);
	addAttributeProperty(ReturnedValue, hidden, YES);

	addMessage(Refresh);

	addMessageWithArguments(Send);
	addMessageProperty(Send, hidden, YES);

	TTObjectInstantiate(TT("dataspace"),  &mDataspaceConverter, kTTValNONE);
}

TTViewer::~TTViewer() // TODO : delete things...
{
	if (mReturnValueCallback) {
		delete (TTValuePtr)mReturnValueCallback->getBaton();
		TTObjectRelease(TTObjectHandle(&mReturnValueCallback));
	}

	if (mDataspaceConverter)
		TTObjectRelease(TTObjectHandle(&mDataspaceConverter));

	if (mDataspaceObserver)
		TTObjectRelease(TTObjectHandle(&mDataspaceObserver));

	if (mDataspaceUnitObserver)
		TTObjectRelease(TTObjectHandle(&mDataspaceUnitObserver));

	if (mSender)
		TTObjectRelease(TTObjectHandle(&mSender));

	if (mReceiver)
		TTObjectRelease(TTObjectHandle(&mReceiver));
}

TTErr TTViewer::setAddress(const TTValue& value)
{
	value.get(0, &mAddress);

	bind();

	return kTTErrNone;
}

TTErr TTViewer::bind()
{
	TTValue			args, v;
	TTObjectPtr		returnAddressCallback, returnValueCallback;
	TTValuePtr		returnAddressBaton, returnValueBaton;

	// Prepare arguments
	if (mAddress == kTTAdrsEmpty)
		return kTTErrGeneric;

	// The default attribute to bind is value
	if (mAddress->getAttribute() == NO_ATTRIBUTE)
		mAddress->appendAttribute(kTTSym_value);

	// Replace a TTSender object
	if (mSender)
		TTObjectRelease(TTObjectHandle(&mSender));

	mSender = NULL;							// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("Sender"), TTObjectHandle(&mSender), kTTValNONE);

	mSender->setAttributeValue(kTTSym_address, mAddress);

	// Replace a TTReceiver object
	if (mReceiver)
		TTObjectRelease(TTObjectHandle(&mReceiver));

	returnAddressCallback = NULL;			// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("callback"), &returnAddressCallback, kTTValNONE);
	returnAddressBaton = new TTValue(TTPtr(this));
	returnAddressCallback->setAttributeValue(kTTSym_baton, TTPtr(returnAddressBaton));
	returnAddressCallback->setAttributeValue(kTTSym_function, TTPtr(&TTViewerReceiveAddressCallback));
	args.append(returnAddressCallback);

	returnValueCallback = NULL;				// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("callback"), &returnValueCallback, kTTValNONE);
	returnValueBaton = new TTValue(TTPtr(this));
	returnValueCallback->setAttributeValue(kTTSym_baton, TTPtr(returnValueBaton));
	returnValueCallback->setAttributeValue(kTTSym_function, TTPtr(&TTViewerReceiveValueCallback));
	args.append(returnValueCallback);

	mReceiver = NULL;						// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("Receiver"), TTObjectHandle(&mReceiver), args);

	mReceiver->setAttributeValue(kTTSym_address, mAddress);

	observeDataspace();
	observeDataspaceUnit();

	return kTTErrNone;
}

TTErr TTViewer::observeDataspace()
{
	TTValue			args;
	TTObjectPtr		returnDataspaceCallback;
	TTValuePtr		returnDataspaceBaton;

	if (mDataspaceObserver)
		TTObjectRelease(TTObjectHandle(&mDataspaceObserver));

	// Make a TTReceiver object
	args.append(NULL);

	returnDataspaceCallback = NULL;				// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("callback"), &returnDataspaceCallback, kTTValNONE);
	returnDataspaceBaton = new TTValue(TTPtr(this));
	returnDataspaceCallback->setAttributeValue(kTTSym_baton, TTPtr(returnDataspaceBaton));
	returnDataspaceCallback->setAttributeValue(kTTSym_function, TTPtr(&TTViewerDataspaceCallback));
	args.append(returnDataspaceCallback);

	mDataspaceObserver = NULL;
	TTObjectInstantiate(TT("Receiver"), TTObjectHandle(&mDataspaceObserver), args);

	mDataspaceObserver->setAttributeValue(kTTSym_address, mAddress->appendAttribute(kTTSym_dataspace));

	mDataspaceObserver->sendMessage(kTTSym_Get);

	return kTTErrNone;
}

TTErr TTViewer::observeDataspaceUnit()
{
	TTValue			args;
	TTObjectPtr		returnDataspaceUnitCallback;
	TTValuePtr		returnDataspaceUnitBaton;

	if (mDataspaceUnitObserver)
		TTObjectRelease(TTObjectHandle(&mDataspaceUnitObserver));

	// Make a TTReceiver object
	args.append(NULL);

	returnDataspaceUnitCallback = NULL;				// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("callback"), &returnDataspaceUnitCallback, kTTValNONE);
	returnDataspaceUnitBaton = new TTValue(TTPtr(this));
	returnDataspaceUnitCallback->setAttributeValue(kTTSym_baton, TTPtr(returnDataspaceUnitBaton));
	returnDataspaceUnitCallback->setAttributeValue(kTTSym_function, TTPtr(&TTViewerDataspaceUnitCallback));
	args.append(returnDataspaceUnitCallback);

	mDataspaceUnitObserver = NULL;
	TTObjectInstantiate(TT("Receiver"), TTObjectHandle(&mDataspaceUnitObserver), args);

	mDataspaceUnitObserver->setAttributeValue(kTTSym_address, mAddress->appendAttribute(kTTSym_dataspaceUnit));

	mDataspaceUnitObserver->sendMessage(kTTSym_Get);

	return kTTErrNone;
}

TTErr TTViewer::setEnable(const TTValue& value)
{
	mEnable = value;

	if (mReceiver)
		mReceiver->setAttributeValue(kTTSym_enable, mEnable);

	return kTTErrNone;
}

TTErr TTViewer::setFreeze(const TTValue& value)
{
	mFreeze = value;

	return kTTErrNone;
}

TTErr TTViewer::setReturnedValue(const TTValue& value)
{
	TTAttributePtr	anAttribute = NULL;
	TTErr			err;

	mReturnedValue = value;

	err = this->findAttribute(kTTSym_returnedValue, &anAttribute);

	if (!err)
		anAttribute->sendNotification(kTTSym_notify, mReturnedValue);	// we use kTTSym_notify because we know that observers are TTCallback

	return kTTErrNone;
}

TTErr TTViewer::Refresh()
{
	if (mReceiver)
		return mReceiver->sendMessage(kTTSym_Get);

	return kTTErrGeneric;
}

TTErr TTViewer::Send(const TTValue& inputValue, TTValue& outputValue)
{
	if (mSender) {

		TTValue valueToSend = inputValue;

		// append view unit
		if (mDataspaceUnit != kTTSym_none)
			valueToSend.append(mDataspaceUnit);

		return mSender->sendMessage(kTTSym_Send, valueToSend, kTTValNONE);
	}

	return kTTErrGeneric;
}

TTErr TTViewer::setDataspaceUnit(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mDataspaceUnit = value;

	if (mDataspaceConverter)
		mDataspaceConverter->setAttributeValue(TT("outputUnit"), mDataspaceUnit);

	// TODO : notifyObservers(kTTSym_dataspaceUnit, n);
	return kTTErrNone;
}

TTErr TTViewer::convertUnit(const TTValue& inputValue, TTValue& outputValue)
{
	if (mDataspaceConverter)
		return mDataspaceConverter->sendMessage(TT("convert"), inputValue, outputValue);

	return kTTErrNone;
}

#if 0
#pragma mark -
#pragma mark Some Methods
#endif

TTErr TTViewerReceiveAddressCallback(TTPtr baton, TTValue& data)
{
	return kTTErrGeneric;
}

TTErr TTViewerReceiveValueCallback(TTPtr baton, TTValue& data)
{
	TTViewerPtr aViewer;
	TTValuePtr	b;
	TTValue		converted;

	// unpack baton (a TTViewer)
	b = (TTValuePtr)baton;
	b->get(0, (TTPtr*)&aViewer);

	if (aViewer->mEnable) {

		if (!aViewer->mFreeze)
			// convert data
			aViewer->convertUnit(data, converted);

		else
			// use last data
			converted = aViewer->mReturnedValue;

		// return value
		if (aViewer->mReturnValueCallback) {
			aViewer->mReturnValueCallback->notify(converted, kTTValNONE);
			aViewer->setReturnedValue(converted);
		}
	}

	return kTTErrNone;
}

TTErr TTViewerDataspaceCallback(TTPtr baton, TTValue& data)
{
	TTViewerPtr aViewer;
	TTValuePtr	b;
	TTValue		v;
	TTErr		err;

	// unpack baton (a TTViewer)
	b = (TTValuePtr)baton;
	b->get(0, (TTPtr*)&aViewer);

	aViewer->mDataspace = data;

	if (aViewer->mDataspaceConverter) {
		aViewer->mDataspaceConverter->setAttributeValue(TT("dataspace"), aViewer->mDataspace);

		err = aViewer->mDataspaceConverter->setAttributeValue(TT("outputUnit"), aViewer->mDataspaceUnit);
		if (err) {
			aViewer->mDataspaceConverter->getAttributeValue(TT("outputUnit"), v);
			v.get(0, &aViewer->mDataspaceUnit);
			aViewer->mDataspaceConverter->setAttributeValue(TT("outputUnit"), aViewer->mDataspaceUnit);
		}
	}

	return kTTErrNone;
}

TTErr TTViewerDataspaceUnitCallback(TTPtr baton, TTValue& data)
{
	TTViewerPtr aViewer;
	TTValuePtr	b;
	TTValue		v;

	// unpack baton (a TTViewer)
	b = (TTValuePtr)baton;
	b->get(0, (TTPtr*)&aViewer);

	if (aViewer->mDataspaceConverter)
		aViewer->mDataspaceConverter->setAttributeValue(TT("inputUnit"), data);

	return kTTErrNone;
}
<commit_msg>Fixing Bug 1138<commit_after>/*
 * A Viewer Object
 * Copyright © 2010, Théo de la Hogue
 *
 * License: This code is licensed under the terms of the "New BSD License"
 * http://creativecommons.org/licenses/BSD/
 */

#include "TTViewer.h"

#define thisTTClass			TTViewer
#define thisTTClassName		"Viewer"
#define thisTTClassTags		"viewer"

TT_MODULAR_CONSTRUCTOR,
mAddress(kTTAdrsEmpty),
mDescription(kTTSymEmpty),
mType(kTTSymEmpty),
mTag(kTTSymEmpty),
mHighlight(NO),
mFreeze(NO),
mDataspace(kTTSym_none),
mDataspaceUnit(kTTSym_none),
mDataspaceConverter(NULL),
mDataspaceObserver(NULL),
mDataspaceUnitObserver(NULL),
mEnable(YES),
mReturnedValue(kTTValNONE),
mReceiver(NULL),
mSender(NULL),
mReturnValueCallback(NULL)
{
	if(arguments.getSize() == 1)
		arguments.get(0, (TTPtr*)&mReturnValueCallback);

	addAttributeWithSetter(Address, kTypeSymbol);
	addAttribute(Description, kTypeSymbol);
	addAttribute(Type, kTypeSymbol);
	addAttribute(Tag, kTypeSymbol);
	addAttribute(Highlight, kTypeBoolean);
	addAttributeWithSetter(Freeze, kTypeBoolean);

	addAttribute(Dataspace, kTypeSymbol);
	addAttributeProperty(Dataspace, readOnly, YES);
	addAttributeWithSetter(DataspaceUnit, kTypeSymbol);

	addAttributeWithSetter(Enable, kTypeBoolean);

	addAttributeWithSetter(ReturnedValue, kTypeLocalValue);
	addAttributeProperty(ReturnedValue, readOnly, YES);
	addAttributeProperty(ReturnedValue, hidden, YES);

	addMessage(Refresh);

	addMessageWithArguments(Send);
	addMessageProperty(Send, hidden, YES);

	TTObjectInstantiate(TT("dataspace"),  &mDataspaceConverter, kTTValNONE);
}

TTViewer::~TTViewer() // TODO : delete things...
{
	if (mReturnValueCallback) {
		delete (TTValuePtr)mReturnValueCallback->getBaton();
		TTObjectRelease(TTObjectHandle(&mReturnValueCallback));
	}

	if (mDataspaceConverter)
		TTObjectRelease(TTObjectHandle(&mDataspaceConverter));

	if (mDataspaceObserver)
		TTObjectRelease(TTObjectHandle(&mDataspaceObserver));

	if (mDataspaceUnitObserver)
		TTObjectRelease(TTObjectHandle(&mDataspaceUnitObserver));

	if (mSender)
		TTObjectRelease(TTObjectHandle(&mSender));

	if (mReceiver)
		TTObjectRelease(TTObjectHandle(&mReceiver));
}

TTErr TTViewer::setAddress(const TTValue& value)
{
	value.get(0, &mAddress);

	bind();

	return kTTErrNone;
}

TTErr TTViewer::bind()
{
	TTValue			args, v;
	TTObjectPtr		returnAddressCallback, returnValueCallback;
	TTValuePtr		returnAddressBaton, returnValueBaton;

	// Prepare arguments
	if (mAddress == kTTAdrsEmpty)
		return kTTErrGeneric;

	// The default attribute to bind is value
	if (mAddress->getAttribute() == NO_ATTRIBUTE)
		mAddress->appendAttribute(kTTSym_value);

	// Replace a TTSender object
	if (mSender)
		TTObjectRelease(TTObjectHandle(&mSender));

	mSender = NULL;							// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("Sender"), TTObjectHandle(&mSender), kTTValNONE);

	mSender->setAttributeValue(kTTSym_address, mAddress);

	// Replace a TTReceiver object
	if (mReceiver)
		TTObjectRelease(TTObjectHandle(&mReceiver));

	returnAddressCallback = NULL;			// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("callback"), &returnAddressCallback, kTTValNONE);
	returnAddressBaton = new TTValue(TTPtr(this));
	returnAddressCallback->setAttributeValue(kTTSym_baton, TTPtr(returnAddressBaton));
	returnAddressCallback->setAttributeValue(kTTSym_function, TTPtr(&TTViewerReceiveAddressCallback));
	args.append(returnAddressCallback);

	returnValueCallback = NULL;				// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("callback"), &returnValueCallback, kTTValNONE);
	returnValueBaton = new TTValue(TTPtr(this));
	returnValueCallback->setAttributeValue(kTTSym_baton, TTPtr(returnValueBaton));
	returnValueCallback->setAttributeValue(kTTSym_function, TTPtr(&TTViewerReceiveValueCallback));
	args.append(returnValueCallback);

	mReceiver = NULL;						// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("Receiver"), TTObjectHandle(&mReceiver), args);

	mReceiver->setAttributeValue(kTTSym_address, mAddress);

	observeDataspace();
	observeDataspaceUnit();

	return kTTErrNone;
}

TTErr TTViewer::observeDataspace()
{
	TTValue			args;
	TTObjectPtr		returnDataspaceCallback;
	TTValuePtr		returnDataspaceBaton;

	if (mDataspaceObserver)
		TTObjectRelease(TTObjectHandle(&mDataspaceObserver));

	// Make a TTReceiver object
	args.append(NULL);

	returnDataspaceCallback = NULL;				// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("callback"), &returnDataspaceCallback, kTTValNONE);
	returnDataspaceBaton = new TTValue(TTPtr(this));
	returnDataspaceCallback->setAttributeValue(kTTSym_baton, TTPtr(returnDataspaceBaton));
	returnDataspaceCallback->setAttributeValue(kTTSym_function, TTPtr(&TTViewerDataspaceCallback));
	args.append(returnDataspaceCallback);

	mDataspaceObserver = NULL;
	TTObjectInstantiate(TT("Receiver"), TTObjectHandle(&mDataspaceObserver), args);

	mDataspaceObserver->setAttributeValue(kTTSym_address, mAddress->appendAttribute(kTTSym_dataspace));

	mDataspaceObserver->sendMessage(kTTSym_Get);

	return kTTErrNone;
}

TTErr TTViewer::observeDataspaceUnit()
{
	TTValue			args;
	TTObjectPtr		returnDataspaceUnitCallback;
	TTValuePtr		returnDataspaceUnitBaton;

	if (mDataspaceUnitObserver)
		TTObjectRelease(TTObjectHandle(&mDataspaceUnitObserver));

	// Make a TTReceiver object
	args.append(NULL);

	returnDataspaceUnitCallback = NULL;				// without this, TTObjectInstantiate try to release an oldObject that doesn't exist ... Is it good ?
	TTObjectInstantiate(TT("callback"), &returnDataspaceUnitCallback, kTTValNONE);
	returnDataspaceUnitBaton = new TTValue(TTPtr(this));
	returnDataspaceUnitCallback->setAttributeValue(kTTSym_baton, TTPtr(returnDataspaceUnitBaton));
	returnDataspaceUnitCallback->setAttributeValue(kTTSym_function, TTPtr(&TTViewerDataspaceUnitCallback));
	args.append(returnDataspaceUnitCallback);

	mDataspaceUnitObserver = NULL;
	TTObjectInstantiate(TT("Receiver"), TTObjectHandle(&mDataspaceUnitObserver), args);

	mDataspaceUnitObserver->setAttributeValue(kTTSym_address, mAddress->appendAttribute(kTTSym_dataspaceUnit));

	mDataspaceUnitObserver->sendMessage(kTTSym_Get);

	return kTTErrNone;
}

TTErr TTViewer::setEnable(const TTValue& value)
{
	mEnable = value;

	if (mReceiver)
		mReceiver->setAttributeValue(kTTSym_enable, mEnable);

	return kTTErrNone;
}

TTErr TTViewer::setFreeze(const TTValue& value)
{
	mFreeze = value;

	return kTTErrNone;
}

TTErr TTViewer::setReturnedValue(const TTValue& value)
{
	TTAttributePtr	anAttribute = NULL;
	TTErr			err;

	mReturnedValue = value;

	err = this->findAttribute(kTTSym_returnedValue, &anAttribute);

	if (!err)
		anAttribute->sendNotification(kTTSym_notify, mReturnedValue);	// we use kTTSym_notify because we know that observers are TTCallback

	return kTTErrNone;
}

TTErr TTViewer::Refresh()
{
	if (mReceiver)
		return mReceiver->sendMessage(kTTSym_Get);

	return kTTErrGeneric;
}

TTErr TTViewer::Send(const TTValue& inputValue, TTValue& outputValue)
{
	if (mSender) {

		TTValue valueToSend = inputValue;

		// append view unit
		if (mDataspaceUnit != kTTSym_none)
			valueToSend.append(mDataspaceUnit);

		return mSender->sendMessage(kTTSym_Send, valueToSend, kTTValNONE);
	}

	return kTTErrGeneric;
}

TTErr TTViewer::setDataspaceUnit(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mDataspaceUnit = value;

	if (mDataspaceConverter)
		mDataspaceConverter->setAttributeValue(TT("outputUnit"), mDataspaceUnit);

	// TODO : notifyObservers(kTTSym_dataspaceUnit, n);
	return kTTErrNone;
}

TTErr TTViewer::convertUnit(const TTValue& inputValue, TTValue& outputValue)
{
	if (mDataspaceConverter)
		return mDataspaceConverter->sendMessage(TT("convert"), inputValue, outputValue);

	return kTTErrNone;
}

#if 0
#pragma mark -
#pragma mark Some Methods
#endif

TTErr TTViewerReceiveAddressCallback(TTPtr baton, TTValue& data)
{
	return kTTErrGeneric;
}

TTErr TTViewerReceiveValueCallback(TTPtr baton, TTValue& data)
{
	TTViewerPtr aViewer;
	TTValuePtr	b;
	TTValue		converted;

	// unpack baton (a TTViewer)
	b = (TTValuePtr)baton;
	b->get(0, (TTPtr*)&aViewer);

	if (aViewer->mEnable) {

		if (!aViewer->mFreeze)
			// convert data
			aViewer->convertUnit(data, converted);

		else
			// use last data
			converted = aViewer->mReturnedValue;

		// return value
		if (aViewer->mReturnValueCallback) {
			aViewer->mReturnValueCallback->notify(converted, kTTValNONE);
			aViewer->setReturnedValue(converted);
		}
	}

	return kTTErrNone;
}

TTErr TTViewerDataspaceCallback(TTPtr baton, TTValue& data)
{
	TTViewerPtr aViewer;
	TTValuePtr	b;
	TTValue		v;

	// unpack baton (a TTViewer)
	b = (TTValuePtr)baton;
	b->get(0, (TTPtr*)&aViewer);

	aViewer->mDataspace = data;

	if (aViewer->mDataspaceConverter)
		aViewer->mDataspaceConverter->setAttributeValue(TT("dataspace"), aViewer->mDataspace);

	return kTTErrNone;
}

TTErr TTViewerDataspaceUnitCallback(TTPtr baton, TTValue& data)
{
	TTViewerPtr aViewer;
	TTValuePtr	b;
	TTValue		v;
	TTErr		err;

	// unpack baton (a TTViewer)
	b = (TTValuePtr)baton;
	b->get(0, (TTPtr*)&aViewer);

	if (aViewer->mDataspaceConverter) {

		// set input unit like the data unit
		aViewer->mDataspaceConverter->setAttributeValue(TT("inputUnit"), data);

		// if no unit : set the output unit like the the data unit
		if (aViewer->mDataspaceUnit == kTTSym_none)
			aViewer->mDataspaceUnit = data;

		// if the unit is wrong : use the default unit
		err = aViewer->mDataspaceConverter->setAttributeValue(TT("outputUnit"), aViewer->mDataspaceUnit);
		if (err) {
			aViewer->mDataspaceConverter->getAttributeValue(TT("outputUnit"), v);
			v.get(0, &aViewer->mDataspaceUnit);
			aViewer->mDataspaceConverter->setAttributeValue(TT("outputUnit"), aViewer->mDataspaceUnit);
		}
	}

	return kTTErrNone;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 FinChain, Inc., and contributors.
 *
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/memo.hpp>

namespace graphene { namespace chain {

   /**
    * @ingroup operations
    *
    * @brief lock an amount of one asset for period to get profile gived by system or ...
    *
    *  Fees are paid by the "from" account
    *
    *  @pre amount.amount > 0
    *  @pre fee.amount >= 0
    *  @pre from != to
    *  @post from account's balance will be reduced by fee and amount
    *  @post to account's balance will be increased by amount
    *  @return n/a
    */
   struct lock_balance_operation : public base_operation
   {
      struct fee_parameters_type {
         uint64_t fee       = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
         uint32_t price_per_kbyte = 10 * GRAPHENE_BLOCKCHAIN_PRECISION; /// only required for large memos.
      };

      asset            fee;
      /// Account that lock balance
      account_id_type  issuer;

      /// The amount of asset to operation
      asset            amount;

      uint32_t         period;
      extensions_type   extensions;

      account_id_type fee_payer()const { return issuer; }
      void            validate()const;
      share_type      calculate_fee(const fee_parameters_type& k)const;
   };

   struct set_lock_data_operation : public base_operation
   {
      struct fee_parameters_type {
         uint64_t fee       = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
         uint32_t price_per_kbyte = 10 * GRAPHENE_BLOCKCHAIN_PRECISION; /// only required for large memos.
      };

      asset             fee;
      /// Account that lock balance
      account_id_type   issuer;
      uint64_t          nominal_interest_rate; //
      uint16_t          reward_coefficient;
      asset             init_interest_pool;
      extensions_type   extensions;

      account_id_type fee_payer()const { return issuer; }
      void            validate()const;
      share_type      calculate_fee(const fee_parameters_type& k)const;
   };

}} // graphene::chain

FC_REFLECT( graphene::chain::lock_balance_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::lock_balance_operation, (fee)(issuer)(amount)(extensions) )

FC_REFLECT( graphene::chain::set_lock_data_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::set_lock_data_operation, (fee)(issuer)(nominal_interest_rate)(reward_coefficient)(init_interest_pool)(extensions) )

<commit_msg>add  unlock_balance_operation<commit_after>/*
 * Copyright (c) 2017 FinChain, Inc., and contributors.
 *
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/memo.hpp>

namespace graphene { namespace chain {

   /**
    * @ingroup operations
    *
    * @brief lock an amount of one asset for period to get profile gived by system or ...
    *
    *  Fees are paid by the "from" account
    *
    *  @pre amount.amount > 0
    *  @pre fee.amount >= 0
    *  @pre from != to
    *  @post from account's balance will be reduced by fee and amount
    *  @post to account's balance will be increased by amount
    *  @return n/a
    */
   struct lock_balance_operation : public base_operation
   {
      struct fee_parameters_type {
         uint64_t fee       = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
         uint32_t price_per_kbyte = 10 * GRAPHENE_BLOCKCHAIN_PRECISION; /// only required for large memos.
      };

      asset            fee;
      /// Account that lock balance
      account_id_type  issuer;

      /// The amount of asset to operation
      asset            amount;

      uint32_t         period;
      extensions_type   extensions;

      account_id_type fee_payer()const { return issuer; }
      void            validate()const;
      share_type      calculate_fee(const fee_parameters_type& k)const;
   };

   struct set_lock_data_operation : public base_operation
   {
      struct fee_parameters_type {
         uint64_t fee       = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
         uint32_t price_per_kbyte = 10 * GRAPHENE_BLOCKCHAIN_PRECISION; /// only required for large memos.
      };

      asset             fee;
      /// Account that lock balance
      account_id_type   issuer;
      uint64_t          nominal_interest_rate; //
      uint16_t          reward_coefficient;
      asset             init_interest_pool;
      extensions_type   extensions;

      account_id_type fee_payer()const { return issuer; }
      void            validate()const;
      share_type      calculate_fee(const fee_parameters_type& k)const;
   };

   struct unlock_balance_operation : public base_operation
   {
      struct fee_parameters_type {
         uint64_t fee       = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
         uint32_t price_per_kbyte = 10 * GRAPHENE_BLOCKCHAIN_PRECISION; /// only required for large memos.
      };

      enum unlock_type{
         expire，
         in_advance
      };

      struct unlock_detail{
         locked_balance_id_type  locked_id;
         unlock_type             type;
      };

      asset            fee;
      /// Account that lock balance
      account_id_type  issuer;

      /// The amount of asset to operation
      vector<unlock_detail>            locked;

      extensions_type   extensions;

      account_id_type fee_payer()const { return issuer; }
      void            validate()const;
      share_type      calculate_fee(const fee_parameters_type& k)const;
   };

}} // graphene::chain

FC_REFLECT( graphene::chain::lock_balance_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::lock_balance_operation, (fee)(issuer)(amount)(extensions) )

FC_REFLECT( graphene::chain::set_lock_data_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::set_lock_data_operation, (fee)(issuer)(nominal_interest_rate)(reward_coefficient)(init_interest_pool)(extensions) )

<|endoftext|>


<commit_before>/*************************************************************************/
/*  message_queue.cpp                                                    */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "message_queue.h"

#include "core/project_settings.h"
#include "core/script_language.h"

MessageQueue *MessageQueue::singleton = NULL;

MessageQueue *MessageQueue::get_singleton() {

	return singleton;
}

Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) {

	_THREAD_SAFE_METHOD_

	int room_needed = sizeof(Message) + sizeof(Variant) * p_argcount;

	if ((buffer_end + room_needed) >= buffer_size) {
		String type;
		if (ObjectDB::get_instance(p_id))
			type = ObjectDB::get_instance(p_id)->get_class();
		print_line("Failed method: " + type + ":" + p_method + " target ID: " + itos(p_id));
		statistics();
		ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings.");
	}

	Message *msg = memnew_placement(&buffer[buffer_end], Message);
	msg->args = p_argcount;
	msg->instance_id = p_id;
	msg->target = p_method;
	msg->type = TYPE_CALL;
	if (p_show_error)
		msg->type |= FLAG_SHOW_ERROR;

	buffer_end += sizeof(Message);

	for (int i = 0; i < p_argcount; i++) {

		Variant *v = memnew_placement(&buffer[buffer_end], Variant);
		buffer_end += sizeof(Variant);
		*v = *p_args[i];
	}

	return OK;
}

Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {

	VARIANT_ARGPTRS;

	int argc = 0;

	for (int i = 0; i < VARIANT_ARG_MAX; i++) {
		if (argptr[i]->get_type() == Variant::NIL)
			break;
		argc++;
	}

	return push_call(p_id, p_method, argptr, argc, false);
}

Error MessageQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value) {

	_THREAD_SAFE_METHOD_

	uint8_t room_needed = sizeof(Message) + sizeof(Variant);

	if ((buffer_end + room_needed) >= buffer_size) {
		String type;
		if (ObjectDB::get_instance(p_id))
			type = ObjectDB::get_instance(p_id)->get_class();
		print_line("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id));
		statistics();
		ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings.");
	}

	Message *msg = memnew_placement(&buffer[buffer_end], Message);
	msg->args = 1;
	msg->instance_id = p_id;
	msg->target = p_prop;
	msg->type = TYPE_SET;

	buffer_end += sizeof(Message);

	Variant *v = memnew_placement(&buffer[buffer_end], Variant);
	buffer_end += sizeof(Variant);
	*v = p_value;

	return OK;
}

Error MessageQueue::push_notification(ObjectID p_id, int p_notification) {

	_THREAD_SAFE_METHOD_

	ERR_FAIL_COND_V(p_notification < 0, ERR_INVALID_PARAMETER);

	uint8_t room_needed = sizeof(Message);

	if ((buffer_end + room_needed) >= buffer_size) {
		print_line("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id));
		statistics();
		ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings.");
	}

	Message *msg = memnew_placement(&buffer[buffer_end], Message);

	msg->type = TYPE_NOTIFICATION;
	msg->instance_id = p_id;
	//msg->target;
	msg->notification = p_notification;

	buffer_end += sizeof(Message);

	return OK;
}

Error MessageQueue::push_call(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {

	return push_call(p_object->get_instance_id(), p_method, VARIANT_ARG_PASS);
}

Error MessageQueue::push_notification(Object *p_object, int p_notification) {

	return push_notification(p_object->get_instance_id(), p_notification);
}
Error MessageQueue::push_set(Object *p_object, const StringName &p_prop, const Variant &p_value) {

	return push_set(p_object->get_instance_id(), p_prop, p_value);
}

void MessageQueue::statistics() {

	Map<StringName, int> set_count;
	Map<int, int> notify_count;
	Map<StringName, int> call_count;
	int null_count = 0;

	uint32_t read_pos = 0;
	while (read_pos < buffer_end) {
		Message *message = (Message *)&buffer[read_pos];

		Object *target = ObjectDB::get_instance(message->instance_id);

		if (target != NULL) {

			switch (message->type & FLAG_MASK) {

				case TYPE_CALL: {

					if (!call_count.has(message->target))
						call_count[message->target] = 0;

					call_count[message->target]++;

				} break;
				case TYPE_NOTIFICATION: {

					if (!notify_count.has(message->notification))
						notify_count[message->notification] = 0;

					notify_count[message->notification]++;

				} break;
				case TYPE_SET: {

					if (!set_count.has(message->target))
						set_count[message->target] = 0;

					set_count[message->target]++;

				} break;
			}

		} else {
			//object was deleted
			print_line("Object was deleted while awaiting a callback");

			null_count++;
		}

		read_pos += sizeof(Message);
		if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION)
			read_pos += sizeof(Variant) * message->args;
	}

	print_line("TOTAL BYTES: " + itos(buffer_end));
	print_line("NULL count: " + itos(null_count));

	for (Map<StringName, int>::Element *E = set_count.front(); E; E = E->next()) {
		print_line("SET " + E->key() + ": " + itos(E->get()));
	}

	for (Map<StringName, int>::Element *E = call_count.front(); E; E = E->next()) {
		print_line("CALL " + E->key() + ": " + itos(E->get()));
	}

	for (Map<int, int>::Element *E = notify_count.front(); E; E = E->next()) {
		print_line("NOTIFY " + itos(E->key()) + ": " + itos(E->get()));
	}
}

int MessageQueue::get_max_buffer_usage() const {

	return buffer_max_used;
}

void MessageQueue::_call_function(Object *p_target, const StringName &p_func, const Variant *p_args, int p_argcount, bool p_show_error) {

	const Variant **argptrs = NULL;
	if (p_argcount) {
		argptrs = (const Variant **)alloca(sizeof(Variant *) * p_argcount);
		for (int i = 0; i < p_argcount; i++) {
			argptrs[i] = &p_args[i];
		}
	}

	Variant::CallError ce;
	p_target->call(p_func, argptrs, p_argcount, ce);
	if (p_show_error && ce.error != Variant::CallError::CALL_OK) {

		ERR_PRINTS("Error calling deferred method: " + Variant::get_call_error_text(p_target, p_func, argptrs, p_argcount, ce) + ".");
	}
}

void MessageQueue::flush() {

	if (buffer_end > buffer_max_used) {
		buffer_max_used = buffer_end;
	}

	uint32_t read_pos = 0;

	//using reverse locking strategy
	_THREAD_SAFE_LOCK_

	ERR_FAIL_COND(flushing); //already flushing, you did something odd
	flushing = true;

	while (read_pos < buffer_end) {

		//lock on each iteration, so a call can re-add itself to the message queue

		Message *message = (Message *)&buffer[read_pos];

		uint32_t advance = sizeof(Message);
		if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION)
			advance += sizeof(Variant) * message->args;

		//pre-advance so this function is reentrant
		read_pos += advance;

		_THREAD_SAFE_UNLOCK_

		Object *target = ObjectDB::get_instance(message->instance_id);

		if (target != NULL) {

			switch (message->type & FLAG_MASK) {
				case TYPE_CALL: {

					Variant *args = (Variant *)(message + 1);

					// messages don't expect a return value

					_call_function(target, message->target, args, message->args, message->type & FLAG_SHOW_ERROR);

				} break;
				case TYPE_NOTIFICATION: {

					// messages don't expect a return value
					target->notification(message->notification);

				} break;
				case TYPE_SET: {

					Variant *arg = (Variant *)(message + 1);
					// messages don't expect a return value
					target->set(message->target, *arg);

				} break;
			}
		}

		if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
			Variant *args = (Variant *)(message + 1);
			for (int i = 0; i < message->args; i++) {
				args[i].~Variant();
			}
		}

		message->~Message();

		_THREAD_SAFE_LOCK_
	}

	buffer_end = 0; // reset buffer
	flushing = false;
	_THREAD_SAFE_UNLOCK_
}

bool MessageQueue::is_flushing() const {

	return flushing;
}

MessageQueue::MessageQueue() {

	ERR_FAIL_COND_MSG(singleton != NULL, "MessageQueue singleton already exist.");
	singleton = this;
	flushing = false;

	buffer_end = 0;
	buffer_max_used = 0;
	buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB);
	ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/message_queue/max_size_kb", PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "0,2048,1,or_greater"));
	buffer_size *= 1024;
	buffer = memnew_arr(uint8_t, buffer_size);
}

MessageQueue::~MessageQueue() {

	uint32_t read_pos = 0;

	while (read_pos < buffer_end) {

		Message *message = (Message *)&buffer[read_pos];
		Variant *args = (Variant *)(message + 1);
		int argc = message->args;
		if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
			for (int i = 0; i < argc; i++)
				args[i].~Variant();
		}
		message->~Message();

		read_pos += sizeof(Message);
		if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION)
			read_pos += sizeof(Variant) * message->args;
	}

	singleton = NULL;
	memdelete_arr(buffer);
}
<commit_msg>Tweak the message queue maximum size property hint<commit_after>/*************************************************************************/
/*  message_queue.cpp                                                    */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "message_queue.h"

#include "core/project_settings.h"
#include "core/script_language.h"

MessageQueue *MessageQueue::singleton = NULL;

MessageQueue *MessageQueue::get_singleton() {

	return singleton;
}

Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) {

	_THREAD_SAFE_METHOD_

	int room_needed = sizeof(Message) + sizeof(Variant) * p_argcount;

	if ((buffer_end + room_needed) >= buffer_size) {
		String type;
		if (ObjectDB::get_instance(p_id))
			type = ObjectDB::get_instance(p_id)->get_class();
		print_line("Failed method: " + type + ":" + p_method + " target ID: " + itos(p_id));
		statistics();
		ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings.");
	}

	Message *msg = memnew_placement(&buffer[buffer_end], Message);
	msg->args = p_argcount;
	msg->instance_id = p_id;
	msg->target = p_method;
	msg->type = TYPE_CALL;
	if (p_show_error)
		msg->type |= FLAG_SHOW_ERROR;

	buffer_end += sizeof(Message);

	for (int i = 0; i < p_argcount; i++) {

		Variant *v = memnew_placement(&buffer[buffer_end], Variant);
		buffer_end += sizeof(Variant);
		*v = *p_args[i];
	}

	return OK;
}

Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {

	VARIANT_ARGPTRS;

	int argc = 0;

	for (int i = 0; i < VARIANT_ARG_MAX; i++) {
		if (argptr[i]->get_type() == Variant::NIL)
			break;
		argc++;
	}

	return push_call(p_id, p_method, argptr, argc, false);
}

Error MessageQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value) {

	_THREAD_SAFE_METHOD_

	uint8_t room_needed = sizeof(Message) + sizeof(Variant);

	if ((buffer_end + room_needed) >= buffer_size) {
		String type;
		if (ObjectDB::get_instance(p_id))
			type = ObjectDB::get_instance(p_id)->get_class();
		print_line("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id));
		statistics();
		ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings.");
	}

	Message *msg = memnew_placement(&buffer[buffer_end], Message);
	msg->args = 1;
	msg->instance_id = p_id;
	msg->target = p_prop;
	msg->type = TYPE_SET;

	buffer_end += sizeof(Message);

	Variant *v = memnew_placement(&buffer[buffer_end], Variant);
	buffer_end += sizeof(Variant);
	*v = p_value;

	return OK;
}

Error MessageQueue::push_notification(ObjectID p_id, int p_notification) {

	_THREAD_SAFE_METHOD_

	ERR_FAIL_COND_V(p_notification < 0, ERR_INVALID_PARAMETER);

	uint8_t room_needed = sizeof(Message);

	if ((buffer_end + room_needed) >= buffer_size) {
		print_line("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id));
		statistics();
		ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings.");
	}

	Message *msg = memnew_placement(&buffer[buffer_end], Message);

	msg->type = TYPE_NOTIFICATION;
	msg->instance_id = p_id;
	//msg->target;
	msg->notification = p_notification;

	buffer_end += sizeof(Message);

	return OK;
}

Error MessageQueue::push_call(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {

	return push_call(p_object->get_instance_id(), p_method, VARIANT_ARG_PASS);
}

Error MessageQueue::push_notification(Object *p_object, int p_notification) {

	return push_notification(p_object->get_instance_id(), p_notification);
}
Error MessageQueue::push_set(Object *p_object, const StringName &p_prop, const Variant &p_value) {

	return push_set(p_object->get_instance_id(), p_prop, p_value);
}

void MessageQueue::statistics() {

	Map<StringName, int> set_count;
	Map<int, int> notify_count;
	Map<StringName, int> call_count;
	int null_count = 0;

	uint32_t read_pos = 0;
	while (read_pos < buffer_end) {
		Message *message = (Message *)&buffer[read_pos];

		Object *target = ObjectDB::get_instance(message->instance_id);

		if (target != NULL) {

			switch (message->type & FLAG_MASK) {

				case TYPE_CALL: {

					if (!call_count.has(message->target))
						call_count[message->target] = 0;

					call_count[message->target]++;

				} break;
				case TYPE_NOTIFICATION: {

					if (!notify_count.has(message->notification))
						notify_count[message->notification] = 0;

					notify_count[message->notification]++;

				} break;
				case TYPE_SET: {

					if (!set_count.has(message->target))
						set_count[message->target] = 0;

					set_count[message->target]++;

				} break;
			}

		} else {
			//object was deleted
			print_line("Object was deleted while awaiting a callback");

			null_count++;
		}

		read_pos += sizeof(Message);
		if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION)
			read_pos += sizeof(Variant) * message->args;
	}

	print_line("TOTAL BYTES: " + itos(buffer_end));
	print_line("NULL count: " + itos(null_count));

	for (Map<StringName, int>::Element *E = set_count.front(); E; E = E->next()) {
		print_line("SET " + E->key() + ": " + itos(E->get()));
	}

	for (Map<StringName, int>::Element *E = call_count.front(); E; E = E->next()) {
		print_line("CALL " + E->key() + ": " + itos(E->get()));
	}

	for (Map<int, int>::Element *E = notify_count.front(); E; E = E->next()) {
		print_line("NOTIFY " + itos(E->key()) + ": " + itos(E->get()));
	}
}

int MessageQueue::get_max_buffer_usage() const {

	return buffer_max_used;
}

void MessageQueue::_call_function(Object *p_target, const StringName &p_func, const Variant *p_args, int p_argcount, bool p_show_error) {

	const Variant **argptrs = NULL;
	if (p_argcount) {
		argptrs = (const Variant **)alloca(sizeof(Variant *) * p_argcount);
		for (int i = 0; i < p_argcount; i++) {
			argptrs[i] = &p_args[i];
		}
	}

	Variant::CallError ce;
	p_target->call(p_func, argptrs, p_argcount, ce);
	if (p_show_error && ce.error != Variant::CallError::CALL_OK) {

		ERR_PRINTS("Error calling deferred method: " + Variant::get_call_error_text(p_target, p_func, argptrs, p_argcount, ce) + ".");
	}
}

void MessageQueue::flush() {

	if (buffer_end > buffer_max_used) {
		buffer_max_used = buffer_end;
	}

	uint32_t read_pos = 0;

	//using reverse locking strategy
	_THREAD_SAFE_LOCK_

	ERR_FAIL_COND(flushing); //already flushing, you did something odd
	flushing = true;

	while (read_pos < buffer_end) {

		//lock on each iteration, so a call can re-add itself to the message queue

		Message *message = (Message *)&buffer[read_pos];

		uint32_t advance = sizeof(Message);
		if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION)
			advance += sizeof(Variant) * message->args;

		//pre-advance so this function is reentrant
		read_pos += advance;

		_THREAD_SAFE_UNLOCK_

		Object *target = ObjectDB::get_instance(message->instance_id);

		if (target != NULL) {

			switch (message->type & FLAG_MASK) {
				case TYPE_CALL: {

					Variant *args = (Variant *)(message + 1);

					// messages don't expect a return value

					_call_function(target, message->target, args, message->args, message->type & FLAG_SHOW_ERROR);

				} break;
				case TYPE_NOTIFICATION: {

					// messages don't expect a return value
					target->notification(message->notification);

				} break;
				case TYPE_SET: {

					Variant *arg = (Variant *)(message + 1);
					// messages don't expect a return value
					target->set(message->target, *arg);

				} break;
			}
		}

		if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
			Variant *args = (Variant *)(message + 1);
			for (int i = 0; i < message->args; i++) {
				args[i].~Variant();
			}
		}

		message->~Message();

		_THREAD_SAFE_LOCK_
	}

	buffer_end = 0; // reset buffer
	flushing = false;
	_THREAD_SAFE_UNLOCK_
}

bool MessageQueue::is_flushing() const {

	return flushing;
}

MessageQueue::MessageQueue() {

	ERR_FAIL_COND_MSG(singleton != NULL, "A MessageQueue singleton already exists.");
	singleton = this;
	flushing = false;

	buffer_end = 0;
	buffer_max_used = 0;
	buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB);
	ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/message_queue/max_size_kb", PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater"));
	buffer_size *= 1024;
	buffer = memnew_arr(uint8_t, buffer_size);
}

MessageQueue::~MessageQueue() {

	uint32_t read_pos = 0;

	while (read_pos < buffer_end) {

		Message *message = (Message *)&buffer[read_pos];
		Variant *args = (Variant *)(message + 1);
		int argc = message->args;
		if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
			for (int i = 0; i < argc; i++)
				args[i].~Variant();
		}
		message->~Message();

		read_pos += sizeof(Message);
		if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION)
			read_pos += sizeof(Variant) * message->args;
	}

	singleton = NULL;
	memdelete_arr(buffer);
}
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright (c) 2013 Aitor Aldoma, Thomas Faeulhammer
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 ******************************************************************************/

#include <pcl/common/angles.h>
#include <pcl/common/io.h>
#include <pcl/common/transforms.h>
#include <pcl/filters/filter.h>
#include <pcl/visualization/pcl_visualizer.h>

#include <v4r/registration/noise_model_based_cloud_integration.h>
#include <v4r/common/organized_edge_detection.h>

#include <glog/logging.h>

namespace v4r
{

template<typename PointT>
void
NMBasedCloudIntegration<PointT>::collectInfo ()
{
    for(size_t i=0; i < input_clouds_.size(); i++)
    {
        pcl::PointCloud<PointT> cloud_aligned;
        pcl::transformPointCloud(*input_clouds_[i], cloud_aligned, transformations_to_global_[i]);
        pcl::PointCloud<pcl::Normal> normals_aligned;
        transformNormals(*input_normals_[i], normals_aligned, transformations_to_global_[i]);

        size_t existing_pts = big_cloud_info_.size();
        size_t kept_new_pts = 0;

        if (indices_.empty())
        {
            big_cloud_info_.resize( existing_pts + cloud_aligned.points.size());

            for(size_t jj=0; jj<cloud_aligned.points.size(); jj++)
            {
                if ( !pcl::isFinite(cloud_aligned.points[jj]))
                    continue;

                PointInfo &pt = big_cloud_info_ [ existing_pts + kept_new_pts ];
                pt.pt = cloud_aligned.points[jj];
                pt.normal = normals_aligned.points[jj];
                pt.sigma_lateral = pt_properties_[i][jj][0];
                pt.sigma_axial = pt_properties_[i][jj][1];
                pt.distance_to_depth_discontinuity = pt_properties_[i][jj][2];
                kept_new_pts++;
            }
        }
        else
        {
            big_cloud_info_.resize( existing_pts + indices_[i].size());

            for(const auto idx : indices_[i])
            {
                if(!pcl::isFinite(cloud_aligned.points[idx]))
                    continue;

                PointInfo &pt = big_cloud_info_ [ existing_pts + kept_new_pts ];
                pt.pt = cloud_aligned.points[idx];
                pt.normal = normals_aligned.points[ idx ];
                pt.sigma_lateral = pt_properties_[i][idx][0];
                pt.sigma_axial = pt_properties_[i][idx][1];
                pt.distance_to_depth_discontinuity = pt_properties_[i][idx][2];
                kept_new_pts++;
            }
        }
        big_cloud_info_.resize( existing_pts + kept_new_pts);

        // compute and store remaining information
        for(size_t jj=0; jj<kept_new_pts; jj++)
        {
            PointInfo &pt = big_cloud_info_ [ existing_pts + jj ];
            pt.origin = i;

            Eigen::Matrix3f sigma = Eigen::Matrix3f::Zero(), sigma_aligned = Eigen::Matrix3f::Zero();
            sigma(0,0) = pt.sigma_lateral;
            sigma(1,1) = pt.sigma_lateral;
            sigma(2,2) = pt.sigma_axial;

            const Eigen::Matrix4f &tf = transformations_to_global_[ i ];
            Eigen::Matrix3f rotation = tf.block<3,3>(0,0); // or inverse?
            sigma_aligned = rotation * sigma * rotation.transpose();

            pt.probability = 1/ sqrt(2 * M_PI * sigma_aligned.determinant());
        }
    }
}

template<typename PointT>
void
NMBasedCloudIntegration<PointT>::reasonAboutPts ()
{
    const int width = input_clouds_[0]->width;
    const int height = input_clouds_[0]->height;
    const float cx = static_cast<float> (width) / 2.f;// - 0.5f;
    const float cy = static_cast<float> (height) / 2.f;// - 0.5f;

//    pcl::visualization::PCLVisualizer vis;
    for (size_t i=0; i<big_cloud_info_.size(); i++)
    {
        PointInfo &pt = big_cloud_info_[i];
        const PointT &ptt = pt.pt;

        for (size_t cloud=0; cloud<input_clouds_.size(); cloud++)
        {
            if( pt.origin == cloud)  // we don't have to reason about the point with respect to the original cloud
                continue;

            // reproject point onto the cloud's image plane and check if its within FOV and if so, if it can be seen or is occluded
            const Eigen::Matrix4f &tf = transformations_to_global_[cloud].inverse() * transformations_to_global_[pt.origin];
            float x = static_cast<float> (tf (0, 0) * ptt.x + tf (0, 1) * ptt.y + tf (0, 2) * ptt.z + tf (0, 3));
            float y = static_cast<float> (tf (1, 0) * ptt.x + tf (1, 1) * ptt.y + tf (1, 2) * ptt.z + tf (1, 3));
            float z = static_cast<float> (tf (2, 0) * ptt.x + tf (2, 1) * ptt.y + tf (2, 2) * ptt.z + tf (2, 3));

            int u = static_cast<int> (param_.focal_length_ * x / z + cx);
            int v = static_cast<int> (param_.focal_length_ * y / z + cy);

            std::cout << "u: " <<u << ", v: " << v << std::endl;

            PointT ptt_aligned;
            ptt_aligned.x = x;
            ptt_aligned.y = y;
            ptt_aligned.z = z;

            if( u<0 || v <0 || u>=width || v >= height )
                pt.occluded_++;
            else
            {
                float thresh = param_.threshold_explained_;
                if( z > 1.f )
                    thresh+= param_.threshold_explained_ * (z-1.f) * (z-1.f);

               const float z_c = input_clouds_[cloud]->points[ v*width + u ].z;
               if ( std::abs(z_c - z) < thresh )
                   pt.explained_++;
               else if (z_c > z )
               {
                   pt.violated_ ++;

//                   vis.removeAllShapes();
//                   vis.removeAllPointClouds();
//                   vis.addPointCloud(input_clouds_[cloud]);
//                   vis.addSphere(ptt_aligned, 0.03f, 1.f, 0.f, 0.f);
//                   vis.spin();
               }
               else
                   pt.occluded_ ++;
            }

        }
    }
}

template<typename PointT>
void
NMBasedCloudIntegration<PointT>::compute (PointTPtr & output)
{
    if(input_clouds_.empty()) {
        std::cerr << "No input clouds set for cloud integration!" << std::endl;
        return;
    }

    big_cloud_info_.clear();

    collectInfo();

    if(param_.reason_about_points_)
        reasonAboutPts();

    pcl::octree::OctreePointCloudPointVector<PointT> octree( param_.octree_resolution_ );
    PointTPtr big_cloud ( new pcl::PointCloud<PointT>());
    big_cloud->width = big_cloud_info_.size();
    big_cloud->height = 1;
    big_cloud->points.resize( big_cloud_info_.size() );
    for(size_t i=0; i < big_cloud_info_.size(); i++)
        big_cloud->points[i] = big_cloud_info_[i].pt;
    octree.setInputCloud( big_cloud );
    octree.addPointsFromInputCloud();

    typename pcl::octree::OctreePointCloudPointVector<PointT>::LeafNodeIterator leaf_it;
    const typename pcl::octree::OctreePointCloudPointVector<PointT>::LeafNodeIterator it2_end = octree.leaf_end();

    output->points.resize( big_cloud_info_.size() );
    output_normals_.reset(new pcl::PointCloud<pcl::Normal>);
    output_normals_->points.resize( big_cloud_info_.size());

    size_t kept = 0;
    size_t total_used = 0;

    for (leaf_it = octree.leaf_begin(); leaf_it != it2_end; ++leaf_it)
    {
        pcl::octree::OctreeContainerPointIndices& container = leaf_it.getLeafContainer();

        // add points from leaf node to indexVector
        std::vector<int> indexVector;
        container.getPointIndices (indexVector);

        if(indexVector.empty() || indexVector.size() < param_.min_points_per_voxel_)
            continue;

        std::vector<PointInfo> voxel_pts ( indexVector.size() );

        for(size_t k=0; k < indexVector.size(); k++)
            voxel_pts[k] = big_cloud_info_ [indexVector[k]];

        PointT p;
        pcl::Normal n;

        if(param_.average_)
        {
            p.getVector3fMap() = Eigen::Vector3f::Zero();
            p.r = p.g = p.b = 0.f;
            n.getNormalVector3fMap() = Eigen::Vector3f::Zero();
            n.curvature = 0.f;

            for(const PointInfo &pt_tmp : voxel_pts)
            {
                p.getVector3fMap() = p.getVector3fMap() +  pt_tmp.pt.getVector3fMap();
                p.r += pt_tmp.pt.r;
                p.g += pt_tmp.pt.g;
                p.b += pt_tmp.pt.b;

                Eigen::Vector3f normal = pt_tmp.normal.getNormalVector3fMap();
                normal.normalize();
                n.getNormalVector3fMap() = n.getNormalVector3fMap() + normal;
                n.curvature += pt_tmp.normal.curvature;
            }

            p.getVector3fMap() = p.getVector3fMap() / indexVector.size();
            p.r /= indexVector.size();
            p.g /= indexVector.size();
            p.b /= indexVector.size();

            n.getNormalVector3fMap() = n.getNormalVector3fMap() / indexVector.size();
            n.curvature /= indexVector.size();

            total_used += indexVector.size();
        }
        else // take only point with max probability
        {
            std::sort(voxel_pts.begin(), voxel_pts.end());

            bool found_good_pt = false;

            for(const PointInfo &pt_tmp : voxel_pts)
            {
                if (pt_tmp.distance_to_depth_discontinuity > param_.edge_radius_px_)
                {
                    p.getVector3fMap() = pt_tmp.pt.getVector3fMap();
                    p.r = pt_tmp.pt.r;
                    p.g = pt_tmp.pt.g;
                    p.b = pt_tmp.pt.b;

                    n.getNormalVector3fMap() = pt_tmp.normal.getNormalVector3fMap();
                    n.curvature = pt_tmp.normal.curvature;
                    found_good_pt = true;
                    break;
                }
            }

            if( !found_good_pt )
                continue;

            total_used++;
        }

        output->points[kept] = p;
        output_normals_->points[kept] = n;
        kept++;
    }

    std::cout << "Number of points in final model:" << kept << " used:" << total_used << std::endl;

    output->points.resize(kept);
    output_normals_->points.resize(kept);
    output->width = output_normals_->width = kept;
    output->height = output_normals_->height = 1;
    output->is_dense = output_normals_->is_dense = true;

    cleanUp();
}

template class V4R_EXPORTS NMBasedCloudIntegration<pcl::PointXYZRGB>;
}
<commit_msg>Speed-up info collection in NMBasedCloudIntegration<commit_after>/******************************************************************************
 * Copyright (c) 2013 Aitor Aldoma, Thomas Faeulhammer
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 ******************************************************************************/

#include <pcl/common/angles.h>
#include <pcl/common/io.h>
#include <pcl/common/transforms.h>
#include <pcl/filters/filter.h>
#include <pcl/visualization/pcl_visualizer.h>

#include <v4r/registration/noise_model_based_cloud_integration.h>
#include <v4r/common/organized_edge_detection.h>

#include <glog/logging.h>

namespace v4r
{

template<typename PointT>
void
NMBasedCloudIntegration<PointT>::collectInfo ()
{
  size_t total_point_count = 0;
  for(size_t i = 0; i < input_clouds_.size(); i++)
    total_point_count += indices_.empty() ? input_clouds_[i]->size() : indices_[i].size();
  big_cloud_info_.resize(total_point_count);

  size_t point_count = 0;
  for(size_t i=0; i < input_clouds_.size(); i++)
  {
    pcl::PointCloud<PointT> cloud_aligned;
    pcl::transformPointCloud(*input_clouds_[i], cloud_aligned, transformations_to_global_[i]);
    pcl::PointCloud<pcl::Normal> normals_aligned;
    transformNormals(*input_normals_[i], normals_aligned, transformations_to_global_[i]);

    size_t kept_new_pts = 0;

    if (indices_.empty())
    {
      for(size_t jj=0; jj<cloud_aligned.points.size(); jj++)
      {
        if ( !pcl::isFinite(cloud_aligned.points[jj]))
          continue;

        PointInfo &pt = big_cloud_info_[point_count + kept_new_pts];
        pt.pt = cloud_aligned.points[jj];
        pt.normal = normals_aligned.points[jj];
        pt.sigma_lateral = pt_properties_[i][jj][0];
        pt.sigma_axial = pt_properties_[i][jj][1];
        pt.distance_to_depth_discontinuity = pt_properties_[i][jj][2];
        kept_new_pts++;
      }
    }
    else
    {
      for(const auto idx : indices_[i])
      {
        if(!pcl::isFinite(cloud_aligned.points[idx]))
          continue;

        PointInfo &pt = big_cloud_info_[point_count + kept_new_pts];
        pt.pt = cloud_aligned.points[idx];
        pt.normal = normals_aligned.points[ idx ];
        pt.sigma_lateral = pt_properties_[i][idx][0];
        pt.sigma_axial = pt_properties_[i][idx][1];
        pt.distance_to_depth_discontinuity = pt_properties_[i][idx][2];
        kept_new_pts++;
      }
    }

    // compute and store remaining information
    for(size_t jj=0; jj<kept_new_pts; jj++)
    {
      PointInfo &pt = big_cloud_info_ [point_count + jj];
      pt.origin = i;

      Eigen::Matrix3f sigma = Eigen::Matrix3f::Zero(), sigma_aligned = Eigen::Matrix3f::Zero();
      sigma(0,0) = pt.sigma_lateral;
      sigma(1,1) = pt.sigma_lateral;
      sigma(2,2) = pt.sigma_axial;

      const Eigen::Matrix4f &tf = transformations_to_global_[ i ];
      Eigen::Matrix3f rotation = tf.block<3,3>(0,0); // or inverse?
      sigma_aligned = rotation * sigma * rotation.transpose();

      pt.probability = 1/ sqrt(2 * M_PI * sigma_aligned.determinant());
    }

    point_count += kept_new_pts;
  }

  big_cloud_info_.resize(point_count);
}

template<typename PointT>
void
NMBasedCloudIntegration<PointT>::reasonAboutPts ()
{
    const int width = input_clouds_[0]->width;
    const int height = input_clouds_[0]->height;
    const float cx = static_cast<float> (width) / 2.f;// - 0.5f;
    const float cy = static_cast<float> (height) / 2.f;// - 0.5f;

//    pcl::visualization::PCLVisualizer vis;
    for (size_t i=0; i<big_cloud_info_.size(); i++)
    {
        PointInfo &pt = big_cloud_info_[i];
        const PointT &ptt = pt.pt;

        for (size_t cloud=0; cloud<input_clouds_.size(); cloud++)
        {
            if( pt.origin == cloud)  // we don't have to reason about the point with respect to the original cloud
                continue;

            // reproject point onto the cloud's image plane and check if its within FOV and if so, if it can be seen or is occluded
            const Eigen::Matrix4f &tf = transformations_to_global_[cloud].inverse() * transformations_to_global_[pt.origin];
            float x = static_cast<float> (tf (0, 0) * ptt.x + tf (0, 1) * ptt.y + tf (0, 2) * ptt.z + tf (0, 3));
            float y = static_cast<float> (tf (1, 0) * ptt.x + tf (1, 1) * ptt.y + tf (1, 2) * ptt.z + tf (1, 3));
            float z = static_cast<float> (tf (2, 0) * ptt.x + tf (2, 1) * ptt.y + tf (2, 2) * ptt.z + tf (2, 3));

            int u = static_cast<int> (param_.focal_length_ * x / z + cx);
            int v = static_cast<int> (param_.focal_length_ * y / z + cy);

            std::cout << "u: " <<u << ", v: " << v << std::endl;

            PointT ptt_aligned;
            ptt_aligned.x = x;
            ptt_aligned.y = y;
            ptt_aligned.z = z;

            if( u<0 || v <0 || u>=width || v >= height )
                pt.occluded_++;
            else
            {
                float thresh = param_.threshold_explained_;
                if( z > 1.f )
                    thresh+= param_.threshold_explained_ * (z-1.f) * (z-1.f);

               const float z_c = input_clouds_[cloud]->points[ v*width + u ].z;
               if ( std::abs(z_c - z) < thresh )
                   pt.explained_++;
               else if (z_c > z )
               {
                   pt.violated_ ++;

//                   vis.removeAllShapes();
//                   vis.removeAllPointClouds();
//                   vis.addPointCloud(input_clouds_[cloud]);
//                   vis.addSphere(ptt_aligned, 0.03f, 1.f, 0.f, 0.f);
//                   vis.spin();
               }
               else
                   pt.occluded_ ++;
            }

        }
    }
}

template<typename PointT>
void
NMBasedCloudIntegration<PointT>::compute (PointTPtr & output)
{
    if(input_clouds_.empty()) {
        std::cerr << "No input clouds set for cloud integration!" << std::endl;
        return;
    }

    big_cloud_info_.clear();

    collectInfo();

    if(param_.reason_about_points_)
        reasonAboutPts();

    pcl::octree::OctreePointCloudPointVector<PointT> octree( param_.octree_resolution_ );
    PointTPtr big_cloud ( new pcl::PointCloud<PointT>());
    big_cloud->width = big_cloud_info_.size();
    big_cloud->height = 1;
    big_cloud->points.resize( big_cloud_info_.size() );
    for(size_t i=0; i < big_cloud_info_.size(); i++)
        big_cloud->points[i] = big_cloud_info_[i].pt;
    octree.setInputCloud( big_cloud );
    octree.addPointsFromInputCloud();

    typename pcl::octree::OctreePointCloudPointVector<PointT>::LeafNodeIterator leaf_it;
    const typename pcl::octree::OctreePointCloudPointVector<PointT>::LeafNodeIterator it2_end = octree.leaf_end();

    output->points.resize( big_cloud_info_.size() );
    output_normals_.reset(new pcl::PointCloud<pcl::Normal>);
    output_normals_->points.resize( big_cloud_info_.size());

    size_t kept = 0;
    size_t total_used = 0;

    for (leaf_it = octree.leaf_begin(); leaf_it != it2_end; ++leaf_it)
    {
        pcl::octree::OctreeContainerPointIndices& container = leaf_it.getLeafContainer();

        // add points from leaf node to indexVector
        std::vector<int> indexVector;
        container.getPointIndices (indexVector);

        if(indexVector.empty() || indexVector.size() < param_.min_points_per_voxel_)
            continue;

        std::vector<PointInfo> voxel_pts ( indexVector.size() );

        for(size_t k=0; k < indexVector.size(); k++)
            voxel_pts[k] = big_cloud_info_ [indexVector[k]];

        PointT p;
        pcl::Normal n;

        if(param_.average_)
        {
            p.getVector3fMap() = Eigen::Vector3f::Zero();
            p.r = p.g = p.b = 0.f;
            n.getNormalVector3fMap() = Eigen::Vector3f::Zero();
            n.curvature = 0.f;

            for(const PointInfo &pt_tmp : voxel_pts)
            {
                p.getVector3fMap() = p.getVector3fMap() +  pt_tmp.pt.getVector3fMap();
                p.r += pt_tmp.pt.r;
                p.g += pt_tmp.pt.g;
                p.b += pt_tmp.pt.b;

                Eigen::Vector3f normal = pt_tmp.normal.getNormalVector3fMap();
                normal.normalize();
                n.getNormalVector3fMap() = n.getNormalVector3fMap() + normal;
                n.curvature += pt_tmp.normal.curvature;
            }

            p.getVector3fMap() = p.getVector3fMap() / indexVector.size();
            p.r /= indexVector.size();
            p.g /= indexVector.size();
            p.b /= indexVector.size();

            n.getNormalVector3fMap() = n.getNormalVector3fMap() / indexVector.size();
            n.curvature /= indexVector.size();

            total_used += indexVector.size();
        }
        else // take only point with max probability
        {
            std::sort(voxel_pts.begin(), voxel_pts.end());

            bool found_good_pt = false;

            for(const PointInfo &pt_tmp : voxel_pts)
            {
                if (pt_tmp.distance_to_depth_discontinuity > param_.edge_radius_px_)
                {
                    p.getVector3fMap() = pt_tmp.pt.getVector3fMap();
                    p.r = pt_tmp.pt.r;
                    p.g = pt_tmp.pt.g;
                    p.b = pt_tmp.pt.b;

                    n.getNormalVector3fMap() = pt_tmp.normal.getNormalVector3fMap();
                    n.curvature = pt_tmp.normal.curvature;
                    found_good_pt = true;
                    break;
                }
            }

            if( !found_good_pt )
                continue;

            total_used++;
        }

        output->points[kept] = p;
        output_normals_->points[kept] = n;
        kept++;
    }

    std::cout << "Number of points in final model:" << kept << " used:" << total_used << std::endl;

    output->points.resize(kept);
    output_normals_->points.resize(kept);
    output->width = output_normals_->width = kept;
    output->height = output_normals_->height = 1;
    output->is_dense = output_normals_->is_dense = true;

    cleanUp();
}

template class V4R_EXPORTS NMBasedCloudIntegration<pcl::PointXYZRGB>;
}
<|endoftext|>


<commit_before>/****************************************************************/
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*          All contents are licensed under LGPL V2.1           */
/*             See LICENSE for full restrictions                */
/****************************************************************/
#include "ComputeElasticityTensor.h"
#include "RotationTensor.h"

template<>
InputParameters validParams<ComputeElasticityTensor>()
{
  InputParameters params = validParams<ComputeElasticityTensorBase>();
  params.addClassDescription("Compute an elasticity tensor.");
  params.addRequiredParam<std::vector<Real> >("C_ijkl", "Stiffness tensor for material");
  params.addParam<MooseEnum>("fill_method", RankFourTensor::fillMethodEnum() = "symmetric9", "The fill method");
  params.addParam<Real>("euler_angle_1", 0.0, "Euler angle in direction 1");
  params.addParam<Real>("euler_angle_2", 0.0, "Euler angle in direction 2");
  params.addParam<Real>("euler_angle_3", 0.0, "Euler angle in direction 3");
  return params;
}

ComputeElasticityTensor::ComputeElasticityTensor(const std::string & name,
                                                 InputParameters parameters) :
    ComputeElasticityTensorBase(name, parameters),
    _Euler_angles(getParam<Real>("euler_angle_1"),
                  getParam<Real>("euler_angle_2"),
                  getParam<Real>("euler_angle_3")),
    _Cijkl(getParam<std::vector<Real> >("C_ijkl"), (RankFourTensor::FillMethod)(int)getParam<MooseEnum>("fill_method"))
{
}

void
ComputeElasticityTensor::computeQpElasticityTensor()
{
  // Define a rotation according to Euler angle parameters
  RotationTensor R(_Euler_angles); // R type: RealTensorValue

  //Assign elasticity tensor at a given quad point
  _elasticity_tensor[_qp] = _Cijkl;

  //Rotate tensor
  _elasticity_tensor[_qp].rotate(R);
}
<commit_msg>Avoid repeated rotation ops in ComputeElasticityTensorBase (#5171)<commit_after>/****************************************************************/
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*          All contents are licensed under LGPL V2.1           */
/*             See LICENSE for full restrictions                */
/****************************************************************/
#include "ComputeElasticityTensor.h"
#include "RotationTensor.h"

template<>
InputParameters validParams<ComputeElasticityTensor>()
{
  InputParameters params = validParams<ComputeElasticityTensorBase>();
  params.addClassDescription("Compute an elasticity tensor.");
  params.addRequiredParam<std::vector<Real> >("C_ijkl", "Stiffness tensor for material");
  params.addParam<MooseEnum>("fill_method", RankFourTensor::fillMethodEnum() = "symmetric9", "The fill method");
  params.addParam<Real>("euler_angle_1", 0.0, "Euler angle in direction 1");
  params.addParam<Real>("euler_angle_2", 0.0, "Euler angle in direction 2");
  params.addParam<Real>("euler_angle_3", 0.0, "Euler angle in direction 3");
  return params;
}

ComputeElasticityTensor::ComputeElasticityTensor(const std::string & name,
                                                 InputParameters parameters) :
    ComputeElasticityTensorBase(name, parameters),
    _Euler_angles(getParam<Real>("euler_angle_1"),
                  getParam<Real>("euler_angle_2"),
                  getParam<Real>("euler_angle_3")),
    _Cijkl(getParam<std::vector<Real> >("C_ijkl"), (RankFourTensor::FillMethod)(int)getParam<MooseEnum>("fill_method"))
{
  // Define a rotation according to Euler angle parameters
  RotationTensor R(_Euler_angles); // R type: RealTensorValue

  // rotate elasticity tensor
  _Cijkl.rotate(R);
}

void
ComputeElasticityTensor::computeQpElasticityTensor()
{
  //Assign elasticity tensor at a given quad point
  _elasticity_tensor[_qp] = _Cijkl;
}
<|endoftext|>