Skip to content

Commit

Permalink
#5122: Add SelectByFilterWalker to evaluate whether a single XMLFilte…
Browse files Browse the repository at this point in the history
…r applies to various nodes in which case they will be highlighted
  • Loading branch information
codereader committed Mar 29, 2020
1 parent 0da2832 commit 7b57cad
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 78 deletions.
60 changes: 50 additions & 10 deletions radiant/filters/BasicFilterSystem.cpp
@@ -1,6 +1,6 @@
#include "BasicFilterSystem.h"

#include "InstanceUpdateWalker.h"
#include <functional>

#include "iradiant.h"
#include "itextstream.h"
Expand All @@ -11,7 +11,8 @@
#include "ishaders.h"
#include "modulesystem/StaticModule.h"

#include <functional>
#include "InstanceUpdateWalker.h"
#include "SelectByFilterWalker.h"

namespace filters
{
Expand Down Expand Up @@ -68,6 +69,32 @@ void BasicFilterSystem::setAllFilterStatesCmd(const cmd::ArgumentList& args)
setAllFilterStates(args.front().getInt() != 0);
}

void BasicFilterSystem::selectObjectsByFilterCmd(const cmd::ArgumentList& args)
{
if (args.size() != 1)
{
rMessage() << "Usage: SelectObjectsByFilter FilterName" << std::endl;
return;
}

if (!GlobalSceneGraph().root())
{
rError() << "No map loaded." << std::endl;
return;
}

auto f = _availableFilters.find(args[0].getString());

if (f == _availableFilters.end())
{
rError() << "Cannot find the filter named " << args[0].getString() << std::endl;
return;
}

SelectByFilterWalker walker(*this, f->second, true);
GlobalSceneGraph().root()->traverse(walker);
}

// Initialise the filter system
void BasicFilterSystem::initialiseModule(const ApplicationContext& ctx)
{
Expand Down Expand Up @@ -97,6 +124,9 @@ void BasicFilterSystem::initialiseModule(const ApplicationContext& ctx)

GlobalEventManager().addCommand("ActivateAllFilters", "ActivateAllFilters");
GlobalEventManager().addCommand("DeactivateAllFilters", "DeactivateAllFilters");

GlobalCommandSystem().addCommand("SelectObjectsByFilter",
std::bind(&BasicFilterSystem::selectObjectsByFilterCmd, this, std::placeholders::_1), { cmd::ARGTYPE_STRING });
}

void BasicFilterSystem::addFiltersFromXML(const xml::NodeList& nodes, bool readOnly)
Expand Down Expand Up @@ -153,17 +183,17 @@ void BasicFilterSystem::addFiltersFromXML(const xml::NodeList& nodes, bool readO
).first->second;

// Add the according toggle command to the eventmanager
IEventPtr fEvent = GlobalEventManager().addToggle(
filter.getEventName(),
std::bind(&XMLFilter::toggle, &inserted, std::placeholders::_1)
);
auto fEvent = createEventToggle(inserted);

// If this filter is in our active set, enable it
if (activeFilterNames.find(filterName) != activeFilterNames.end())
{
fEvent->setToggled(true);
_activeFilters.insert(std::make_pair(filterName, inserted));
}

// Add the statement to the command system
// TODO
}
}

Expand Down Expand Up @@ -253,6 +283,11 @@ std::string BasicFilterSystem::getFilterEventName(const std::string& filter)
return f != _availableFilters.end() ? f->second.getEventName() : std::string();
}

bool BasicFilterSystem::getFilterState(const std::string& filter)
{
return _activeFilters.find(filter) != _activeFilters.end();
}

// Change the state of a named filter
void BasicFilterSystem::setFilterState(const std::string& filter, bool state)
{
Expand Down Expand Up @@ -320,10 +355,7 @@ bool BasicFilterSystem::addFilter(const std::string& filterName, const FilterRul
result.first->second.setRules(ruleSet);

// Add the according toggle command to the eventmanager
auto fEvent = GlobalEventManager().addToggle(
result.first->second.getEventName(),
std::bind(&XMLFilter::toggle, &result.first->second, std::placeholders::_1)
);
createEventToggle(result.first->second);

// Clear the cache, the rules have changed
_visibilityCache.clear();
Expand All @@ -333,6 +365,14 @@ bool BasicFilterSystem::addFilter(const std::string& filterName, const FilterRul
return true;
}

IEventPtr BasicFilterSystem::createEventToggle(XMLFilter& filter)
{
return GlobalEventManager().addToggle(
filter.getEventName(),
std::bind(&XMLFilter::toggle, &filter, std::placeholders::_1)
);
}

bool BasicFilterSystem::removeFilter(const std::string& filter)
{
auto f = _availableFilters.find(filter);
Expand Down
65 changes: 31 additions & 34 deletions radiant/filters/BasicFilterSystem.h
Expand Up @@ -4,6 +4,7 @@
#include "imodule.h"
#include "ifilter.h"
#include "icommandsystem.h"
#include "ieventmanager.h"
#include "xmlutil/Node.h"

#include <map>
Expand All @@ -16,10 +17,10 @@ namespace filters

/** FilterSystem implementation class.
*/

class BasicFilterSystem
: public FilterSystem
class BasicFilterSystem :
public FilterSystem
{
private:
// Hashtable of available filters, indexed by name
typedef std::map<std::string, XMLFilter> FilterTable;
FilterTable _availableFilters;
Expand All @@ -46,68 +47,64 @@ class BasicFilterSystem

void addFiltersFromXML(const xml::NodeList& nodes, bool readOnly);

public:
virtual ~BasicFilterSystem() {}
IEventPtr createEventToggle(XMLFilter& filter);

void setAllFilterStatesCmd(const cmd::ArgumentList& args);

void selectObjectsByFilterCmd(const cmd::ArgumentList& args);

public:
// FilterSystem implementation
sigc::signal<void> filtersChangedSignal() const;
sigc::signal<void> filtersChangedSignal() const override;

// Invoke the InstanceUpateWalker to update the filtered status.
void update();
void update() override;

// Updates the given subgraph
void updateSubgraph(const scene::INodePtr& root);
void updateSubgraph(const scene::INodePtr& root) override;

// Filter system visit function
void forEachFilter(IFilterVisitor& visitor);
void forEachFilter(IFilterVisitor& visitor) override;

std::string getFilterEventName(const std::string& filter);
std::string getFilterEventName(const std::string& filter) override;

bool getFilterState(const std::string& filter) {
return (_activeFilters.find(filter) != _activeFilters.end());
}
bool getFilterState(const std::string& filter) override;

// Set the state of a filter
void setFilterState(const std::string& filter, bool state);
void setFilterState(const std::string& filter, bool state) override;

// Query whether an item is visible or filtered out
bool isVisible(const FilterRule::Type type, const std::string& name);
bool isVisible(const FilterRule::Type type, const std::string& name) override;

// Query whether an entity is visible or filtered out
bool isEntityVisible(const FilterRule::Type type, const Entity& entity);
bool isEntityVisible(const FilterRule::Type type, const Entity& entity) override;

// Whether this filter is read-only and can't be changed
bool filterIsReadOnly(const std::string& filter);
bool filterIsReadOnly(const std::string& filter) override;

// Adds a new filter to the system
bool addFilter(const std::string& filterName, const FilterRules& ruleSet);
bool addFilter(const std::string& filterName, const FilterRules& ruleSet) override;

// Removes the filter and returns true on success
bool removeFilter(const std::string& filter);
bool removeFilter(const std::string& filter) override;

// Renames the filter and updates eventmanager
bool renameFilter(const std::string& oldFilterName, const std::string& newFilterName);
bool renameFilter(const std::string& oldFilterName, const std::string& newFilterName) override;

// Returns the ruleset of the named filter
FilterRules getRuleSet(const std::string& filter);
FilterRules getRuleSet(const std::string& filter) override;

// Applies the ruleset and replaces the previous one for a given filter.
bool setFilterRules(const std::string& filter, const FilterRules& ruleSet);
bool setFilterRules(const std::string& filter, const FilterRules& ruleSet) override;

/**
* Activates or deactivates all known filters.
*/
void setAllFilterStates(bool state);

// Command target, inspects arguments and passes on to the
void setAllFilterStatesCmd(const cmd::ArgumentList& args);
// Activates or deactivates all known filters.
void setAllFilterStates(bool state) override;

// RegisterableModule implementation
virtual const std::string& getName() const;
virtual const StringSet& getDependencies() const;
virtual void initialiseModule(const ApplicationContext& ctx);
virtual void shutdownModule();
const std::string& getName() const override;
const StringSet& getDependencies() const override;
void initialiseModule(const ApplicationContext& ctx) override;
void shutdownModule() override;
};
typedef std::shared_ptr<BasicFilterSystem> BasicFilterSystemPtr;

} // namespace filters
83 changes: 49 additions & 34 deletions radiant/filters/InstanceUpdateWalker.h
@@ -1,9 +1,8 @@
#pragma once

#include "iscenegraph.h"
#include "inode.h"
#include "ientity.h"
#include "iselectable.h"
#include "ieclass.h"
#include "ipatch.h"
#include "ibrush.h"

Expand Down Expand Up @@ -42,8 +41,8 @@ class NodeVisibilityUpdater :
};

/**
* Scenegraph walker to update filtered status of Instances based on the
* status of their parent entity class.
* Scenegraph walker to update filtered status of nodes based on the
* currently active set of filters.
*/
class InstanceUpdateWalker :
public scene::NodeVisitor
Expand All @@ -69,63 +68,79 @@ class InstanceUpdateWalker :
_brushesAreVisible(_filterSystem.isVisible(FilterRule::TYPE_OBJECT, "brush"))
{}

// Pre-descent walker function
bool pre(const scene::INodePtr& node) override
{
// Check entity eclass and spawnargs
if (Node_isEntity(node))
{
Entity* entity = Node_getEntity(node);
bool isVisible = evaluateEntity(node);

// Check the eclass first
bool entityIsVisible = _filterSystem.isEntityVisible(FilterRule::TYPE_ENTITYCLASS, *entity) &&
_filterSystem.isEntityVisible(FilterRule::TYPE_ENTITYKEYVALUE, *entity);
setSubgraphFilterStatus(node, isVisible);

node->traverse(entityIsVisible ? _showWalker : _hideWalker);

if (!entityIsVisible)
{
// de-select this node and all children
node->traverse(_deselector);
}

// If the entity is hidden, don't traverse the child nodes
return entityIsVisible;
// If the entity is hidden, don't traverse its child nodes
return isVisible;
}

// greebo: Update visibility of Patches
// greebo: Check visibility of Patches
if (Node_isPatch(node))
{
auto patchNode = std::dynamic_pointer_cast<IPatchNode>(node);
bool isVisible = evaluatePatch(node);

bool isVisible = _patchesAreVisible && patchNode->getPatch().hasVisibleMaterial();

node->traverse(isVisible ? _showWalker : _hideWalker);
setSubgraphFilterStatus(node, isVisible);
}
// greebo: Update visibility of Brushes
// greebo: Check visibility of Brushes
else if (Node_isBrush(node))
{
auto brush = Node_getIBrush(node);

bool isVisible = _brushesAreVisible && brush->hasVisibleMaterial();
bool isVisible = evaluateBrush(node);

node->traverse(isVisible ? _showWalker : _hideWalker);
setSubgraphFilterStatus(node, isVisible);

// In case the brush has at least one visible material trigger a fine-grained update
if (isVisible)
{
brush->updateFaceVisibility();
Node_getIBrush(node)->updateFaceVisibility();
}
}

if (!node->visible())
// Continue the traversal
return true;
}

private:
bool evaluateEntity(const scene::INodePtr& node)
{
assert(Node_isEntity(node));

Entity* entity = Node_getEntity(node);

// Check the eclass first
return _filterSystem.isEntityVisible(FilterRule::TYPE_ENTITYCLASS, *entity) &&
_filterSystem.isEntityVisible(FilterRule::TYPE_ENTITYKEYVALUE, *entity);
}

bool evaluatePatch(const scene::INodePtr& node)
{
assert(Node_isPatch(node));

return _patchesAreVisible && Node_getIPatch(node)->hasVisibleMaterial();
}

bool evaluateBrush(const scene::INodePtr& node)
{
assert(Node_isBrush(node));

return _brushesAreVisible && Node_getIBrush(node)->hasVisibleMaterial();
}

void setSubgraphFilterStatus(const scene::INodePtr& node, bool isVisible)
{
node->traverse(isVisible ? _showWalker : _hideWalker);

if (!isVisible)
{
// de-select this node and all children
node->traverse(_deselector);
}

// Continue the traversal
return true;
}
};

Expand Down

0 comments on commit 7b57cad

Please sign in to comment.