- Added "select inside" and "select touching"

Both functions now work with multiple selectionbrushes, allowing complex
  selection operations.
- Added entries for the selectionfunctions in "Edit" and the main toolbar.

git-svn-id: svn://svn.icculus.org/gtkradiant/GtkRadiant/trunk@113 8a3a26a2-13c4-0310-b231-cf6edde360e5
This commit is contained in:
namespace
2006-10-07 15:08:18 +00:00
parent 049119a99d
commit 8d4bd599b6
5 changed files with 200 additions and 3 deletions

View File

@@ -38,6 +38,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "gtkutil/dialog.h"
#include "gtkutil/widget.h"
#include "brushmanip.h"
#include "brush.h"
#include "patchmanip.h"
#include "patchdialog.h"
#include "selection.h"
@@ -52,6 +53,172 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
select_workzone_t g_select_workzone;
/**
Loops over all selected brushes and stores their
world AABBs in the specified array.
*/
class CollectSelectedBrushesBounds : public SelectionSystem::Visitor
{
AABB* m_bounds; // array of AABBs
Unsigned m_max; // max AABB-elements in array
Unsigned& m_count;// count of valid AABBs stored in array
public:
CollectSelectedBrushesBounds(AABB* bounds, Unsigned max, Unsigned& count)
: m_bounds(bounds),
m_max(max),
m_count(count)
{
m_count = 0;
}
void visit(scene::Instance& instance) const
{
ASSERT_MESSAGE(m_count <= m_max, "Invalid m_count in CollectSelectedBrushesBounds");
// stop if the array is already full
if(m_count == m_max)
return;
Selectable* selectable = Instance_getSelectable(instance);
if((selectable != 0)
&& instance.isSelected())
{
// brushes only
if(Instance_getBrush(instance) != 0)
{
m_bounds[m_count] = instance.worldAABB();
++m_count;
}
}
}
};
/**
Selects all objects that intersect one of the bounding AABBs.
The exact intersection-method is specified through TSelectionPolicy
*/
template<class TSelectionPolicy>
class SelectByBounds : public scene::Graph::Walker
{
AABB* m_aabbs; // selection aabbs
Unsigned m_count; // number of aabbs in m_aabbs
TSelectionPolicy policy; // type that contains a custom intersection method aabb<->aabb
public:
SelectByBounds(AABB* aabbs, Unsigned count)
: m_aabbs(aabbs),
m_count(count)
{
}
bool pre(const scene::Path& path, scene::Instance& instance) const
{
Selectable* selectable = Instance_getSelectable(instance);
// ignore worldspawn
Entity* entity = Node_getEntity(path.top());
if(entity)
{
if(string_equal(entity->getKeyValue("classname"), "worldspawn"))
return true;
}
if( (path.size() > 1) &&
(!path.top().get().isRoot()) &&
(selectable != 0)
)
{
for(Unsigned i = 0; i < m_count; ++i)
{
if(policy.Evaluate(m_aabbs[i], instance))
{
selectable->setSelected(true);
}
}
}
return true;
}
/**
Performs selection operation on the global scenegraph.
If delete_bounds_src is true, then the objects which were
used as source for the selection aabbs will be deleted.
*/
static void DoSelection(bool delete_bounds_src = true)
{
if(GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive)
{
// we may not need all AABBs since not all selected objects have to be brushes
const Unsigned max = (Unsigned)GlobalSelectionSystem().countSelected();
AABB* aabbs = new AABB[max];
Unsigned count;
CollectSelectedBrushesBounds collector(aabbs, max, count);
GlobalSelectionSystem().foreachSelected(collector);
// nothing usable in selection
if(!count)
{
delete[] aabbs;
return;
}
// delete selected objects
if(delete_bounds_src)// see deleteSelection
{
UndoableCommand undo("deleteSelected");
Select_Delete();
}
// select objects with bounds
GlobalSceneGraph().traverse(SelectByBounds<TSelectionPolicy>(aabbs, count));
SceneChangeNotify();
delete[] aabbs;
}
}
};
/**
SelectionPolicy for SelectByBounds
Returns true if box and the AABB of instance intersect
*/
class SelectionPolicy_Touching
{
public:
bool Evaluate(const AABB& box, scene::Instance& instance) const
{
const AABB& other(instance.worldAABB());
for(Unsigned i = 0; i < 3; ++i)
{
if(fabsf(box.origin[i] - other.origin[i]) > (box.extents[i] + other.extents[i]))
return false;
}
return true;
}
};
/**
SelectionPolicy for SelectByBounds
Returns true if the AABB of instance is inside box
*/
class SelectionPolicy_Inside
{
public:
bool Evaluate(const AABB& box, scene::Instance& instance) const
{
const AABB& other(instance.worldAABB());
for(Unsigned i = 0; i < 3; ++i)
{
if(fabsf(box.origin[i] - other.origin[i]) > (box.extents[i] - other.extents[i]))
return false;
}
return true;
}
};
class DeleteSelected : public scene::Graph::Walker
{
mutable bool m_remove;
@@ -73,12 +240,13 @@ public:
{
m_remove = true;
return false;
return false;// dont traverse into child elements
}
return true;
}
void post(const scene::Path& path, scene::Instance& instance) const
{
if(m_removedChild)
{
m_removedChild = false;
@@ -93,6 +261,7 @@ public:
}
}
// node should be removed
if(m_remove)
{
if(Node_isEntity(path.parent()) != 0)
@@ -575,7 +744,15 @@ void Select_AllOfType()
}
}
void Select_Inside(void)
{
SelectByBounds<SelectionPolicy_Inside>::DoSelection();
}
void Select_Touching(void)
{
SelectByBounds<SelectionPolicy_Touching>::DoSelection(false);
}
void Select_FitTexture(float horizontal, float vertical)
{