Skip to content

IfcConvert and IfcGeomIterator filtering improvements#188

Merged
aothms merged 22 commits into
IfcOpenShell:masterfrom
Tridify:include-exclude
Mar 4, 2017
Merged

IfcConvert and IfcGeomIterator filtering improvements#188
aothms merged 22 commits into
IfcOpenShell:masterfrom
Tridify:include-exclude

Conversation

@Stinkfist0

@Stinkfist0 Stinkfist0 commented Feb 23, 2017

Copy link
Copy Markdown
Contributor
  • possibility to --include and --exclude simultaneously: however, only single --include and --exclude supported for now
  • "outsourced" filtering from IfcGeomIterator to any possible client code instead, default implementations for old and new filters provided
  • filtering by Description and Tag
  • --<include|exclude> --<enties|layers|guids|names> <value1 value2 ...> syntax changed to --<include|exclude>=<entities|layers|arg> (<GlobalId|Name|Description|Tag>) <value1 value2 ...>
  • closes IfcConvert enhancements #20
  • there's some room for more cleanup and refactoring, will continue the work after the discussed (on IRC) merging of IfcBaseEntity and IfcLateboundEntity happens and after tackling Sub-meshes that share material are merged? #104

…include|exclude>=arg <Name|GlobalId>. The plan is to support arbitrary arguments in the future.
…pport for IfcProxy.Tag (untested, did not find suitable test input yet).
… filter::traverse_match: dynamic_cast instead of static_cast as the decomposing entity is not guaranteed to be IfcProduct
@Stinkfist0 Stinkfist0 changed the title InfcConvert and IfcGeomIterator filtering improvments InfConvert and IfcGeomIterator filtering improvements Feb 24, 2017
@Stinkfist0 Stinkfist0 changed the title InfConvert and IfcGeomIterator filtering improvements IfcConvert and IfcGeomIterator filtering improvements Feb 24, 2017
@Stinkfist0

Stinkfist0 commented Feb 24, 2017

Copy link
Copy Markdown
Contributor Author

Ideally I'd like to see Iterator refactored a bit more. It currently reminds a bit of Java-style iterators, but is lacking certain things. I think something like this would be nice:

No filtering:

IfcGeom::Iterator it(...);
while(it.has_next()) {
    // next() renamed to create_next() here to imply it does work and is not simply a getter
    auto geom = it.create_next();
    // write geom
}

Filtering:

IfcGeom::Iterator it(...);
while(it.has_next()) {
    auto next_prod = it.peek_next_product();
    if (filter_matches(next_prod)) {
        auto geom = it.create_next();
        // write geom
    } else {
        it.skip_next();
    }
}

@Stinkfist0

Stinkfist0 commented Feb 24, 2017

Copy link
Copy Markdown
Contributor Author

While still at it, I could also throw in specifying the ID usage of serialized element (Unique ID or any of the currently supported inclusion/exclusion argument names (GlobalId, Name, Description, Tag), https://sourceforge.net/p/ifcopenshell/discussion/1782717/thread/0ff477aa/#70f4

@aothms

aothms commented Mar 1, 2017

Copy link
Copy Markdown
Member

While I quite like your suggestion, how about making it more STL-like? (We should also really then consider renaming it to something else than iterator.)

IfcGeom::Iterator model(...);
auto filtered_model = ...;
for(auto it = filtered_model.begin(); it != filtered_model.end(); ++it) {
    auto geom = *it; // <- when dereferenced the geometry is interpreted? Is that too unconventional?
}

// range based for also fine of course

// or

for(auto it = model.begin(); it != model.end(); next_matching(filters, it)) {
    auto geom = *it;
}

// And then perhaps sth like:

ColladaSerializer s(...);
std::copy(model.begin(), model.end(), s.begin());

Not sure really, just thinking out loud. For example, would it be nice to std::sort() the geometries at some point? Or perhaps we can implement the filters by means of boost::adaptors::filtered. Or should we implement this triangulation using std::transform()? Just a feeling that if we keep calling this iterator it better matches with the conception that C++ programmers have of iterators.

While still at it, I could also throw in specifying the ID usage of serialized element (Unique ID or any of the currently supported inclusion/exclusion argument names (GlobalId, Name, Description, Tag), https://sourceforge.net/p/ifcopenshell/discussion/1782717/thread/0ff477aa/#70f4

Yeah, sorry, I mixed up things in my head when typing that reply, I implied that that was already being worked on. I'm inclined to say let's first factor out this IfcLateBoundEntity before creating more dependencies on it, but maybe that's not really a consideration.

@aothms

aothms commented Mar 1, 2017

Copy link
Copy Markdown
Member

I think everything is nicely thought out, but one thing was surprising to me

  1. I tried first to filter out internal elements:
    IfcConvert Duplex_A_20110907_optimized.ifc --exclude=arg Name "*Interior*"
  2. This worked well, but left some floating doors from the internal walls, so, I added a --traverse.
    traverse
    The surprising thing is that this is also applied to the default entity filter and all windows and doors disappear as they are considered children of IfcOpeningElement.

I don't see an immediately obvious solution to this, besides perhaps applying --traverse only to filters specified by users on the command line. A sec IfcConvert Duplex_A_20110907_optimized.ifc --traverse also doesn't make a lot of sense to me and the result is also counter intuitive.

Or, how about this, add a plus sign (or asterisks, but then what about shell completion) when filters need to traverse, so: IfcConvert Duplex_A_20110907_optimized.ifc --exclude=arg+ Name "*Interior*"? Thoughts? Would that be a major effort to implement?

@Stinkfist0

Stinkfist0 commented Mar 1, 2017

Copy link
Copy Markdown
Contributor Author

Yeah, that was surprising also to me, at first I even thought it was a bug in my code! Let me ponder for a minute what would be the most convenient way for specifying traversal on a per filter basis, it really comes down simply to boost::program_options parsing and handling as the per filter traversal code is already possible.

@Stinkfist0

Stinkfist0 commented Mar 1, 2017

Copy link
Copy Markdown
Contributor Author

Maybe --include+ and --exclude+ would be convenient to specify traversal on filter basis, but arg+, layers+, and entities+ should work as fine. The latter might be faster to implement.

…d --exclude+ options that control traversal on per filter basis.
@Stinkfist0

Stinkfist0 commented Mar 1, 2017

Copy link
Copy Markdown
Contributor Author

OK! I Should be good to go. I think I'm going to postpone the work with specifying "arbitrary" element ID for now.

@Stinkfist0

Copy link
Copy Markdown
Contributor Author

Couple more minor commits incoming if you don't mind.

@Stinkfist0

Copy link
Copy Markdown
Contributor Author

Oh, and regarding your iterator ideas, those look quite good too, I especially like the look of for(auto it = model.begin(); it != model.end(); next_matching(filters, it)), let's discuss these in more detail in a separate issue.

@Stinkfist0

Copy link
Copy Markdown
Contributor Author

One more pending thing: I just found out that IfcConvert crashes (null ptr access in TokenFunc::asStringRef) when trying to read an empty IFC file, so I'll see if that's easy to fix quickly.

Comment thread src/ifcparse/IfcSpfHeader.cpp Outdated
void IfcSpfHeader::readSemicolon() {
if (!TokenFunc::isOperator(_lexer->Next(), ';')) {
Token next = _lexer->Next();
if (next.lexer != 0 && !TokenFunc::isOperator(next, ';')) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not look into this really in-depth as noticed the IfcSpfLexer* lexer; //TODO: remove it from here comment. Can be refactored properly when time permits.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, I had a look as well. This only protects the header parser. I think that in the data section this issue less likely to manifest itself, as there eof is detected and non complete instances are not registered in the map. Still, I think it might be better to resolve it in asStringRef() to reduce the likelihood of hard segfaults, by simply throwing an exception there it seems the issue is addressed as well (also, the check on lexer is a bit cryptic, better check on type == NONE).

I suggest the following instead at the start of asStringRef():

	if (t.type == Token_NONE) {
		throw IfcParse::IfcException("Null token encountered, premature end of file?");
	}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, thanks, will do as suggested.

@Stinkfist0

Copy link
Copy Markdown
Contributor Author

OK, cleanup up the the fix.

@aothms

aothms commented Mar 4, 2017

Copy link
Copy Markdown
Member

Works well, many thanks. There are some small things in the ifcconvert help text that needs updating, I will commit that right way.

@aothms aothms merged commit 3275119 into IfcOpenShell:master Mar 4, 2017
@Stinkfist0 Stinkfist0 deleted the include-exclude branch March 4, 2017 18:21
@LEILX LEILX mentioned this pull request Jun 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IfcConvert enhancements

2 participants