<commit_before>#include <Game/ECS/GameWorld.h>
#include <Game/ECS/Components/ActionQueueComponent.h>
#include <Game/ECS/Components/SceneComponent.h>
#include <AI/Navigation/NavAgentComponent.h>
#include <AI/Movement/SteerAction.h>
#include <Game/Objects/SmartObjectComponent.h>
#include <Game/Objects/SmartObject.h>
#include <DetourCommon.h>

namespace DEM::Game
{

static inline float SqDistanceToInteractionZone(const vector3& Pos, const CInteractionZone& Zone, UPTR& OutSegment, float& OutT)
{
	const auto VertexCount = Zone.Vertices.size();
	if (VertexCount == 0) return std::numeric_limits<float>().max();
	else if (VertexCount == 1) return vector3::SqDistance(Pos, Zone.Vertices[0]);
	else if (VertexCount == 2) return dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[0].v, Zone.Vertices[1].v, OutT);
	else
	{
		float MinSqDistance = std::numeric_limits<float>().max();
		if (Zone.ClosedPolygon)
		{
			// NB: only convex polys are supported for now!
			if (dtPointInPolygon(Pos.v, Zone.Vertices[0].v, VertexCount))
			{
				OutSegment = VertexCount;
				return 0.f;
			}

			// Process implicit closing edge
			MinSqDistance = dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[VertexCount - 1].v, Zone.Vertices[0].v, OutT);
			OutSegment = VertexCount - 1;
		}

		for (UPTR i = 0; i < VertexCount - 1; ++i)
		{
			float t;
			const float SqDistance = dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[i].v, Zone.Vertices[i + 1].v, t);
			if (SqDistance < MinSqDistance)
			{
				MinSqDistance = SqDistance;
				OutSegment = i;
				OutT = t;
			}
		}

		return MinSqDistance;
	}
}
//---------------------------------------------------------------------

static inline vector3 PointInInteractionZone(const vector3& Pos, const CInteractionZone& Zone, UPTR Segment, float t)
{
	const auto VertexCount = Zone.Vertices.size();
	if (VertexCount == 0) return vector3::Zero;
	else if (VertexCount == 1) return Zone.Vertices[0];
	else if (VertexCount == 2) return vector3::lerp(Zone.Vertices[0], Zone.Vertices[1], t);
	else if (Segment == VertexCount) return Pos;
	else return vector3::lerp(Zone.Vertices[Segment], Zone.Vertices[(Segment + 1) % VertexCount], t);
}
//---------------------------------------------------------------------

// NB: non-zero _AllowedZones implied
static vector3 FindLocalInteractionPoint(SwitchSmartObjectState& Action, const CSmartObject& SO, const vector3& ActorLocalPos, bool OnlyCurrZone)
{
	float MinSqDistance = std::numeric_limits<float>().max();
	UPTR SegmentIdx = 0;
	float t = 0.f;

	if (OnlyCurrZone)
	{
		MinSqDistance = SqDistanceToInteractionZone(ActorLocalPos, SO.GetInteractionZone(Action._ZoneIndex), SegmentIdx, t);
	}
	else
	{
		// Find closest suitable zone
		const auto ZoneCount = SO.GetInteractionZoneCount();
		for (U8 i = 0; i < ZoneCount; ++i)
		{
			if (!((1 << i) & Action._AllowedZones)) continue;

			const float SqDistance = SqDistanceToInteractionZone(ActorLocalPos, SO.GetInteractionZone(i), SegmentIdx, t);
			if (SqDistance < MinSqDistance)
			{
				MinSqDistance = SqDistance;
				Action._ZoneIndex = i;
			}
		}
	}

	// For static smart objects try each zone only once
	if (SO.IsStatic()) Action._AllowedZones &= ~(1 << Action._ZoneIndex);

	return PointInInteractionZone(ActorLocalPos, SO.GetInteractionZone(Action._ZoneIndex), SegmentIdx, t);
}
//---------------------------------------------------------------------

//???when dynamic fails? zones aren't marked as failed!
static bool UpdateMovementSubAction(CActionQueueComponent& Queue, HAction Action, const CSmartObject& SO,
	const AI::CNavAgentComponent* pNavAgent, const matrix44& ObjectWorldTfm, const vector3& ActorPos, bool OnlyCurrZone)
{
	auto pAction = Action.As<SwitchSmartObjectState>();
	if (!pAction->_AllowedZones)
	{
		Queue.SetStatus(Action, EActionStatus::Failed);
		return true;
	}

	matrix44 WorldToSmartObject;
	ObjectWorldTfm.invert_simple(WorldToSmartObject);
	const vector3 SOSpaceActorPos = WorldToSmartObject.transform_coord(ActorPos);

	vector3 ActionPos = FindLocalInteractionPoint(*pAction, SO, SOSpaceActorPos, OnlyCurrZone);

	const auto& Zone = SO.GetInteractionZone(pAction->_ZoneIndex);
	const float Radius = Zone.Radius; //???apply actor radius too?
	const float SqRadius = std::max(Radius * Radius, AI::Steer::SqLinearTolerance);

	// FIXME: find closest navigable point, use radius as arrival distance, apply to the last path segment
	//if (SqRadius < MinSqDistance)
	//	ActionPos = vector3::lerp(ActionPos, SOSpaceActorPos, Radius / n_sqrt(MinSqDistance));

	ActionPos = ObjectWorldTfm.transform_coord(ActionPos);

	if (vector3::SqDistance2D(ActionPos, ActorPos) <= SqRadius) return false;

	if (pNavAgent)
	{
		float NearestPos[3];
		const float Extents[3] = { Radius, pNavAgent->Height, Radius };
		dtPolyRef ObjPolyRef = 0;
		pNavAgent->pNavQuery->findNearestPoly(ActionPos.v, Extents, pNavAgent->Settings->GetQueryFilter(), &ObjPolyRef, NearestPos);
		const float SqDiff = dtVdist2DSqr(ActionPos.v, NearestPos);
		if (!ObjPolyRef || SqDiff > SqRadius)
		{
			// No navigable point found
			// FIXME: try the next zone right now!
			// TODO: could explore all the interaction zone for intersecion with valid navigation polys, but it is slow
			//???what if dynamic? discard this zone or fail immediately?
			return true;
		}
		else if (SqDiff > 0.f) ActionPos = NearestPos;

		// FIXME: use pNavAgent->Corridor.getFirstPoly() instead! Offmesh can break this now!
		const float AgentExtents[3] = { pNavAgent->Radius, pNavAgent->Height, pNavAgent->Radius };
		dtPolyRef AgentPolyRef = 0;
		pNavAgent->pNavQuery->findNearestPoly(ActorPos.v, AgentExtents, pNavAgent->Settings->GetQueryFilter(), &AgentPolyRef, NearestPos);
		//n_assert_dbg(pNavAgent->Corridor.getFirstPoly() == AgentPolyRef);

		if (ObjPolyRef != AgentPolyRef)
		{
			// To avoid possible parent path invalidation, could try to optimize with:
			//Agent.pNavQuery->findLocalNeighbourhood
			//Agent.pNavQuery->moveAlongSurface
			//Agent.pNavQuery->raycast
			// but it would require additional logic and complicate navigation.
			Queue.PushOrUpdateChild<AI::Navigate>(Action, ActionPos, 0.f);
			return true;
		}
	}

	Queue.PushOrUpdateChild<AI::Steer>(Action, ActionPos, ObjectWorldTfm.Translation(), 0.f);
	return true;
}
//---------------------------------------------------------------------

// NB: OutPos is not changed if function returns false
static bool IsNavPolyInInteractionZone(const CInteractionZone& Zone, dtPolyRef PolyRef, dtNavMeshQuery& Query, vector3& OutPos)
{
	////!!!calc distance from PolyRef to Zone skeleton!
	//const float SqDistance = 0.f;

	//const float Radius = Zone.Radius; //???apply actor radius too?
	//const float SqRadius = std::max(Radius * Radius, AI::Steer::SqLinearTolerance);
	//return SqDistance <= SqRadius;

	//!!!return closest pos!

	//NOT_IMPLEMENTED;
	return false;
}
//---------------------------------------------------------------------

// If new path intersects with another interaction zone, can optimize by navigating to it instead of the original target
static void OptimizeStaticPath(SwitchSmartObjectState& Action, AI::Navigate& NavAction, const CSmartObject& SO, const AI::CNavAgentComponent* pNavAgent)
{
	//!!!TODO: instead of _PathScanned could increment path version on replan and recheck,
	//to handle partial path and corridor optimizations!
	if (Action._PathScanned || !Action._AllowedZones || !pNavAgent || pNavAgent->State != AI::ENavigationState::Following) return;

	Action._PathScanned = true;

	const auto ZoneCount = SO.GetInteractionZoneCount();

	// Don't test the last poly, we already navigate to it
	const int PolysToTest = pNavAgent->Corridor.getPathCount() - 1;
	for (int PolyIdx = 0; PolyIdx < PolysToTest; ++PolyIdx)
	{
		const auto PolyRef = pNavAgent->Corridor.getPath()[PolyIdx];
		for (U8 ZoneIdx = 0; ZoneIdx < ZoneCount; ++ZoneIdx)
		{
			if (!((1 << ZoneIdx) & Action._AllowedZones)) continue;
			if (IsNavPolyInInteractionZone(SO.GetInteractionZone(ZoneIdx), PolyRef, *pNavAgent->pNavQuery, NavAction._Destination))
			{
				Action._ZoneIndex = ZoneIdx;
				Action._AllowedZones &= ~(1 << ZoneIdx);
				return;
			}
		}
	}
}
//---------------------------------------------------------------------

void InteractWithSmartObjects(CGameWorld& World)
{
	World.ForEachEntityWith<CActionQueueComponent, const CSceneComponent, const AI::CNavAgentComponent*>(
		[&World](auto EntityID, auto& Entity,
			CActionQueueComponent& Queue,
			const CSceneComponent& ActorSceneComponent,
			const AI::CNavAgentComponent* pNavAgent)
	{
		if (!ActorSceneComponent.RootNode) return;

		auto Action = Queue.FindCurrent<SwitchSmartObjectState>();
		if (!Action) return;

		const auto ActionStatus = Queue.GetStatus(Action);
		if (ActionStatus != EActionStatus::Active) return;

		const auto ChildAction = Queue.GetChild(Action);
		const auto ChildActionStatus = Queue.GetStatus(ChildAction);
		if (ChildActionStatus == EActionStatus::Cancelled)
		{
			Queue.SetStatus(Action, EActionStatus::Cancelled);
			return;
		}

		auto pAction = Action.As<SwitchSmartObjectState>();

		auto pSOComponent = World.FindComponent<CSmartObjectComponent>(pAction->_Object);
		if (!pSOComponent || !pSOComponent->Asset)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		const CSmartObject* pSOAsset = pSOComponent->Asset->GetObject<CSmartObject>();
		if (!pSOAsset)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		auto pSOSceneComponent = World.FindComponent<CSceneComponent>(pAction->_Object);
		if (!pSOSceneComponent || !pSOSceneComponent->RootNode)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		// Cache allowed zones once
		if (!pAction->_AllowedZones)
		{
			const auto ZoneCount = pSOAsset->GetInteractionZoneCount();
			for (U8 i = 0; i < ZoneCount; ++i)
			{
				const auto& Zone = pSOAsset->GetInteractionZone(i);
				for (const auto& Interaction : Zone.Interactions)
				{
					if (Interaction.ID == pAction->_Interaction)
					{
						pAction->_AllowedZones |= (1 << i);
						break;
					}
				}
			}

			if (!pAction->_AllowedZones)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}

		const auto& ActorPos = ActorSceneComponent.RootNode->GetWorldPosition();
		const auto& ObjectWorldTfm = pSOSceneComponent->RootNode->GetWorldMatrix();

		// Move to the interaction point

		if (auto pSteerAction = ChildAction.As<AI::Steer>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				if (pSOAsset->IsStatic()) return;
				else if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, true)) return;
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}
		else if (auto pNavAction = ChildAction.As<AI::Navigate>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				if (pSOAsset->IsStatic())
				{
					OptimizeStaticPath(*pAction, *pNavAction, *pSOAsset, pNavAgent);
					return;
				}
				else
				{
					// Update movement target from the current zone
					if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, true)) return;
				}
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				// Try another remaining zones with navigable points one by one, fail if none left
				if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, false)) return;
			}
		}
		else if (!pSOAsset->IsStatic() || !ChildAction)
		{
			// Move to the closest zone with navigable point inside, fail if none left
			if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, false)) return;
		}

		// Face interaction direction

		if (auto pTurnAction = ChildAction.As<AI::Turn>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				if (pSOAsset->IsStatic()) return;

				// get facing from SO asset for the current region
				// if facing is still required by SO, update it in Turn action and return
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}
		else
		{
			// get facing from SO asset for the current region
			// if facing is required by SO and not already looking at that dir, generate Turn action and return

			//!!!DBG TMP!
			vector3 TargetDir = ObjectWorldTfm.Translation() - ActorPos;
			TargetDir.norm();

			//???update only if there is no Turn already active?
			vector3 LookatDir = -ActorSceneComponent.RootNode->GetWorldMatrix().AxisZ();
			LookatDir.norm();
			const float Angle = vector3::Angle2DNorm(LookatDir, TargetDir);
			if (std::fabsf(Angle) >= DEM::AI::Turn::AngularTolerance)
			{
				Queue.PushOrUpdateChild<AI::Turn>(Action, TargetDir);
				return;
			}
		}

		// Interact with object

		pSOComponent->RequestedState = pAction->_State;
		pSOComponent->Force = pAction->_Force;

		// TODO: start actor animation and/or state switching, wait for its end before setting Succeeded status!
		Queue.SetStatus(Action, EActionStatus::Succeeded);
	});
}
//---------------------------------------------------------------------

}
<commit_msg>SO iact movement progress 3<commit_after>#include <Game/ECS/GameWorld.h>
#include <Game/ECS/Components/ActionQueueComponent.h>
#include <Game/ECS/Components/SceneComponent.h>
#include <AI/Navigation/NavAgentComponent.h>
#include <AI/Movement/SteerAction.h>
#include <Game/Objects/SmartObjectComponent.h>
#include <Game/Objects/SmartObject.h>
#include <DetourCommon.h>

namespace DEM::Game
{

static inline float SqDistanceToInteractionZone(const vector3& Pos, const CInteractionZone& Zone, UPTR& OutSegment, float& OutT)
{
	const auto VertexCount = Zone.Vertices.size();
	if (VertexCount == 0) return std::numeric_limits<float>().max();
	else if (VertexCount == 1) return vector3::SqDistance(Pos, Zone.Vertices[0]);
	else if (VertexCount == 2) return dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[0].v, Zone.Vertices[1].v, OutT);
	else
	{
		float MinSqDistance = std::numeric_limits<float>().max();
		if (Zone.ClosedPolygon)
		{
			// NB: only convex polys are supported for now!
			if (dtPointInPolygon(Pos.v, Zone.Vertices[0].v, VertexCount))
			{
				OutSegment = VertexCount;
				return 0.f;
			}

			// Process implicit closing edge
			MinSqDistance = dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[VertexCount - 1].v, Zone.Vertices[0].v, OutT);
			OutSegment = VertexCount - 1;
		}

		for (UPTR i = 0; i < VertexCount - 1; ++i)
		{
			float t;
			const float SqDistance = dtDistancePtSegSqr2D(Pos.v, Zone.Vertices[i].v, Zone.Vertices[i + 1].v, t);
			if (SqDistance < MinSqDistance)
			{
				MinSqDistance = SqDistance;
				OutSegment = i;
				OutT = t;
			}
		}

		return MinSqDistance;
	}
}
//---------------------------------------------------------------------

static inline vector3 PointInInteractionZone(const vector3& Pos, const CInteractionZone& Zone, UPTR Segment, float t)
{
	const auto VertexCount = Zone.Vertices.size();
	if (VertexCount == 0) return vector3::Zero;
	else if (VertexCount == 1) return Zone.Vertices[0];
	else if (VertexCount == 2) return vector3::lerp(Zone.Vertices[0], Zone.Vertices[1], t);
	else if (Segment == VertexCount) return Pos;
	else return vector3::lerp(Zone.Vertices[Segment], Zone.Vertices[(Segment + 1) % VertexCount], t);
}
//---------------------------------------------------------------------

static bool UpdateMovementSubAction(CActionQueueComponent& Queue, HAction Action, const CSmartObject& SO,
	const AI::CNavAgentComponent* pNavAgent, const matrix44& ObjectWorldTfm, const vector3& ActorPos, bool OnlyCurrZone)
{
	matrix44 WorldToSmartObject;
	ObjectWorldTfm.invert_simple(WorldToSmartObject);
	const vector3 SOSpaceActorPos = WorldToSmartObject.transform_coord(ActorPos);

	auto pAction = Action.As<SwitchSmartObjectState>();
	while (OnlyCurrZone || pAction->_AllowedZones)
	{
		float MinSqDistance = std::numeric_limits<float>().max();
		UPTR SegmentIdx = 0;
		float t = 0.f;

		if (OnlyCurrZone)
		{
			MinSqDistance = SqDistanceToInteractionZone(SOSpaceActorPos, SO.GetInteractionZone(pAction->_ZoneIndex), SegmentIdx, t);
		}
		else
		{
			// Find closest suitable zone
			// NB: could calculate once, sort and then loop over them, but most probably it will be slower than now,
			// because it is expected that almost always the first or at worst the second interaction zone will be selected.
			const auto ZoneCount = SO.GetInteractionZoneCount();
			for (U8 i = 0; i < ZoneCount; ++i)
			{
				if (!((1 << i) & pAction->_AllowedZones)) continue;

				UPTR CurrS;
				float CurrT;
				const float SqDistance = SqDistanceToInteractionZone(SOSpaceActorPos, SO.GetInteractionZone(i), CurrS, CurrT);
				if (SqDistance < MinSqDistance)
				{
					MinSqDistance = SqDistance;
					SegmentIdx = CurrS;
					t = CurrT;
					pAction->_ZoneIndex = i;
				}
			}
		}

		const auto& Zone = SO.GetInteractionZone(pAction->_ZoneIndex);

		// For static smart objects try each zone only once
		if (SO.IsStatic()) pAction->_AllowedZones &= ~(1 << pAction->_ZoneIndex);

		vector3 ActionPos = PointInInteractionZone(SOSpaceActorPos, Zone, SegmentIdx, t);

		const float Radius = Zone.Radius; //???apply actor radius too?
		const float SqRadius = std::max(Radius * Radius, AI::Steer::SqLinearTolerance);

		// FIXME: find closest navigable point, use radius as arrival distance, apply to the last path segment
		//if (SqRadius < MinSqDistance)
		//	ActionPos = vector3::lerp(ActionPos, SOSpaceActorPos, Radius / n_sqrt(MinSqDistance));

		ActionPos = ObjectWorldTfm.transform_coord(ActionPos);

		if (vector3::SqDistance2D(ActionPos, ActorPos) <= SqRadius) return false;

		if (pNavAgent)
		{
			float NearestPos[3];
			const float Extents[3] = { Radius, pNavAgent->Height, Radius };
			dtPolyRef ObjPolyRef = 0;
			pNavAgent->pNavQuery->findNearestPoly(ActionPos.v, Extents, pNavAgent->Settings->GetQueryFilter(), &ObjPolyRef, NearestPos);
			const float SqDiff = dtVdist2DSqr(ActionPos.v, NearestPos);
			if (!ObjPolyRef || SqDiff > SqRadius)
			{
				// No navigable point found inside a zone
				// NB: this check is simplified and may fail to navigate zones where navigable point exists, so, in order
				// to work, each zone must have at least one navigable point in a zone radius from each base point.
				// TODO: could explore all the interaction zone for intersecion with valid navigation polys, but it is slow
				// FIXME: dynamic SO fails immediately for now, to avoid infinite looping. How to process dynamic SO correctly?
				if (OnlyCurrZone || !SO.IsStatic()) break;
				continue;
			}
			else if (SqDiff > 0.f) ActionPos = NearestPos;

			// FIXME: use pNavAgent->Corridor.getFirstPoly() instead! Offmesh can break this now!
			const float AgentExtents[3] = { pNavAgent->Radius, pNavAgent->Height, pNavAgent->Radius };
			dtPolyRef AgentPolyRef = 0;
			pNavAgent->pNavQuery->findNearestPoly(ActorPos.v, AgentExtents, pNavAgent->Settings->GetQueryFilter(), &AgentPolyRef, NearestPos);
			//n_assert_dbg(pNavAgent->Corridor.getFirstPoly() == AgentPolyRef);

			if (ObjPolyRef != AgentPolyRef)
			{
				// To avoid possible parent path invalidation, could try to optimize with:
				//Agent.pNavQuery->findLocalNeighbourhood
				//Agent.pNavQuery->moveAlongSurface
				//Agent.pNavQuery->raycast
				// but it would require additional logic and complicate navigation.
				Queue.PushOrUpdateChild<AI::Navigate>(Action, ActionPos, 0.f);
				return true;
			}
		}

		Queue.PushOrUpdateChild<AI::Steer>(Action, ActionPos, ObjectWorldTfm.Translation(), 0.f);
		return true;
	}

	// No suitable zones left, fail SO interaction entirely
	Queue.SetStatus(Action, EActionStatus::Failed);
	return true;
}
//---------------------------------------------------------------------

// NB: OutPos is not changed if function returns false
static bool IsNavPolyInInteractionZone(const CInteractionZone& Zone, dtPolyRef PolyRef, dtNavMeshQuery& Query, vector3& OutPos)
{
	////!!!calc distance from PolyRef to Zone skeleton!
	//const float SqDistance = 0.f;

	//const float Radius = Zone.Radius; //???apply actor radius too?
	//const float SqRadius = std::max(Radius * Radius, AI::Steer::SqLinearTolerance);
	//return SqDistance <= SqRadius;

	//!!!return closest pos!

	//NOT_IMPLEMENTED;
	return false;
}
//---------------------------------------------------------------------

// If new path intersects with another interaction zone, can optimize by navigating to it instead of the original target
static void OptimizeStaticPath(SwitchSmartObjectState& Action, AI::Navigate& NavAction, const CSmartObject& SO, const AI::CNavAgentComponent* pNavAgent)
{
	//!!!TODO: instead of _PathScanned could increment path version on replan and recheck,
	//to handle partial path and corridor optimizations!
	if (Action._PathScanned || !Action._AllowedZones || !pNavAgent || pNavAgent->State != AI::ENavigationState::Following) return;

	Action._PathScanned = true;

	const auto ZoneCount = SO.GetInteractionZoneCount();

	// Don't test the last poly, we already navigate to it
	const int PolysToTest = pNavAgent->Corridor.getPathCount() - 1;
	for (int PolyIdx = 0; PolyIdx < PolysToTest; ++PolyIdx)
	{
		const auto PolyRef = pNavAgent->Corridor.getPath()[PolyIdx];
		for (U8 ZoneIdx = 0; ZoneIdx < ZoneCount; ++ZoneIdx)
		{
			if (!((1 << ZoneIdx) & Action._AllowedZones)) continue;
			if (IsNavPolyInInteractionZone(SO.GetInteractionZone(ZoneIdx), PolyRef, *pNavAgent->pNavQuery, NavAction._Destination))
			{
				Action._ZoneIndex = ZoneIdx;
				Action._AllowedZones &= ~(1 << ZoneIdx);
				return;
			}
		}
	}
}
//---------------------------------------------------------------------

void InteractWithSmartObjects(CGameWorld& World)
{
	World.ForEachEntityWith<CActionQueueComponent, const CSceneComponent, const AI::CNavAgentComponent*>(
		[&World](auto EntityID, auto& Entity,
			CActionQueueComponent& Queue,
			const CSceneComponent& ActorSceneComponent,
			const AI::CNavAgentComponent* pNavAgent)
	{
		if (!ActorSceneComponent.RootNode) return;

		auto Action = Queue.FindCurrent<SwitchSmartObjectState>();
		if (!Action) return;

		const auto ActionStatus = Queue.GetStatus(Action);
		if (ActionStatus != EActionStatus::Active) return;

		const auto ChildAction = Queue.GetChild(Action);
		const auto ChildActionStatus = Queue.GetStatus(ChildAction);
		if (ChildActionStatus == EActionStatus::Cancelled)
		{
			Queue.SetStatus(Action, EActionStatus::Cancelled);
			return;
		}

		auto pAction = Action.As<SwitchSmartObjectState>();

		auto pSOComponent = World.FindComponent<CSmartObjectComponent>(pAction->_Object);
		if (!pSOComponent || !pSOComponent->Asset)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		const CSmartObject* pSOAsset = pSOComponent->Asset->GetObject<CSmartObject>();
		if (!pSOAsset)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		auto pSOSceneComponent = World.FindComponent<CSceneComponent>(pAction->_Object);
		if (!pSOSceneComponent || !pSOSceneComponent->RootNode)
		{
			Queue.SetStatus(Action, EActionStatus::Failed);
			return;
		}

		// Cache allowed zones once
		if (!pAction->_AllowedZones)
		{
			const auto ZoneCount = pSOAsset->GetInteractionZoneCount();
			for (U8 i = 0; i < ZoneCount; ++i)
			{
				const auto& Zone = pSOAsset->GetInteractionZone(i);
				for (const auto& Interaction : Zone.Interactions)
				{
					if (Interaction.ID == pAction->_Interaction)
					{
						pAction->_AllowedZones |= (1 << i);
						break;
					}
				}
			}

			if (!pAction->_AllowedZones)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}

		const auto& ActorPos = ActorSceneComponent.RootNode->GetWorldPosition();
		const auto& ObjectWorldTfm = pSOSceneComponent->RootNode->GetWorldMatrix();

		// Move to the interaction point

		if (auto pSteerAction = ChildAction.As<AI::Steer>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				if (pSOAsset->IsStatic()) return;
				else if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, true)) return;
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}
		else if (auto pNavAction = ChildAction.As<AI::Navigate>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				if (pSOAsset->IsStatic())
				{
					OptimizeStaticPath(*pAction, *pNavAction, *pSOAsset, pNavAgent);
					return;
				}
				else
				{
					// Update movement target from the current zone
					if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, true)) return;
				}
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				// Try another remaining zones with navigable points one by one, fail if none left
				if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, false)) return;
			}
		}
		else if (!pSOAsset->IsStatic() || !ChildAction)
		{
			// Move to the closest zone with navigable point inside, fail if none left
			if (UpdateMovementSubAction(Queue, Action, *pSOAsset, pNavAgent, ObjectWorldTfm, ActorPos, false)) return;
		}

		// Face interaction direction

		if (auto pTurnAction = ChildAction.As<AI::Turn>())
		{
			if (ChildActionStatus == EActionStatus::Active)
			{
				if (pSOAsset->IsStatic()) return;

				// get facing from SO asset for the current region
				// if facing is still required by SO, update it in Turn action and return
			}
			else if (ChildActionStatus == EActionStatus::Failed)
			{
				Queue.SetStatus(Action, EActionStatus::Failed);
				return;
			}
		}
		else
		{
			// get facing from SO asset for the current region
			// if facing is required by SO and not already looking at that dir, generate Turn action and return

			//!!!DBG TMP!
			vector3 TargetDir = ObjectWorldTfm.Translation() - ActorPos;
			TargetDir.norm();

			//???update only if there is no Turn already active?
			vector3 LookatDir = -ActorSceneComponent.RootNode->GetWorldMatrix().AxisZ();
			LookatDir.norm();
			const float Angle = vector3::Angle2DNorm(LookatDir, TargetDir);
			if (std::fabsf(Angle) >= DEM::AI::Turn::AngularTolerance)
			{
				Queue.PushOrUpdateChild<AI::Turn>(Action, TargetDir);
				return;
			}
		}

		// Interact with object

		pSOComponent->RequestedState = pAction->_State;
		pSOComponent->Force = pAction->_Force;

		// TODO: start actor animation and/or state switching, wait for its end before setting Succeeded status!
		Queue.SetStatus(Action, EActionStatus::Succeeded);
	});
}
//---------------------------------------------------------------------

}
<|endoftext|>


<commit_before>/*
 * Copyright 2010 MQWeb - Franky Braem
 *
 * Licensed under the EUPL, Version 1.1 or – as soon they
 * will be approved by the European Commission - subsequent
 * versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the
 * Licence.
 * You may obtain a copy of the Licence at:
 *
 * http://joinup.ec.europa.eu/software/page/eupl
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the Licence is
 * distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the Licence for the specific language governing
 * permissions and limitations under the Licence.
 */

#include "MQ/Web/StaticRequestHandler.h"

#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/DateTimeParser.h"

#include "Poco/Util/Application.h"

#include "Poco/URI.h"
#include "Poco/Path.h"
#include "Poco/File.h"

namespace MQ
{
namespace Web
{

StaticRequestHandler::StaticRequestHandler()
{
}

void StaticRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)
{
	// Check for the favicon.ico request, we don't have one for now,
	// so set status code to HTTP_NOT_FOUND
	if ( request.getURI().compare("/favicon.ico") == 0 )
	{
		response.setStatus(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
		response.send();
		return;
	}

  std::string lastModifiedHeader = request.get("If-Modified-Since", "");

  Poco::URI uri(request.getURI());

  Poco::Util::Application& app = Poco::Util::Application::instance();
  std::string staticPathname = app.config().getString("mq.web.static", "");
  if ( staticPathname.empty() )
  {
    staticPathname = app.config().getString("application.dir");
  }

  if ( ! staticPathname.empty() )
  {
    Poco::Path staticPath(staticPathname);
    staticPath.makeDirectory();

	std::vector<std::string> uriPathSegments;
	uri.getPathSegments(uriPathSegments);
	std::vector<std::string>::iterator it = uriPathSegments.begin();
	it++;
	for(; it != uriPathSegments.end(); ++it)
	{
		staticPath.append(*it);
	}

    Poco::File staticFile(staticPath);

    Poco::Logger& logger = Poco::Logger::get("mq.web.access");
    if ( staticFile.exists() )
    {
      if ( !lastModifiedHeader.empty() )
      {
        Poco::DateTime lastModifiedDate;
        int timeZoneDifferential = 0;
        if ( Poco::DateTimeParser::tryParse(Poco::DateTimeFormat::HTTP_FORMAT, lastModifiedHeader, lastModifiedDate, timeZoneDifferential) )
        {
          if ( staticFile.getLastModified() <= lastModifiedDate.timestamp() )
          {
            logger.information(Poco::Logger::format("$0 : HTTP_NOT_MODIFIED", staticPath.toString()));
            response.setStatus(Poco::Net::HTTPResponse::HTTP_NOT_MODIFIED);
            response.send();
            return;
          }
        }
      }

      logger.information(Poco::Logger::format("$0 : HTTP_OK", staticPath.toString()));

      std::string mimeType;
      if ( staticPath.getExtension().compare("gif") == 0 )
      {
        mimeType = "image/gif";
      }
      else if ( staticPath.getExtension().compare("css") == 0 )
      {
        mimeType = "text/css";
      }
      else if (   staticPath.getExtension().compare("html") == 0
               || staticPath.getExtension().compare("htm") == 0)
      {
        mimeType = "text/html";
      }
      else if ( staticPath.getExtension().compare("js") == 0 )
      {
        mimeType = "text/plain";
      }
      else if ( staticPath.getExtension().compare("png") == 0 )
      {
        mimeType = "image/png";
      }
      else if (    staticPath.getExtension().compare("jpg") == 0
                || staticPath.getExtension().compare("jpeg") == 0)
      {
        mimeType = "image/jpeg";
      }

      response.sendFile(staticPath.toString(), mimeType);
      response.setStatus(Poco::Net::HTTPResponse::HTTP_OK);
      return;
    }
    else
    {
      logger.error(Poco::Logger::format("$0 : HTTP_NOT_FOUND", staticFile.path()));
    }
  }

  response.setStatus(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
  response.send();
}

} } // Namespace MQ::Web
<commit_msg>Return mime type text/javascript for JavaScript files<commit_after>/*
 * Copyright 2010 MQWeb - Franky Braem
 *
 * Licensed under the EUPL, Version 1.1 or – as soon they
 * will be approved by the European Commission - subsequent
 * versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the
 * Licence.
 * You may obtain a copy of the Licence at:
 *
 * http://joinup.ec.europa.eu/software/page/eupl
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the Licence is
 * distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the Licence for the specific language governing
 * permissions and limitations under the Licence.
 */

#include "MQ/Web/StaticRequestHandler.h"

#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/DateTimeParser.h"

#include "Poco/Util/Application.h"

#include "Poco/URI.h"
#include "Poco/Path.h"
#include "Poco/File.h"

namespace MQ
{
namespace Web
{

StaticRequestHandler::StaticRequestHandler()
{
}

void StaticRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)
{
	// Check for the favicon.ico request, we don't have one for now,
	// so set status code to HTTP_NOT_FOUND
	if ( request.getURI().compare("/favicon.ico") == 0 )
	{
		response.setStatus(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
		response.send();
		return;
	}

  std::string lastModifiedHeader = request.get("If-Modified-Since", "");

  Poco::URI uri(request.getURI());

  Poco::Util::Application& app = Poco::Util::Application::instance();
  std::string staticPathname = app.config().getString("mq.web.static", "");
  if ( staticPathname.empty() )
  {
    staticPathname = app.config().getString("application.dir");
  }

  if ( ! staticPathname.empty() )
  {
    Poco::Path staticPath(staticPathname);
    staticPath.makeDirectory();

	std::vector<std::string> uriPathSegments;
	uri.getPathSegments(uriPathSegments);
	std::vector<std::string>::iterator it = uriPathSegments.begin();
	it++;
	for(; it != uriPathSegments.end(); ++it)
	{
		staticPath.append(*it);
	}

    Poco::File staticFile(staticPath);

    Poco::Logger& logger = Poco::Logger::get("mq.web.access");
    if ( staticFile.exists() )
    {
      if ( !lastModifiedHeader.empty() )
      {
        Poco::DateTime lastModifiedDate;
        int timeZoneDifferential = 0;
        if ( Poco::DateTimeParser::tryParse(Poco::DateTimeFormat::HTTP_FORMAT, lastModifiedHeader, lastModifiedDate, timeZoneDifferential) )
        {
          if ( staticFile.getLastModified() <= lastModifiedDate.timestamp() )
          {
            logger.information(Poco::Logger::format("$0 : HTTP_NOT_MODIFIED", staticPath.toString()));
            response.setStatus(Poco::Net::HTTPResponse::HTTP_NOT_MODIFIED);
            response.send();
            return;
          }
        }
      }

      logger.information(Poco::Logger::format("$0 : HTTP_OK", staticPath.toString()));

      std::string mimeType;
      if ( staticPath.getExtension().compare("gif") == 0 )
      {
        mimeType = "image/gif";
      }
      else if ( staticPath.getExtension().compare("css") == 0 )
      {
        mimeType = "text/css";
      }
      else if (   staticPath.getExtension().compare("html") == 0
               || staticPath.getExtension().compare("htm") == 0)
      {
        mimeType = "text/html";
      }
      else if ( staticPath.getExtension().compare("js") == 0 )
      {
        mimeType = "text/javascript";
      }
      else if ( staticPath.getExtension().compare("png") == 0 )
      {
        mimeType = "image/png";
      }
      else if (    staticPath.getExtension().compare("jpg") == 0
                || staticPath.getExtension().compare("jpeg") == 0)
      {
        mimeType = "image/jpeg";
      }

      response.sendFile(staticPath.toString(), mimeType);
      response.setStatus(Poco::Net::HTTPResponse::HTTP_OK);
      return;
    }
    else
    {
      logger.error(Poco::Logger::format("$0 : HTTP_NOT_FOUND", staticFile.path()));
    }
  }

  response.setStatus(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
  response.send();
}

} } // Namespace MQ::Web
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    MathematicalMorphologyBinaryFilters.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

//  Software Guide : BeginLatex
//
//  The following section illustrates the use of filters for binary images. The
//  filter names clearly specify the type of image on which it operates.  The
//  header files required for a minimal example on the use of mathematical
//  morphology filters are presented below.
//
//  \index{itk::BinaryDilateImageFilter!header|textbf}
//  \index{itk::BinaryErodeImageFilter!header|textbf}
//
//  Software Guide : EndLatex

#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"

// Software Guide : BeginCodeSnippet
#include "itkBinaryErodeImageFilter.h"
#include "itkBinaryDilateImageFilter.h"
#include "itkBinaryBallStructuringElement.h"
// Software Guide : EndCodeSnippet

#include "itkBinaryThresholdImageFilter.h"

int main( int argc, char ** argv )
{

  if( argc < 4 )
    {
    std::cerr << "Usage: " << std::endl;
    std::cerr << argv[0] << "  inputImageFile  ";
    std::cerr << " outputImageFileErosion  outputImageFileDilation" << std::endl;
    return 1;
    }

  //  Software Guide : BeginLatex
  //
  //  The following code defines the input and output pixel types and their
  //  associated image types.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  const unsigned int Dimension = 2;

  typedef unsigned char   InputPixelType;
  typedef unsigned char   OutputPixelType;

  typedef itk::Image< InputPixelType,  Dimension >   InputImageType;
  typedef itk::Image< OutputPixelType, Dimension >   OutputImageType;
  // Software Guide : EndCodeSnippet

  typedef itk::ImageFileReader< InputImageType  >  ReaderType;
  typedef itk::ImageFileWriter< OutputImageType >  WriterType;

  typedef itk::BinaryThresholdImageFilter< InputImageType, InputImageType >  ThresholdFilterType;

  //  Software Guide : BeginLatex
  //
  //  Mathematical morphology operations are based on the application of an
  //  operator over a neighborhood of each input pixel. The combination of the
  //  rule and the neighborhood is known as \emph{Structuring Element}. Altough
  //  some rules have become the \emph{de facto} standard on image processing
  //  there is a good deal of freedom as to what kind of algorithmic rule
  //  should be applied on the neighborhood. The implementation on Insight
  //  follows the typical rule of minimum for erosion and maximum for dilation.
  //
  //  The structuring element is implemented as a NeighborhoodOperator. In
  //  particular, the default structuring element is the
  //  \code{BinaryBallStructuringElement} class. This class is instantiated
  //  using the pixel type and dimension of the input image.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef itk::BinaryBallStructuringElement<
                      InputPixelType,
                      Dimension  >             StructuringElementType;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The structuring element type is then used along the input and output
  //  image types for instantiating the type of the filters.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef itk::BinaryErodeImageFilter<
                            InputImageType,
                            OutputImageType,
                            StructuringElementType >  ErodeFilterType;

  typedef itk::BinaryDilateImageFilter<
                            InputImageType,
                            OutputImageType,
                            StructuringElementType >  DilateFilterType;
  // Software Guide : EndCodeSnippet

  // Creation of Reader and Writer filters
  ReaderType::Pointer reader = ReaderType::New();
  WriterType::Pointer writerDilation = WriterType::New();
  WriterType::Pointer writerErosion  = WriterType::New();

  ThresholdFilterType::Pointer thresholder = ThresholdFilterType::New();

  //  Software Guide : BeginLatex
  //
  //  The filters can now be created by invoking the \code{New()} method and
  //  assigning the result to SmartPointers.
  //
  //  \index{itk::BinaryDilateImageFilter!New()}
  //  \index{itk::BinaryErodeImageFilter!New()}
  //  \index{itk::BinaryDilateImageFilter!Pointer}
  //  \index{itk::BinaryErodeImageFilter!Pointer}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  ErodeFilterType::Pointer  binaryErode  = ErodeFilterType::New();
  DilateFilterType::Pointer binaryDilate = DilateFilterType::New();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The structuring element is not a reference counted class. It is then
  //  created as a static C++ object instead of using \code{New()} and
  //  SmartPointers. The radius of the neighborhood associated with the
  //  structuring element is defined with the \code{SetRadius()} method and the
  //  resulting structuring element is passed to the mathematical morphology
  //  filter through the \code{SetKernel()} method, as illustrated below.
  //
  //  \index{itk::BinaryBallStructuringElement!SetRadius()}
  //  \index{itk::BinaryDilateImageFilter!SetKernel()}
  //  \index{itk::BinaryErodeImageFilter!SetKernel()}
  //  \index{SetRadius()!itk::BinaryBallStructuringElement}
  //  \index{SetKernel()!itk::BinaryDilateImageFilter}
  //  \index{SetKernel()!itk::BinaryErodeImageFilter}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  StructuringElementType  structuringElement;

  structuringElement.SetRadius( 2 );  // 5x5 structuring element

  binaryErode->SetKernel(  structuringElement );
  binaryDilate->SetKernel( structuringElement );
  // Software Guide : EndCodeSnippet

  reader->SetFileName( argv[1] );

  writerErosion->SetFileName(  argv[2] );
  writerDilation->SetFileName( argv[3] );

  //  Software Guide : BeginLatex
  //
  //  A binary image is provided as input to the filters. This image can be,
  //  for example, the the output of a binary threshold image filter.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  thresholder->SetInput( reader->GetOutput() );

  InputPixelType background =  10;
  InputPixelType foreground = 240;

  thresholder->SetOutsideValue( background );
  thresholder->SetInsideValue(  foreground );

  thresholder->SetLowerThreshold( 128 );
  thresholder->SetUpperThreshold( 255 );
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginCodeSnippet
  binaryErode->SetInput( thresholder->GetOutput() );
  binaryDilate->SetInput( thresholder->GetOutput() );
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  It is necessary to define what could be considered objects on the binary
  //  images. This is specified with the methods \code{SetErodeValue()} and
  //  \code{SetDilateValue()}. The value passed to these methods will be
  //  considered the value over which the dilation and erosion rules will
  //  apply.
  //
  //  \index{itk::BinaryDilateImageFilter!SetDilateValue()}
  //  \index{itk::BinaryErodeImageFilter!SetErodeValue()}
  //  \index{SetDilateValue()!itk::BinaryDilateImageFilter}
  //  \index{SetErodeValue()!itk::BinaryErodeImageFilter}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  binaryErode->SetErodeValue( foreground );
  binaryDilate->SetDilateValue( foreground );
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The execution of the filter can be triggered by the invokation of the
  //  \code{Update()} method on the filter or on other downstream filter, for
  //  example a writer.
  //
  //  \index{itk::BinaryDilateImageFilter!Update()}
  //  \index{itk::BinaryErodeImageFilter!Update()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  writerDilation->SetInput( binaryDilate->GetOutput() );
  writerDilation->Update();
  // Software Guide : EndCodeSnippet

  writerErosion->SetInput( binaryErode->GetOutput() );
  writerErosion->Update();

  return 0;

}

<commit_msg>FIX: CreateStructuringElement() was missing.      Definition of foreground, background was added. DOC: Images added in latex.<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    MathematicalMorphologyBinaryFilters.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

//  Software Guide : BeginLatex
//
//  The following section illustrates the use of filters for binary images. The
//  filter names clearly specify the type of image on which it operates.  The
//  header files required for a minimal example on the use of mathematical
//  morphology filters are presented below.
//
//  \index{itk::BinaryDilateImageFilter!header|textbf}
//  \index{itk::BinaryErodeImageFilter!header|textbf}
//
//  Software Guide : EndLatex

#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"

// Software Guide : BeginCodeSnippet
#include "itkBinaryErodeImageFilter.h"
#include "itkBinaryDilateImageFilter.h"
#include "itkBinaryBallStructuringElement.h"
// Software Guide : EndCodeSnippet

#include "itkBinaryThresholdImageFilter.h"

int main( int argc, char ** argv )
{

  if( argc < 4 )
    {
    std::cerr << "Usage: " << std::endl;
    std::cerr << argv[0] << "  inputImageFile  ";
    std::cerr << " outputImageFileErosion  outputImageFileDilation" << std::endl;
    return 1;
    }

  //  Software Guide : BeginLatex
  //
  //  The following code defines the input and output pixel types and their
  //  associated image types.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  const unsigned int Dimension = 2;

  typedef unsigned char   InputPixelType;
  typedef unsigned char   OutputPixelType;

  typedef itk::Image< InputPixelType,  Dimension >   InputImageType;
  typedef itk::Image< OutputPixelType, Dimension >   OutputImageType;
  // Software Guide : EndCodeSnippet

  typedef itk::ImageFileReader< InputImageType  >  ReaderType;
  typedef itk::ImageFileWriter< OutputImageType >  WriterType;

  typedef itk::BinaryThresholdImageFilter< InputImageType, InputImageType >  ThresholdFilterType;

  //  Software Guide : BeginLatex
  //
  //  Mathematical morphology operations are based on the application of an
  //  operator over a neighborhood of each input pixel. The combination of the
  //  rule and the neighborhood is known as \emph{Structuring Element}. Altough
  //  some rules have become the \emph{de facto} standard on image processing
  //  there is a good deal of freedom as to what kind of algorithmic rule
  //  should be applied on the neighborhood. The implementation on Insight
  //  follows the typical rule of minimum for erosion and maximum for dilation.
  //
  //  The structuring element is implemented as a NeighborhoodOperator. In
  //  particular, the default structuring element is the
  //  \code{BinaryBallStructuringElement} class. This class is instantiated
  //  using the pixel type and dimension of the input image.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef itk::BinaryBallStructuringElement<
                      InputPixelType,
                      Dimension  >             StructuringElementType;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The structuring element type is then used along the input and output
  //  image types for instantiating the type of the filters.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef itk::BinaryErodeImageFilter<
                            InputImageType,
                            OutputImageType,
                            StructuringElementType >  ErodeFilterType;

  typedef itk::BinaryDilateImageFilter<
                            InputImageType,
                            OutputImageType,
                            StructuringElementType >  DilateFilterType;
  // Software Guide : EndCodeSnippet

  // Creation of Reader and Writer filters
  ReaderType::Pointer reader = ReaderType::New();
  WriterType::Pointer writerDilation = WriterType::New();
  WriterType::Pointer writerErosion  = WriterType::New();

  ThresholdFilterType::Pointer thresholder = ThresholdFilterType::New();

  //  Software Guide : BeginLatex
  //
  //  The filters can now be created by invoking the \code{New()} method and
  //  assigning the result to SmartPointers.
  //
  //  \index{itk::BinaryDilateImageFilter!New()}
  //  \index{itk::BinaryErodeImageFilter!New()}
  //  \index{itk::BinaryDilateImageFilter!Pointer}
  //  \index{itk::BinaryErodeImageFilter!Pointer}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  ErodeFilterType::Pointer  binaryErode  = ErodeFilterType::New();
  DilateFilterType::Pointer binaryDilate = DilateFilterType::New();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The structuring element is not a reference counted class. It is then
  //  created as a static C++ object instead of using \code{New()} and
  //  SmartPointers. The radius of the neighborhood associated with the
  //  structuring element is defined with the \code{SetRadius()} method and the
  //  \code{CreateStructuringElement()} method is invoked in order to
  //  initialize the operator.  The resulting structuring element is passed to
  //  the mathematical morphology filter through the \code{SetKernel()} method,
  //  as illustrated below.
  //
  //  \index{itk::BinaryBallStructuringElement!SetRadius()}
  //  \index{itk::BinaryBallStructuringElement!CreateStructuringElement()}
  //  \index{itk::BinaryDilateImageFilter!SetKernel()}
  //  \index{itk::BinaryErodeImageFilter!SetKernel()}
  //  \index{SetRadius()!itk::BinaryBallStructuringElement}
  //  \index{SetKernel()!itk::BinaryDilateImageFilter}
  //  \index{SetKernel()!itk::BinaryErodeImageFilter}
  //  \index{SetRadius()!itk::BinaryBallStructuringElement}
  //  \index{CreateStructuringElement()!itk::BinaryBallStructuringElement}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  StructuringElementType  structuringElement;

  structuringElement.SetRadius( 1 );  // 3x3 structuring element

  structuringElement.CreateStructuringElement();

  binaryErode->SetKernel(  structuringElement );
  binaryDilate->SetKernel( structuringElement );
  // Software Guide : EndCodeSnippet

  reader->SetFileName( argv[1] );

  writerErosion->SetFileName(  argv[2] );
  writerDilation->SetFileName( argv[3] );

  //  Software Guide : BeginLatex
  //
  //  A binary image is provided as input to the filters. This image can be,
  //  for example, the the output of a binary threshold image filter.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  thresholder->SetInput( reader->GetOutput() );

  InputPixelType background =  10;
  InputPixelType foreground = 240;

  thresholder->SetOutsideValue( background );
  thresholder->SetInsideValue(  foreground );

  thresholder->SetLowerThreshold( 128 );
  thresholder->SetUpperThreshold( 255 );
  // Software Guide : EndCodeSnippet

  // Software Guide : BeginCodeSnippet
  binaryErode->SetInput( thresholder->GetOutput() );
  binaryDilate->SetInput( thresholder->GetOutput() );
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  It is necessary to define what could be considered objects on the binary
  //  images. This is specified with the methods \code{SetErodeValue()} and
  //  \code{SetDilateValue()}. The value passed to these methods will be
  //  considered the value over which the dilation and erosion rules will
  //  apply.
  //
  //  \index{itk::BinaryDilateImageFilter!SetDilateValue()}
  //  \index{itk::BinaryErodeImageFilter!SetErodeValue()}
  //  \index{SetDilateValue()!itk::BinaryDilateImageFilter}
  //  \index{SetErodeValue()!itk::BinaryErodeImageFilter}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  binaryErode->SetErodeValue( foreground );
  binaryDilate->SetDilateValue( foreground );
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The execution of the filter can be triggered by the invokation of the
  //  \code{Update()} method on the filter or on other downstream filter, for
  //  example a writer.
  //
  //  \index{itk::BinaryDilateImageFilter!Update()}
  //  \index{itk::BinaryErodeImageFilter!Update()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  writerDilation->SetInput( binaryDilate->GetOutput() );
  writerDilation->Update();
  // Software Guide : EndCodeSnippet

  writerErosion->SetInput( binaryErode->GetOutput() );
  writerErosion->Update();

  //  Software Guide : BeginLatex
  //
  // \begin{figure}
  // \center
  // \includegraphics[width=4cm]{BinaryThresholdImageFilterOutput.eps}
  // \includegraphics[width=4cm]{MathematicalMorphologyBinaryErosionOutput.eps}
  // \includegraphics[width=4cm]{MathematicalMorphologyBinaryDilationOutput.eps}
  // \caption{Effect of Erosion and Dilation in a binary image.}
  // \label{fig:MathematicalMorphologyBinaryFilters}
  // \end{figure}
  //
  //  Figure \ref{fig:MathematicalMorphologyBinaryFilters} illustrates the
  //  effect of the erosion and dilation filters on a bianry image from a MRI
  //  brain slice. The figure shows how these operations can be used to remove
  //  spurious details from segmented images.
  //
  //  Software Guide : EndLatex

  return 0;

}

<|endoftext|>


<commit_before><commit_msg>Delete directory returns whether is has succeded or not. Added a way to recursively delete files when deleting a directory.<commit_after><|endoftext|>


<commit_before><commit_msg>[cx/array] : improve compiler support<commit_after><|endoftext|>


<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkBloxBoundaryPointImageTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include <stdio.h>

// Native ITK stuff
#include "itkSize.h"
#include "itkIndex.h"
#include "itkImage.h"
#include "itkImageRegionIterator.h"
#include "itkPoint.h"

// Blox stuff
#include "itkBloxBoundaryPointPixel.h"
#include "itkBloxBoundaryPointImage.h"
#include "itkGradientImageToBloxBoundaryPointImageFilter.h"

// Spatial function stuff
#include "itkSphereSpatialFunction.h"
#include "itkFloodFilledSpatialFunctionConditionalIterator.h"

// DOG gradient related stuff
#include "itkBinomialBlurImageFilter.h"
#include "itkDifferenceOfGaussiansGradientImageFilter.h"

// Main for testing BloxImage/BloxPixel storage
int itkBloxBoundaryPointImageTest(int, char* [] )
{
  const unsigned int dim = 3;

  //-----------------Create a new input image--------------------
  // Image size and spacing parameters
  unsigned long sourceImageSize[]  = { 20,20,20 };
  double sourceImageSpacing[] = { 1.0,1.0,1.0 };
  double sourceImageOrigin[] = { 0,0,0 };

  // Image typedef
  typedef itk::Image< unsigned char, dim > ImageType;

  // Creates the sourceImage (but doesn't set the size or allocate memory)
  ImageType::Pointer sourceImage = ImageType::New();
  sourceImage->SetOrigin(sourceImageOrigin);
  sourceImage->SetSpacing(sourceImageSpacing);

  printf("New sourceImage created\n");

  //-----The following block allocates the sourceImage-----

  // Create a size object native to the sourceImage type
  ImageType::SizeType sourceImageSizeObject;
  // Set the size object to the array defined earlier
  sourceImageSizeObject.SetSize( sourceImageSize );
  // Create a region object native to the sourceImage type
  ImageType::RegionType largestPossibleRegion;
  // Resize the region
  largestPossibleRegion.SetSize( sourceImageSizeObject );
  // Set the largest legal region size (i.e. the size of the whole sourceImage) to what we just defined
  sourceImage->SetLargestPossibleRegion( largestPossibleRegion );
  // Set the buffered region
  sourceImage->SetBufferedRegion( largestPossibleRegion );
  // Set the requested region
  sourceImage->SetRequestedRegion( largestPossibleRegion );
  // Now allocate memory for the sourceImage
  sourceImage->Allocate();

  printf("New sourceImage allocated\n");

  // Initialize the image to hold all 0's
  itk::ImageRegionIterator<ImageType> it =
    itk::ImageRegionIterator<ImageType>(sourceImage, largestPossibleRegion);

  for(it.GoToBegin(); !it.IsAtEnd(); ++it)
    {
    it.Set(0);
    }

  //---------Create and initialize a spatial function-----------

  typedef itk::SphereSpatialFunction<dim> FunctionType;
  typedef FunctionType::InputType FunctionPositionType;

  // Create and initialize a new sphere function

  FunctionType::Pointer spatialFunc = FunctionType::New();
  spatialFunc->SetRadius( 5 );

  FunctionPositionType center;
  center[0]=10;
  center[1]=10;
  center[2]=10;
  spatialFunc->SetCenter(center);

  printf("Sphere spatial function created\n");

  //---------Create and initialize a spatial function iterator-----------
  ImageType::IndexType seedPos;
  const ImageType::IndexValueType pos[] = {10,10,10};
  seedPos.SetIndex(pos);

  typedef itk::FloodFilledSpatialFunctionConditionalIterator
    <ImageType, FunctionType> ItType;
  ItType sfi = ItType(sourceImage, spatialFunc, seedPos);

  // Iterate through the entire image and set interior pixels to 255
  for( ; !( sfi.IsAtEnd() ); ++sfi)
    {
    sfi.Set(255);
    }

  printf("Spatial function iterator created, sphere drawn\n");

  //--------------------Do blurring and edge detection----------------
  typedef ImageType OutputType;

  // Create a binomial blur filter
  itk::BinomialBlurImageFilter<ImageType, OutputType>::Pointer binfilter;
  binfilter = itk::BinomialBlurImageFilter<ImageType, OutputType>::New();

  sourceImage->SetRequestedRegion(sourceImage->GetLargestPossibleRegion() );

  // Set filter parameters
  binfilter->SetInput(sourceImage);
  binfilter->SetRepetitions(4);

  // Set up the output of the filter
  ImageType::Pointer blurredImage = binfilter->GetOutput();

  // Execute the filter
  binfilter->Update();
  std::cout << "Binomial blur filter updated\n";

  // Create a differennce of gaussians gradient filter
  typedef itk::DifferenceOfGaussiansGradientImageFilter<OutputType,
    double> DOGFilterType;
  DOGFilterType::Pointer DOGFilter = DOGFilterType::New();

  // We're filtering the output of the binomial filter
  DOGFilter->SetInput(blurredImage);

  // Get the output of the gradient filter
  DOGFilterType::TOutputImage::Pointer gradientImage = DOGFilter->GetOutput();

  // Go!
  DOGFilter->Update();

  //------------------------Blox Boundary Point Analysis-------------------------

  typedef itk::GradientImageToBloxBoundaryPointImageFilter<DOGFilterType::TOutputImage> TBPFilter;
  typedef TBPFilter::TOutputImage BloxBPImageType;

  TBPFilter::Pointer bpFilter= TBPFilter::New();
  bpFilter->SetInput( DOGFilter->GetOutput() );

  // Test the macros in the filter
  bpFilter->SetThreshold(128.0);

  if(bpFilter->GetThreshold() != 128.0)
    return EXIT_FAILURE;

  BloxBPImageType::Pointer bloxBoundaryPointImage = bpFilter->GetOutput();

  bpFilter->Update();

  //-------------------Pull boundary points out of the image----------------------

  // Create an iterator that will walk the blox image
  typedef itk::ImageRegionIterator<BloxBPImageType> BloxIterator;

  BloxIterator bloxIt = BloxIterator(bloxBoundaryPointImage,
                                     bloxBoundaryPointImage->GetRequestedRegion() );

  // Used for obtaining the index of a pixel
  BloxBPImageType::IndexType bloxindex;

  // Used for obtaining position data from a BloxPoint
  itk::Point<double, 3> position;

  // Position are we at in the list
  int depth;

  for ( bloxIt.GoToBegin(); !bloxIt.IsAtEnd(); ++bloxIt)
    {
    // The iterator for accessing linked list info
    itk::BloxBoundaryPointPixel<3>::iterator bpiterator;

    // What position are we at in the list?
    depth = 0;

    // Get the index of the pixel
    bloxindex = bloxIt.GetIndex();

    // Get the number of items in the pixel to exercise the GetSize function
    unsigned long int numItems = bloxIt.Value().GetSize();

    // Walk through all of the elements at the pixel
    for (bpiterator = bloxIt.Value().begin(); bpiterator != bloxIt.Value().end(); ++bpiterator)
      {
      position = (*bpiterator)->GetPhysicalPosition();
      depth++;
      std::cout << "Boundary point at ";
      std::cout << "Position=(" << position[0] << " " << position[1] << " " << position[2] << ") ";
      std::cout << "at index=(" << bloxindex.m_Index[0] << " " << bloxindex.m_Index[1] << " " << bloxindex.m_Index[2] << "),";
      std::cout << "depth=" << depth << "\n";
      } // end iterate
    }

  return EXIT_SUCCESS;
}
<commit_msg>FIX: Warning: unused variable. Now it is printed out.<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkBloxBoundaryPointImageTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include <stdio.h>

// Native ITK stuff
#include "itkSize.h"
#include "itkIndex.h"
#include "itkImage.h"
#include "itkImageRegionIterator.h"
#include "itkPoint.h"

// Blox stuff
#include "itkBloxBoundaryPointPixel.h"
#include "itkBloxBoundaryPointImage.h"
#include "itkGradientImageToBloxBoundaryPointImageFilter.h"

// Spatial function stuff
#include "itkSphereSpatialFunction.h"
#include "itkFloodFilledSpatialFunctionConditionalIterator.h"

// DOG gradient related stuff
#include "itkBinomialBlurImageFilter.h"
#include "itkDifferenceOfGaussiansGradientImageFilter.h"

// Main for testing BloxImage/BloxPixel storage
int itkBloxBoundaryPointImageTest(int, char* [] )
{
  const unsigned int dim = 3;

  //-----------------Create a new input image--------------------
  // Image size and spacing parameters
  unsigned long sourceImageSize[]  = { 20,20,20 };
  double sourceImageSpacing[] = { 1.0,1.0,1.0 };
  double sourceImageOrigin[] = { 0,0,0 };

  // Image typedef
  typedef itk::Image< unsigned char, dim > ImageType;

  // Creates the sourceImage (but doesn't set the size or allocate memory)
  ImageType::Pointer sourceImage = ImageType::New();
  sourceImage->SetOrigin(sourceImageOrigin);
  sourceImage->SetSpacing(sourceImageSpacing);

  printf("New sourceImage created\n");

  //-----The following block allocates the sourceImage-----

  // Create a size object native to the sourceImage type
  ImageType::SizeType sourceImageSizeObject;
  // Set the size object to the array defined earlier
  sourceImageSizeObject.SetSize( sourceImageSize );
  // Create a region object native to the sourceImage type
  ImageType::RegionType largestPossibleRegion;
  // Resize the region
  largestPossibleRegion.SetSize( sourceImageSizeObject );
  // Set the largest legal region size (i.e. the size of the whole sourceImage) to what we just defined
  sourceImage->SetLargestPossibleRegion( largestPossibleRegion );
  // Set the buffered region
  sourceImage->SetBufferedRegion( largestPossibleRegion );
  // Set the requested region
  sourceImage->SetRequestedRegion( largestPossibleRegion );
  // Now allocate memory for the sourceImage
  sourceImage->Allocate();

  printf("New sourceImage allocated\n");

  // Initialize the image to hold all 0's
  itk::ImageRegionIterator<ImageType> it =
    itk::ImageRegionIterator<ImageType>(sourceImage, largestPossibleRegion);

  for(it.GoToBegin(); !it.IsAtEnd(); ++it)
    {
    it.Set(0);
    }

  //---------Create and initialize a spatial function-----------

  typedef itk::SphereSpatialFunction<dim> FunctionType;
  typedef FunctionType::InputType FunctionPositionType;

  // Create and initialize a new sphere function

  FunctionType::Pointer spatialFunc = FunctionType::New();
  spatialFunc->SetRadius( 5 );

  FunctionPositionType center;
  center[0]=10;
  center[1]=10;
  center[2]=10;
  spatialFunc->SetCenter(center);

  printf("Sphere spatial function created\n");

  //---------Create and initialize a spatial function iterator-----------
  ImageType::IndexType seedPos;
  const ImageType::IndexValueType pos[] = {10,10,10};
  seedPos.SetIndex(pos);

  typedef itk::FloodFilledSpatialFunctionConditionalIterator
    <ImageType, FunctionType> ItType;
  ItType sfi = ItType(sourceImage, spatialFunc, seedPos);

  // Iterate through the entire image and set interior pixels to 255
  for( ; !( sfi.IsAtEnd() ); ++sfi)
    {
    sfi.Set(255);
    }

  printf("Spatial function iterator created, sphere drawn\n");

  //--------------------Do blurring and edge detection----------------
  typedef ImageType OutputType;

  // Create a binomial blur filter
  itk::BinomialBlurImageFilter<ImageType, OutputType>::Pointer binfilter;
  binfilter = itk::BinomialBlurImageFilter<ImageType, OutputType>::New();

  sourceImage->SetRequestedRegion(sourceImage->GetLargestPossibleRegion() );

  // Set filter parameters
  binfilter->SetInput(sourceImage);
  binfilter->SetRepetitions(4);

  // Set up the output of the filter
  ImageType::Pointer blurredImage = binfilter->GetOutput();

  // Execute the filter
  binfilter->Update();
  std::cout << "Binomial blur filter updated\n";

  // Create a differennce of gaussians gradient filter
  typedef itk::DifferenceOfGaussiansGradientImageFilter<OutputType,
    double> DOGFilterType;
  DOGFilterType::Pointer DOGFilter = DOGFilterType::New();

  // We're filtering the output of the binomial filter
  DOGFilter->SetInput(blurredImage);

  // Get the output of the gradient filter
  DOGFilterType::TOutputImage::Pointer gradientImage = DOGFilter->GetOutput();

  // Go!
  DOGFilter->Update();

  //------------------------Blox Boundary Point Analysis-------------------------

  typedef itk::GradientImageToBloxBoundaryPointImageFilter<DOGFilterType::TOutputImage> TBPFilter;
  typedef TBPFilter::TOutputImage BloxBPImageType;

  TBPFilter::Pointer bpFilter= TBPFilter::New();
  bpFilter->SetInput( DOGFilter->GetOutput() );

  // Test the macros in the filter
  bpFilter->SetThreshold(128.0);

  if(bpFilter->GetThreshold() != 128.0)
    return EXIT_FAILURE;

  BloxBPImageType::Pointer bloxBoundaryPointImage = bpFilter->GetOutput();

  bpFilter->Update();

  //-------------------Pull boundary points out of the image----------------------

  // Create an iterator that will walk the blox image
  typedef itk::ImageRegionIterator<BloxBPImageType> BloxIterator;

  BloxIterator bloxIt = BloxIterator(bloxBoundaryPointImage,
                                     bloxBoundaryPointImage->GetRequestedRegion() );

  // Used for obtaining the index of a pixel
  BloxBPImageType::IndexType bloxindex;

  // Used for obtaining position data from a BloxPoint
  itk::Point<double, 3> position;

  // Position are we at in the list
  int depth;

  for ( bloxIt.GoToBegin(); !bloxIt.IsAtEnd(); ++bloxIt)
    {
    // The iterator for accessing linked list info
    itk::BloxBoundaryPointPixel<3>::iterator bpiterator;

    // What position are we at in the list?
    depth = 0;

    // Get the index of the pixel
    bloxindex = bloxIt.GetIndex();

    // Get the number of items in the pixel to exercise the GetSize function
    unsigned long int numItems = bloxIt.Value().GetSize();
    std::cout << "Number of items in the pixel = " << numItems << std::endl;

    // Walk through all of the elements at the pixel
    for (bpiterator = bloxIt.Value().begin(); bpiterator != bloxIt.Value().end(); ++bpiterator)
      {
      position = (*bpiterator)->GetPhysicalPosition();
      depth++;
      std::cout << "Boundary point at ";
      std::cout << "Position=(" << position[0] << " " << position[1] << " " << position[2] << ") ";
      std::cout << "at index=(" << bloxindex.m_Index[0] << " " << bloxindex.m_Index[1] << " " << bloxindex.m_Index[2] << "),";
      std::cout << "depth=" << depth << "\n";
      } // end iterate
    }

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2014 ETH Zurich
 ** 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 the ETH Zurich nor the names of its contributors 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 HOLDER 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 "GridLayoutFormElement.h"

#include "DeclarativeItemBase.h"
#include "GridLayouter.h"
#include "../items/ItemRegion.h"
#include "../cursor/LayoutCursor.h"

namespace Visualization {

GridLayoutFormElement::GridLayoutFormElement()
{
	// initialize element grid
	elementGrid_ = QVector<QVector<FormElement*>>(numColumns_, QVector<FormElement*>(numRows_));

	// initialize span grid
	spanGrid_ = QVector<QVector<QPair<int, int>>>(numColumns_, QVector<QPair<int, int>>(numRows_, QPair<int, int>(1, 1)));

	// initialize alignments
	defaultColumnHorizontalAlignments_ = QVector<LayoutStyle::Alignment>(numColumns_, defaultVerticalAlignment_);
	defaultRowVerticalAlignments_ = QVector<LayoutStyle::Alignment>(numRows_, defaultHorizontalAlignment_);
	cellVerticalAlignmentGrid_ = QVector<QVector<LayoutStyle::Alignment>>(numColumns_,
			QVector<LayoutStyle::Alignment>(numRows_, defaultVerticalAlignment_));
	cellHorizontalAlignmentGrid_ = QVector<QVector<LayoutStyle::Alignment>>(numColumns_,
			QVector<LayoutStyle::Alignment>(numRows_, defaultHorizontalAlignment_));

	// initialize stretch factors
	columnStretchFactors_ = QVector<float>(numColumns_, defaultColumnStretchFactor_);
	rowStretchFactors_ = QVector<float>(numRows_, defaultRowStretchFactor_);
	computeOverallStretchFactors();
}

GridLayoutFormElement::GridLayoutFormElement(const GridLayoutFormElement& other) : LayoutFormElement{other},
	// Copy everything and then clone all child FormElements
	numColumns_{other.numColumns_},
	numRows_{other.numRows_},
	spaceBetweenColumns_{other.spaceBetweenColumns_},
	spaceBetweenRows_{other.spaceBetweenRows_},

	lastCell_{other.lastCell_},

	elementGrid_{other.elementGrid_},
	spanGrid_{other.spanGrid_},

	defaultHorizontalAlignment_{other.defaultHorizontalAlignment_},
	defaultVerticalAlignment_{other.defaultVerticalAlignment_},
	defaultRowVerticalAlignments_{other.defaultRowVerticalAlignments_},
	defaultColumnHorizontalAlignments_{other.defaultColumnHorizontalAlignments_},
	cellHorizontalAlignmentGrid_{other.cellHorizontalAlignmentGrid_},
	cellVerticalAlignmentGrid_{other.cellVerticalAlignmentGrid_},

	defaultColumnStretchFactor_{other.defaultColumnStretchFactor_},
	defaultRowStretchFactor_{other.defaultRowStretchFactor_},
	columnStretchFactors_{other.columnStretchFactors_},
	rowStretchFactors_{other.rowStretchFactors_},
	overallColumnStretchFactor_{other.overallColumnStretchFactor_},
	overallRowStretchFactor_{other.overallRowStretchFactor_}
{
	// Adjust child Elements
	for (int x = 0; x<elementGrid_.size(); ++x)
		for (int y = 0; y<elementGrid_[x].size(); ++y)
			if (elementGrid_[x][y])
				{
					auto child = elementGrid_[x][y]->clone();
					elementGrid_[x][y] = child;
					addChild(child);
				}
}

GridLayoutFormElement::~GridLayoutFormElement()
{
	// elements were deleted by Element
}

GridLayoutFormElement* GridLayoutFormElement::clone() const
{
	return new GridLayoutFormElement(*this);
}

GridLayoutFormElement* GridLayoutFormElement::put(int column, int row, FormElement* element)
{
	auto toAdd = element->cloneIfAlreadyUsed();

	adjustSize(column, row);
	lastCell_ = QPair<int, int>(column, row);

	removeChild(elementGrid_[column][row]);
	addChild(toAdd);

	SAFE_DELETE(elementGrid_[column][row]);
	elementGrid_[column][row] = toAdd;
	return this;
}

void GridLayoutFormElement::computeSize(Item* item, int availableWidth, int availableHeight)
{
	QSize finalSize = GridLayouter::computeSize<true>(availableWidth, availableHeight, GridLayouter::NoMajor,
		[this](){return numRows_;},	// numRows
		[this](){return numColumns_;},	// numColumns
		[this](int x, int y){return elementGrid_[x][y];},	// has
		[this](int x, int y){return spanGrid_[x][y];},	// spanGrid
		[this, item](int x, int y){return elementGrid_[x][y]->width(item);},	// width
		[this, item](int x, int y){return elementGrid_[x][y]->height(item);},	// height
		[this, item](int x, int y, int w, int h){elementGrid_[x][y]->computeSize(item, w, h);},	// computeElementSize
		[this, item](int x, int y, int w, int h){elementGrid_[x][y]->computeSize(item, w, h);},	// changeGeometry
		[this, item](int x, int y){return elementGrid_[x][y]->sizeDependsOnParent(item);},	// isStretchable
		[this, item](int x, int y, QPoint pos){elementGrid_[x][y]->setPos(item, pos);},	// setPosition
		[this](int y){return rowStretchFactors_[y];},	// rowStretchFactors
		[this](int x){return columnStretchFactors_[x];},	// columnStretchFactors
		[this](int x, int y){return cellHorizontalAlignmentGrid_[x][y];},	// horizontalAlignment
		[this](int x, int y){return cellVerticalAlignmentGrid_[x][y];},	// verticalAlignment
		[this](){return spaceBetweenRows_;},	// spaceBetweenRows
		[this](){return spaceBetweenColumns_;},	// spaceBetweenColumns
		[this](){return topMargin();},	// topMargin
		[this](){return bottomMargin();},	// bottomMargin
		[this](){return leftMargin();},	// leftMargin
		[this](){return rightMargin();},	// rightMargin
		[](){return 0;},	// minWidth
		[](){return 0;}	// minHeight
	);

	setSize(item, finalSize);
}

bool GridLayoutFormElement::sizeDependsOnParent(const Item*) const
{
	return overallColumnStretchFactor_ > 0 || overallRowStretchFactor_ > 0;
}

QList<ItemRegion> GridLayoutFormElement::regions(DeclarativeItemBase* item, int parentX, int parentY)
{
	QList<ItemRegion> allRegions;

	// regions for the child elements
	for (int x=0; x<numColumns_; x++)
		for (int y=0; y<numRows_; y++)
			if (elementGrid_[x][y])
				allRegions.append(elementGrid_[x][y]->regions(item, this->x(item) + parentX, this->y(item) + parentY));

	allRegions << GridLayouter::regions(item, this, parentX + x(item), parentY + y(item), GridLayouter::RowMajor,
													false, true, !noBoundaryCursors(item),
			item->style()->extraCursorsOutsideShape(), false, true,
			[this](){return numRows_;},	// numRows
			[this](){return numColumns_;},	// numColumns
			[this](int x, int y){return elementGrid_[x][y];},	// has
			[this, item](int x, int y){return elementGrid_[x][y]->width(item);},	// width
			[this, item](int x, int y){return elementGrid_[x][y]->height(item);},	// height
			[this, item](int x, int y){return elementGrid_[x][y]->x(item) + this->x(item);},	// xPos
			[this, item](int x, int y){return elementGrid_[x][y]->y(item) + this->y(item);},	// yPos
			[](int, int)->Item*{return nullptr;},	// childItem
			[this](){return spaceBetweenRows_;},	// spaceBetweenRows
			[this](){return spaceBetweenColumns_;},	// spaceBetweenColumns
			[this](){return topMargin();},	// topMargin
			[this](){return bottomMargin();},	// bottomMargin
			[this](){return leftMargin();},	// leftMargin
			[this](){return rightMargin();}	// rightMargin
		);

	return allRegions;
}

void GridLayoutFormElement::computeOverallStretchFactors()
{
	overallColumnStretchFactor_ = 0;
	for (auto stretchFactor : columnStretchFactors_) overallColumnStretchFactor_ += stretchFactor;

	overallRowStretchFactor_ = 0;
	for (auto stretchFactor : rowStretchFactors_) overallRowStretchFactor_ += stretchFactor;
}

void GridLayoutFormElement::adjustSize(int containColumn, int containRow)
{
	if (containColumn >= numColumns_ || containRow >= numRows_)
	{
		// calculate new size
		int newNumColumns = (containColumn < numColumns_) ? numColumns_ : containColumn + 1;
		int newNumRows = (containRow < numRows_) ? numRows_ : containRow + 1;

		// adjust element grid
		auto newElementGrid = QVector<QVector<FormElement*>>(newNumColumns, QVector<FormElement*>(newNumRows, nullptr));
		for (int x=0; x<numColumns_; x++)
			for (int y=0; y<numRows_; y++)
				newElementGrid[x][y] = elementGrid_[x][y];
		elementGrid_ = newElementGrid;

		// adjust span grid
		auto newSpanGrid = QVector<QVector<QPair<int, int>>>(
				newNumColumns, QVector<QPair<int, int>>(newNumRows, QPair<int, int>(1, 1)));
		for (int x=0; x<numColumns_; x++)
			for (int y=0; y<numRows_; y++)
				newSpanGrid[x][y] = spanGrid_[x][y];
		spanGrid_ = newSpanGrid;

		// adjust alignments
		if (numColumns_ < newNumColumns)
		{
			auto newColumnAlignments = QVector<LayoutStyle::Alignment>(newNumColumns, defaultHorizontalAlignment_);
			for (int x=0; x<numColumns_; x++)
				newColumnAlignments[x] = defaultColumnHorizontalAlignments_[x];
			defaultColumnHorizontalAlignments_ = newColumnAlignments;
		}

		if (numRows_ < newNumRows)
		{
			auto newRowAlignments = QVector<LayoutStyle::Alignment>(newNumRows, defaultVerticalAlignment_);
			for (int y=0; y<numRows_; y++)
				newRowAlignments[y] = defaultRowVerticalAlignments_[y];
			defaultRowVerticalAlignments_ = newRowAlignments;
		}

		auto newCellHorizontalGrid = QVector<QVector<LayoutStyle::Alignment>>(newNumColumns,
				QVector<LayoutStyle::Alignment>(newNumRows));
		for (int x=0; x<newNumColumns; x++)
			for (int y=0; y<newNumRows; y++)
				if (x<numColumns_ && y<numRows_)
					newCellHorizontalGrid[x][y] = cellHorizontalAlignmentGrid_[x][y];
				else
					newCellHorizontalGrid[x][y] = defaultColumnHorizontalAlignments_[x];
		cellHorizontalAlignmentGrid_ = newCellHorizontalGrid;

		auto newCellVerticalGrid = QVector<QVector<LayoutStyle::Alignment>>(newNumColumns,
				QVector<LayoutStyle::Alignment>(newNumRows));
		for (int x=0; x<newNumColumns; x++)
			for (int y=0; y<newNumRows; y++)
				if (x<numColumns_ && y<numRows_)
					newCellVerticalGrid[x][y] = cellVerticalAlignmentGrid_[x][y];
				else
					newCellVerticalGrid[x][y] = defaultRowVerticalAlignments_[y];
		cellVerticalAlignmentGrid_ = newCellVerticalGrid;

		// adjust stretch factors
		if (numColumns_ < newNumColumns)
		{
			auto newColumnStretchFactors = QVector<float>(newNumColumns, defaultColumnStretchFactor_);
			for (int x=0; x<numColumns_; x++)
				newColumnStretchFactors[x] = columnStretchFactors_[x];
			columnStretchFactors_ = newColumnStretchFactors;
		}

		if (numRows_ < newNumRows)
		{
			auto newRowStretchFactors = QVector<float>(newNumRows, defaultRowStretchFactor_);
			for (int y=0; y<numRows_; y++)
				newRowStretchFactors[y] = rowStretchFactors_[y];
			rowStretchFactors_ = newRowStretchFactors;
		}

		computeOverallStretchFactors();

		// set new grid size
		numColumns_ = newNumColumns;
		numRows_ = newNumRows;
	}
}

int GridLayoutFormElement::focusedElementIndex(Item* item) const
{
	if (numColumns_ == 1)
		for (int y=0; y<numRows_; ++y)
			if (elementGrid_[0][y]->elementOrChildHasFocus(item)) return y;
	if (numRows_ == 1)
		for (int x=0; x<numColumns_; ++x)
			if (elementGrid_[x][0]->elementOrChildHasFocus(item)) return x;

	return -1;
}

QPoint GridLayoutFormElement::focusedElement2DIndex(Item* item) const
{
	for (int x=0; x<numColumns_; ++x)
		for (int y=0; y<numRows_; ++y)
			if (elementGrid_[x][y]->elementOrChildHasFocus(item))
				return {x, y};

	return {-1, -1};
}

int GridLayoutFormElement::length(Visualization::Item*) const
{
	Q_ASSERT(numColumns_ == 1 || numRows_ == 1);
	// TODO: is this the right thing? Should we check for null?
	if (numColumns_ == 1) return numRows_;
	return numColumns_;
}

const Visualization::Item* GridLayoutFormElement::itemAt(Visualization::DeclarativeItemBase* item, int index) const
{
	Q_ASSERT(numColumns_ == 1 || numRows_ == 1);
	FormElement* element = nullptr;
	if (numColumns_ == 1) element = elementGrid_[0][index];
	else element = elementGrid_[index][0];
	auto children = element->allHandledChildPointers();
	Q_ASSERT(children.size() == 1);
	return item->*(children.first());
}

} /* namespace Visualization */
<commit_msg>Fix cursor settings ignored by GridLayoutFormElement<commit_after>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2014 ETH Zurich
 ** 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 the ETH Zurich nor the names of its contributors 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 HOLDER 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 "GridLayoutFormElement.h"

#include "DeclarativeItemBase.h"
#include "GridLayouter.h"
#include "../items/ItemRegion.h"
#include "../cursor/LayoutCursor.h"

namespace Visualization {

GridLayoutFormElement::GridLayoutFormElement()
{
	// initialize element grid
	elementGrid_ = QVector<QVector<FormElement*>>(numColumns_, QVector<FormElement*>(numRows_));

	// initialize span grid
	spanGrid_ = QVector<QVector<QPair<int, int>>>(numColumns_, QVector<QPair<int, int>>(numRows_, QPair<int, int>(1, 1)));

	// initialize alignments
	defaultColumnHorizontalAlignments_ = QVector<LayoutStyle::Alignment>(numColumns_, defaultVerticalAlignment_);
	defaultRowVerticalAlignments_ = QVector<LayoutStyle::Alignment>(numRows_, defaultHorizontalAlignment_);
	cellVerticalAlignmentGrid_ = QVector<QVector<LayoutStyle::Alignment>>(numColumns_,
			QVector<LayoutStyle::Alignment>(numRows_, defaultVerticalAlignment_));
	cellHorizontalAlignmentGrid_ = QVector<QVector<LayoutStyle::Alignment>>(numColumns_,
			QVector<LayoutStyle::Alignment>(numRows_, defaultHorizontalAlignment_));

	// initialize stretch factors
	columnStretchFactors_ = QVector<float>(numColumns_, defaultColumnStretchFactor_);
	rowStretchFactors_ = QVector<float>(numRows_, defaultRowStretchFactor_);
	computeOverallStretchFactors();
}

GridLayoutFormElement::GridLayoutFormElement(const GridLayoutFormElement& other) : LayoutFormElement{other},
	// Copy everything and then clone all child FormElements
	numColumns_{other.numColumns_},
	numRows_{other.numRows_},
	spaceBetweenColumns_{other.spaceBetweenColumns_},
	spaceBetweenRows_{other.spaceBetweenRows_},

	lastCell_{other.lastCell_},

	elementGrid_{other.elementGrid_},
	spanGrid_{other.spanGrid_},

	defaultHorizontalAlignment_{other.defaultHorizontalAlignment_},
	defaultVerticalAlignment_{other.defaultVerticalAlignment_},
	defaultRowVerticalAlignments_{other.defaultRowVerticalAlignments_},
	defaultColumnHorizontalAlignments_{other.defaultColumnHorizontalAlignments_},
	cellHorizontalAlignmentGrid_{other.cellHorizontalAlignmentGrid_},
	cellVerticalAlignmentGrid_{other.cellVerticalAlignmentGrid_},

	defaultColumnStretchFactor_{other.defaultColumnStretchFactor_},
	defaultRowStretchFactor_{other.defaultRowStretchFactor_},
	columnStretchFactors_{other.columnStretchFactors_},
	rowStretchFactors_{other.rowStretchFactors_},
	overallColumnStretchFactor_{other.overallColumnStretchFactor_},
	overallRowStretchFactor_{other.overallRowStretchFactor_}
{
	// Adjust child Elements
	for (int x = 0; x<elementGrid_.size(); ++x)
		for (int y = 0; y<elementGrid_[x].size(); ++y)
			if (elementGrid_[x][y])
				{
					auto child = elementGrid_[x][y]->clone();
					elementGrid_[x][y] = child;
					addChild(child);
				}
}

GridLayoutFormElement::~GridLayoutFormElement()
{
	// elements were deleted by Element
}

GridLayoutFormElement* GridLayoutFormElement::clone() const
{
	return new GridLayoutFormElement(*this);
}

GridLayoutFormElement* GridLayoutFormElement::put(int column, int row, FormElement* element)
{
	auto toAdd = element->cloneIfAlreadyUsed();

	adjustSize(column, row);
	lastCell_ = QPair<int, int>(column, row);

	removeChild(elementGrid_[column][row]);
	addChild(toAdd);

	SAFE_DELETE(elementGrid_[column][row]);
	elementGrid_[column][row] = toAdd;
	return this;
}

void GridLayoutFormElement::computeSize(Item* item, int availableWidth, int availableHeight)
{
	QSize finalSize = GridLayouter::computeSize<true>(availableWidth, availableHeight, GridLayouter::NoMajor,
		[this](){return numRows_;},	// numRows
		[this](){return numColumns_;},	// numColumns
		[this](int x, int y){return elementGrid_[x][y];},	// has
		[this](int x, int y){return spanGrid_[x][y];},	// spanGrid
		[this, item](int x, int y){return elementGrid_[x][y]->width(item);},	// width
		[this, item](int x, int y){return elementGrid_[x][y]->height(item);},	// height
		[this, item](int x, int y, int w, int h){elementGrid_[x][y]->computeSize(item, w, h);},	// computeElementSize
		[this, item](int x, int y, int w, int h){elementGrid_[x][y]->computeSize(item, w, h);},	// changeGeometry
		[this, item](int x, int y){return elementGrid_[x][y]->sizeDependsOnParent(item);},	// isStretchable
		[this, item](int x, int y, QPoint pos){elementGrid_[x][y]->setPos(item, pos);},	// setPosition
		[this](int y){return rowStretchFactors_[y];},	// rowStretchFactors
		[this](int x){return columnStretchFactors_[x];},	// columnStretchFactors
		[this](int x, int y){return cellHorizontalAlignmentGrid_[x][y];},	// horizontalAlignment
		[this](int x, int y){return cellVerticalAlignmentGrid_[x][y];},	// verticalAlignment
		[this](){return spaceBetweenRows_;},	// spaceBetweenRows
		[this](){return spaceBetweenColumns_;},	// spaceBetweenColumns
		[this](){return topMargin();},	// topMargin
		[this](){return bottomMargin();},	// bottomMargin
		[this](){return leftMargin();},	// leftMargin
		[this](){return rightMargin();},	// rightMargin
		[](){return 0;},	// minWidth
		[](){return 0;}	// minHeight
	);

	setSize(item, finalSize);
}

bool GridLayoutFormElement::sizeDependsOnParent(const Item*) const
{
	return overallColumnStretchFactor_ > 0 || overallRowStretchFactor_ > 0;
}

QList<ItemRegion> GridLayoutFormElement::regions(DeclarativeItemBase* item, int parentX, int parentY)
{
	QList<ItemRegion> allRegions;

	// regions for the child elements
	for (int x=0; x<numColumns_; x++)
		for (int y=0; y<numRows_; y++)
			if (elementGrid_[x][y])
				allRegions.append(elementGrid_[x][y]->regions(item, this->x(item) + parentX, this->y(item) + parentY));

	allRegions << GridLayouter::regions(item, this, parentX + x(item), parentY + y(item), GridLayouter::RowMajor,
													hasCursorWhenEmpty(item), !noInnerCursors(item), !noBoundaryCursors(item),
			item->style()->extraCursorsOutsideShape(), false, notLocationEquivalentCursors(item),
			[this](){return numRows_;},	// numRows
			[this](){return numColumns_;},	// numColumns
			[this](int x, int y){return elementGrid_[x][y];},	// has
			[this, item](int x, int y){return elementGrid_[x][y]->width(item);},	// width
			[this, item](int x, int y){return elementGrid_[x][y]->height(item);},	// height
			[this, item](int x, int y){return elementGrid_[x][y]->x(item) + this->x(item);},	// xPos
			[this, item](int x, int y){return elementGrid_[x][y]->y(item) + this->y(item);},	// yPos
			[](int, int)->Item*{return nullptr;},	// childItem
			[this](){return spaceBetweenRows_;},	// spaceBetweenRows
			[this](){return spaceBetweenColumns_;},	// spaceBetweenColumns
			[this](){return topMargin();},	// topMargin
			[this](){return bottomMargin();},	// bottomMargin
			[this](){return leftMargin();},	// leftMargin
			[this](){return rightMargin();}	// rightMargin
		);

	return allRegions;
}

void GridLayoutFormElement::computeOverallStretchFactors()
{
	overallColumnStretchFactor_ = 0;
	for (auto stretchFactor : columnStretchFactors_) overallColumnStretchFactor_ += stretchFactor;

	overallRowStretchFactor_ = 0;
	for (auto stretchFactor : rowStretchFactors_) overallRowStretchFactor_ += stretchFactor;
}

void GridLayoutFormElement::adjustSize(int containColumn, int containRow)
{
	if (containColumn >= numColumns_ || containRow >= numRows_)
	{
		// calculate new size
		int newNumColumns = (containColumn < numColumns_) ? numColumns_ : containColumn + 1;
		int newNumRows = (containRow < numRows_) ? numRows_ : containRow + 1;

		// adjust element grid
		auto newElementGrid = QVector<QVector<FormElement*>>(newNumColumns, QVector<FormElement*>(newNumRows, nullptr));
		for (int x=0; x<numColumns_; x++)
			for (int y=0; y<numRows_; y++)
				newElementGrid[x][y] = elementGrid_[x][y];
		elementGrid_ = newElementGrid;

		// adjust span grid
		auto newSpanGrid = QVector<QVector<QPair<int, int>>>(
				newNumColumns, QVector<QPair<int, int>>(newNumRows, QPair<int, int>(1, 1)));
		for (int x=0; x<numColumns_; x++)
			for (int y=0; y<numRows_; y++)
				newSpanGrid[x][y] = spanGrid_[x][y];
		spanGrid_ = newSpanGrid;

		// adjust alignments
		if (numColumns_ < newNumColumns)
		{
			auto newColumnAlignments = QVector<LayoutStyle::Alignment>(newNumColumns, defaultHorizontalAlignment_);
			for (int x=0; x<numColumns_; x++)
				newColumnAlignments[x] = defaultColumnHorizontalAlignments_[x];
			defaultColumnHorizontalAlignments_ = newColumnAlignments;
		}

		if (numRows_ < newNumRows)
		{
			auto newRowAlignments = QVector<LayoutStyle::Alignment>(newNumRows, defaultVerticalAlignment_);
			for (int y=0; y<numRows_; y++)
				newRowAlignments[y] = defaultRowVerticalAlignments_[y];
			defaultRowVerticalAlignments_ = newRowAlignments;
		}

		auto newCellHorizontalGrid = QVector<QVector<LayoutStyle::Alignment>>(newNumColumns,
				QVector<LayoutStyle::Alignment>(newNumRows));
		for (int x=0; x<newNumColumns; x++)
			for (int y=0; y<newNumRows; y++)
				if (x<numColumns_ && y<numRows_)
					newCellHorizontalGrid[x][y] = cellHorizontalAlignmentGrid_[x][y];
				else
					newCellHorizontalGrid[x][y] = defaultColumnHorizontalAlignments_[x];
		cellHorizontalAlignmentGrid_ = newCellHorizontalGrid;

		auto newCellVerticalGrid = QVector<QVector<LayoutStyle::Alignment>>(newNumColumns,
				QVector<LayoutStyle::Alignment>(newNumRows));
		for (int x=0; x<newNumColumns; x++)
			for (int y=0; y<newNumRows; y++)
				if (x<numColumns_ && y<numRows_)
					newCellVerticalGrid[x][y] = cellVerticalAlignmentGrid_[x][y];
				else
					newCellVerticalGrid[x][y] = defaultRowVerticalAlignments_[y];
		cellVerticalAlignmentGrid_ = newCellVerticalGrid;

		// adjust stretch factors
		if (numColumns_ < newNumColumns)
		{
			auto newColumnStretchFactors = QVector<float>(newNumColumns, defaultColumnStretchFactor_);
			for (int x=0; x<numColumns_; x++)
				newColumnStretchFactors[x] = columnStretchFactors_[x];
			columnStretchFactors_ = newColumnStretchFactors;
		}

		if (numRows_ < newNumRows)
		{
			auto newRowStretchFactors = QVector<float>(newNumRows, defaultRowStretchFactor_);
			for (int y=0; y<numRows_; y++)
				newRowStretchFactors[y] = rowStretchFactors_[y];
			rowStretchFactors_ = newRowStretchFactors;
		}

		computeOverallStretchFactors();

		// set new grid size
		numColumns_ = newNumColumns;
		numRows_ = newNumRows;
	}
}

int GridLayoutFormElement::focusedElementIndex(Item* item) const
{
	if (numColumns_ == 1)
		for (int y=0; y<numRows_; ++y)
			if (elementGrid_[0][y]->elementOrChildHasFocus(item)) return y;
	if (numRows_ == 1)
		for (int x=0; x<numColumns_; ++x)
			if (elementGrid_[x][0]->elementOrChildHasFocus(item)) return x;

	return -1;
}

QPoint GridLayoutFormElement::focusedElement2DIndex(Item* item) const
{
	for (int x=0; x<numColumns_; ++x)
		for (int y=0; y<numRows_; ++y)
			if (elementGrid_[x][y]->elementOrChildHasFocus(item))
				return {x, y};

	return {-1, -1};
}

int GridLayoutFormElement::length(Visualization::Item*) const
{
	Q_ASSERT(numColumns_ == 1 || numRows_ == 1);
	// TODO: is this the right thing? Should we check for null?
	if (numColumns_ == 1) return numRows_;
	return numColumns_;
}

const Visualization::Item* GridLayoutFormElement::itemAt(Visualization::DeclarativeItemBase* item, int index) const
{
	Q_ASSERT(numColumns_ == 1 || numRows_ == 1);
	FormElement* element = nullptr;
	if (numColumns_ == 1) element = elementGrid_[0][index];
	else element = elementGrid_[index][0];
	auto children = element->allHandledChildPointers();
	Q_ASSERT(children.size() == 1);
	return item->*(children.first());
}

} /* namespace Visualization */
<|endoftext|>