mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-10 14:07:43 +00:00
Refactor test_sweeps.py: remove _is_swept_shape, restructure test directories, update test fixtures, and adjust assertions for geometry validation. Extend sweep_along_curve constructor to support fixed reference direction.
This commit is contained in:
@@ -123,11 +123,8 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcFixedReferenceSweptAreaSolid
|
|||||||
auto sweep = taxonomy::make<taxonomy::sweep_along_curve>(
|
auto sweep = taxonomy::make<taxonomy::sweep_along_curve>(
|
||||||
matrix, // matrix4::ptr - no transformation needed
|
matrix, // matrix4::ptr - no transformation needed
|
||||||
profile, // face::ptr - the profile to sweep
|
profile, // face::ptr - the profile to sweep
|
||||||
dir // item::ptr curve - the directrix curve
|
dir, // item::ptr curve - the directrix curve
|
||||||
);
|
ref);
|
||||||
|
|
||||||
// Set the fixed reference direction for the sweep
|
|
||||||
sweep->direction = ref; // The fixed reference direction
|
|
||||||
|
|
||||||
return sweep;
|
return sweep;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1215,10 +1215,10 @@ typedef item const* ptr;
|
|||||||
virtual sweep_along_curve* clone_() const { return new sweep_along_curve(*this); }
|
virtual sweep_along_curve* clone_() const { return new sweep_along_curve(*this); }
|
||||||
virtual kinds kind() const { return SWEEP_ALONG_CURVE; }
|
virtual kinds kind() const { return SWEEP_ALONG_CURVE; }
|
||||||
|
|
||||||
sweep_along_curve(matrix4::ptr m, face::ptr basis, item::ptr surf, item::ptr crv) : sweep(m, basis), surface(surf), curve(crv) {}
|
sweep_along_curve(matrix4::ptr m, face::ptr basis, item::ptr surf, item::ptr crv) : sweep(m, basis), surface(surf), curve(crv), direction(nullptr) {}
|
||||||
|
|
||||||
// New constructor for fixed reference swept area solid
|
// New constructor for fixed reference swept area solid
|
||||||
sweep_along_curve(matrix4::ptr m, face::ptr profile, item::ptr directrix) : sweep(m, profile), surface(nullptr), curve(directrix) { }
|
sweep_along_curve(matrix4::ptr m, face::ptr profile, item::ptr directrix, direction3::ptr ref) : sweep(m, profile), surface(nullptr), curve(directrix), direction(ref) { }
|
||||||
|
|
||||||
virtual size_t calc_hash() const {
|
virtual size_t calc_hash() const {
|
||||||
auto v = std::make_tuple(static_cast<size_t>(SWEEP_ALONG_CURVE), matrix->hash_components(), basis->calc_hash(), surface->calc_hash(), curve->calc_hash());
|
auto v = std::make_tuple(static_cast<size_t>(SWEEP_ALONG_CURVE), matrix->hash_components(), basis->calc_hash(), surface->calc_hash(), curve->calc_hash());
|
||||||
|
|||||||
+2
-2
@@ -30,8 +30,8 @@ DATA;
|
|||||||
#30=IFCCARTESIANPOINT((0.,0.));
|
#30=IFCCARTESIANPOINT((0.,0.));
|
||||||
#31=IFCAXIS2PLACEMENT2D(#30,$);
|
#31=IFCAXIS2PLACEMENT2D(#30,$);
|
||||||
#32=IFCCIRCLEPROFILEDEF(.AREA.,$,#31,0.179);
|
#32=IFCCIRCLEPROFILEDEF(.AREA.,$,#31,0.179);
|
||||||
#33=IFCCARTESIANPOINT((289080.818499209,5822851.36621952,118.886));
|
#33=IFCCARTESIANPOINT((289.080,582.2,118.886));
|
||||||
#34=IFCCARTESIANPOINT((289081.570466963,5822857.54453541,118.886));
|
#34=IFCCARTESIANPOINT((290.081,582.9,118.886));
|
||||||
#35=IFCPOLYLINE((#33,#34));
|
#35=IFCPOLYLINE((#33,#34));
|
||||||
#36=IFCDIRECTION((1.,0.,0.));
|
#36=IFCDIRECTION((1.,0.,0.));
|
||||||
#37=IFCFIXEDREFERENCESWEPTAREASOLID(#32,$,#35,$,$,#36);
|
#37=IFCFIXEDREFERENCESWEPTAREASOLID(#32,$,#35,$,$,#36);
|
||||||
Vendored
+10
-10
@@ -1,11 +1,11 @@
|
|||||||
ISO-10303-21;
|
ISO-10303-21;
|
||||||
HEADER;
|
HEADER;
|
||||||
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
|
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
|
||||||
FILE_NAME('/dev/null','2025-08-26T16:01:38+02:00',('AdaUser'),('AdaOrg'),'IfcOpenShell 0.8.2','IfcOpenShell 0.8.2','Nobody');
|
FILE_NAME('/dev/null','2025-08-27T14:53:55+02:00',('AdaUser'),('AdaOrg'),'IfcOpenShell 0.8.2','IfcOpenShell 0.8.2','Nobody');
|
||||||
FILE_SCHEMA(('IFC4X3_ADD2'));
|
FILE_SCHEMA(('IFC4X3_ADD2'));
|
||||||
ENDSEC;
|
ENDSEC;
|
||||||
DATA;
|
DATA;
|
||||||
#1=IFCPROJECT('1TMwI763XBx9E7h3kqXonB',$,'AdaProject',$,$,$,$,(#11),#6);
|
#1=IFCPROJECT('2MG118AczBnBRhfOi7rLGC',$,'AdaProject',$,$,$,$,(#11),#6);
|
||||||
#2=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
#2=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||||
#3=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
#3=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||||
#4=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
#4=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||||
@@ -24,18 +24,18 @@ DATA;
|
|||||||
#17=IFCORGANIZATION('ADA','Assembly For Design and Analysis',$,$,$);
|
#17=IFCORGANIZATION('ADA','Assembly For Design and Analysis',$,$,$);
|
||||||
#18=IFCPERSONANDORGANIZATION(#16,#17,$);
|
#18=IFCPERSONANDORGANIZATION(#16,#17,$);
|
||||||
#19=IFCAPPLICATION(#17,'XXX','ADA','ADA');
|
#19=IFCAPPLICATION(#17,'XXX','ADA','ADA');
|
||||||
#20=IFCOWNERHISTORY(#18,#19,.READWRITE.,$,$,#18,#19,1756216898);
|
#20=IFCOWNERHISTORY(#18,#19,.READWRITE.,$,$,#18,#19,1756299235);
|
||||||
#21=IFCDIRECTION((0.,0.,1.));
|
#21=IFCDIRECTION((0.,0.,1.));
|
||||||
#22=IFCDIRECTION((1.,0.,0.));
|
#22=IFCDIRECTION((1.,0.,0.));
|
||||||
#23=IFCCARTESIANPOINT((0.,0.,0.));
|
#23=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
#24=IFCAXIS2PLACEMENT3D(#23,#21,#22);
|
#24=IFCAXIS2PLACEMENT3D(#23,#21,#22);
|
||||||
#25=IFCLOCALPLACEMENT($,#24);
|
#25=IFCLOCALPLACEMENT($,#24);
|
||||||
#26=IFCSITE('301mrn5JHCxRMMvjp9uGM1',#20,'Ada',$,$,#25,$,$,.ELEMENT.,$,$,$,$,$);
|
#26=IFCSITE('2V9fV9zKnAP9J5CfXLkqps',#20,'Ada',$,$,#25,$,$,.ELEMENT.,$,$,$,$,$);
|
||||||
#27=IFCRELAGGREGATES('0RnSPGnurA1RhM0uNbRZ8k',#20,'Project Container',$,#1,(#26));
|
#27=IFCRELAGGREGATES('12FX4aagX3HQdiLOLDvGX_',#20,'Project Container',$,#1,(#26));
|
||||||
#28=IFCPROPERTYSINGLEVALUE('project',$,IFCTEXT('AdaProject'),$);
|
#28=IFCPROPERTYSINGLEVALUE('project',$,IFCTEXT('AdaProject'),$);
|
||||||
#29=IFCPROPERTYSINGLEVALUE('schema',$,IFCTEXT('IFC4X3_add2'),$);
|
#29=IFCPROPERTYSINGLEVALUE('schema',$,IFCTEXT('IFC4X3_add2'),$);
|
||||||
#30=IFCPROPERTYSET('1Yj9$v$uH0ZQk9AOoWx4hD',#20,'Properties',$,(#28,#29));
|
#30=IFCPROPERTYSET('2yltt4JCTAOAyhAFFKmDBW',#20,'Properties',$,(#28,#29));
|
||||||
#31=IFCRELDEFINESBYPROPERTIES('2zKQjOOuP0VwvxPEpnJusI',#20,'Properties',$,(#26),#30);
|
#31=IFCRELDEFINESBYPROPERTIES('3U0CyjG8XALx2oClwZMPg9',#20,'Properties',$,(#26),#30);
|
||||||
#32=IFCMATERIAL('S355',$,'Steel');
|
#32=IFCMATERIAL('S355',$,'Steel');
|
||||||
#33=IFCPROPERTYSINGLEVALUE('Grade',$,IFCTEXT('S355'),$);
|
#33=IFCPROPERTYSINGLEVALUE('Grade',$,IFCTEXT('S355'),$);
|
||||||
#34=IFCPROPERTYSINGLEVALUE('YieldStress',$,IFCPRESSUREMEASURE(355000000.),$);
|
#34=IFCPROPERTYSINGLEVALUE('YieldStress',$,IFCPRESSUREMEASURE(355000000.),$);
|
||||||
@@ -45,7 +45,7 @@ DATA;
|
|||||||
#38=IFCPROPERTYSINGLEVALUE('SpecificHeatCapacity',$,IFCSPECIFICHEATCAPACITYMEASURE(0.03),$);
|
#38=IFCPROPERTYSINGLEVALUE('SpecificHeatCapacity',$,IFCSPECIFICHEATCAPACITYMEASURE(0.03),$);
|
||||||
#39=IFCPROPERTYSINGLEVALUE('MassDensity',$,IFCMASSDENSITYMEASURE(7850.),$);
|
#39=IFCPROPERTYSINGLEVALUE('MassDensity',$,IFCMASSDENSITYMEASURE(7850.),$);
|
||||||
#40=IFCMATERIALPROPERTIES('MaterialMechanical','A Material property description',(#33,#34,#35,#36,#37,#38,#39),#32);
|
#40=IFCMATERIALPROPERTIES('MaterialMechanical','A Material property description',(#33,#34,#35,#36,#37,#38,#39),#32);
|
||||||
#41=IFCRELASSOCIATESMATERIAL('13SLSaNyz4PwOBYuDhXcWi',#20,'S355','Objects related to S355',(#64),#32);
|
#41=IFCRELASSOCIATESMATERIAL('3__DXGCfr15fk06ugmrrR4',#20,'S355','Objects related to S355',(#64),#32);
|
||||||
#42=IFCDIRECTION((0.,0.,1.));
|
#42=IFCDIRECTION((0.,0.,1.));
|
||||||
#43=IFCDIRECTION((1.,0.,0.));
|
#43=IFCDIRECTION((1.,0.,0.));
|
||||||
#44=IFCCARTESIANPOINT((0.,0.,0.));
|
#44=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
@@ -68,7 +68,7 @@ DATA;
|
|||||||
#61=IFCSURFACESTYLESHADING(#60,0.);
|
#61=IFCSURFACESTYLESHADING(#60,0.);
|
||||||
#62=IFCSURFACESTYLE('Color1',.BOTH.,(#61));
|
#62=IFCSURFACESTYLE('Color1',.BOTH.,(#61));
|
||||||
#63=IFCSTYLEDITEM(#57,(#62),'Color1');
|
#63=IFCSTYLEDITEM(#57,(#62),'Color1');
|
||||||
#64=IFCBUILDINGELEMENTPROXY('1R8AUxDeX02RhatZPFgzSt',#20,'sweep1',$,$,#46,#59,$,$);
|
#64=IFCBUILDINGELEMENTPROXY('07ngBzsO5BK8C3rDUqQfSD',#20,'sweep1',$,$,#46,#59,$,$);
|
||||||
#65=IFCRELCONTAINEDINSPATIALSTRUCTURE('0HNU_Fs7r0SRBiQ9Xqgv$k',#20,'Physical model',$,(#64),#26);
|
#65=IFCRELCONTAINEDINSPATIALSTRUCTURE('1_r$SsBj1C3wgAjnhfmkqs',#20,'Physical model',$,(#64),#26);
|
||||||
ENDSEC;
|
ENDSEC;
|
||||||
END-ISO-10303-21;
|
END-ISO-10303-21;
|
||||||
Vendored
+14
-14
@@ -1,16 +1,16 @@
|
|||||||
ISO-10303-21;
|
ISO-10303-21;
|
||||||
HEADER;
|
HEADER;
|
||||||
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
|
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
|
||||||
FILE_NAME('/dev/null','2025-08-27T11:12:44+02:00',('AdaUser'),('AdaOrg'),'IfcOpenShell 0.8.2','IfcOpenShell 0.8.2','Nobody');
|
FILE_NAME('/dev/null','2025-08-27T15:03:01+02:00',('AdaUser'),('AdaOrg'),'IfcOpenShell 0.8.2','IfcOpenShell 0.8.2','Nobody');
|
||||||
FILE_SCHEMA(('IFC4X3_ADD2'));
|
FILE_SCHEMA(('IFC4X3_ADD2'));
|
||||||
ENDSEC;
|
ENDSEC;
|
||||||
DATA;
|
DATA;
|
||||||
#1=IFCPROJECT('0bfgwqvaX7Vvg12DS5E4TH',$,'AdaProject',$,$,$,$,(#11),#6);
|
#1=IFCPROJECT('1tjHZhewf3AROX1xjpvefY',$,'AdaProject',$,$,$,$,(#11),#6);
|
||||||
#2=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
#2=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||||
#3=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
#3=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||||
#4=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
#4=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||||
#5=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
#5=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||||
#6=IFCUNITASSIGNMENT((#4,#2,#5,#3));
|
#6=IFCUNITASSIGNMENT((#5,#4,#2,#3));
|
||||||
#7=IFCCARTESIANPOINT((0.,0.,0.));
|
#7=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
#8=IFCDIRECTION((0.,0.,1.));
|
#8=IFCDIRECTION((0.,0.,1.));
|
||||||
#9=IFCDIRECTION((1.,0.,0.));
|
#9=IFCDIRECTION((1.,0.,0.));
|
||||||
@@ -24,18 +24,18 @@ DATA;
|
|||||||
#17=IFCORGANIZATION('ADA','Assembly For Design and Analysis',$,$,$);
|
#17=IFCORGANIZATION('ADA','Assembly For Design and Analysis',$,$,$);
|
||||||
#18=IFCPERSONANDORGANIZATION(#16,#17,$);
|
#18=IFCPERSONANDORGANIZATION(#16,#17,$);
|
||||||
#19=IFCAPPLICATION(#17,'XXX','ADA','ADA');
|
#19=IFCAPPLICATION(#17,'XXX','ADA','ADA');
|
||||||
#20=IFCOWNERHISTORY(#18,#19,.READWRITE.,$,$,#18,#19,1756285964);
|
#20=IFCOWNERHISTORY(#18,#19,.READWRITE.,$,$,#18,#19,1756299781);
|
||||||
#21=IFCDIRECTION((0.,0.,1.));
|
#21=IFCDIRECTION((0.,0.,1.));
|
||||||
#22=IFCDIRECTION((1.,0.,0.));
|
#22=IFCDIRECTION((1.,0.,0.));
|
||||||
#23=IFCCARTESIANPOINT((0.,0.,0.));
|
#23=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
#24=IFCAXIS2PLACEMENT3D(#23,#21,#22);
|
#24=IFCAXIS2PLACEMENT3D(#23,#21,#22);
|
||||||
#25=IFCLOCALPLACEMENT($,#24);
|
#25=IFCLOCALPLACEMENT($,#24);
|
||||||
#26=IFCSITE('1GDQO$O1L1QOKEaI81COHL',#20,'Ada',$,$,#25,$,$,.ELEMENT.,$,$,$,$,$);
|
#26=IFCSITE('1$h1D7pKLBhQ2roRvKz9Kq',#20,'Ada',$,$,#25,$,$,.ELEMENT.,$,$,$,$,$);
|
||||||
#27=IFCRELAGGREGATES('34vxVzLRfCB8P7OThopnom',#20,'Project Container',$,#1,(#26));
|
#27=IFCRELAGGREGATES('1cbS0eMMz5482GelhPUH3D',#20,'Project Container',$,#1,(#26));
|
||||||
#28=IFCPROPERTYSINGLEVALUE('project',$,IFCTEXT('AdaProject'),$);
|
#28=IFCPROPERTYSINGLEVALUE('project',$,IFCTEXT('AdaProject'),$);
|
||||||
#29=IFCPROPERTYSINGLEVALUE('schema',$,IFCTEXT('IFC4X3_add2'),$);
|
#29=IFCPROPERTYSINGLEVALUE('schema',$,IFCTEXT('IFC4X3_add2'),$);
|
||||||
#30=IFCPROPERTYSET('2hTgvJbh1B7RR4nwsOfRDy',#20,'Properties',$,(#28,#29));
|
#30=IFCPROPERTYSET('2Jj1ZXLcTFjxFj$8jn9r9Z',#20,'Properties',$,(#28,#29));
|
||||||
#31=IFCRELDEFINESBYPROPERTIES('2wzYfhPqT9hwqI5vOO4yex',#20,'Properties',$,(#26),#30);
|
#31=IFCRELDEFINESBYPROPERTIES('30tu3wf014T9Gbje7aeoNI',#20,'Properties',$,(#26),#30);
|
||||||
#32=IFCMATERIAL('S355',$,'Steel');
|
#32=IFCMATERIAL('S355',$,'Steel');
|
||||||
#33=IFCPROPERTYSINGLEVALUE('Grade',$,IFCTEXT('S355'),$);
|
#33=IFCPROPERTYSINGLEVALUE('Grade',$,IFCTEXT('S355'),$);
|
||||||
#34=IFCPROPERTYSINGLEVALUE('YieldStress',$,IFCPRESSUREMEASURE(355000000.),$);
|
#34=IFCPROPERTYSINGLEVALUE('YieldStress',$,IFCPRESSUREMEASURE(355000000.),$);
|
||||||
@@ -45,16 +45,16 @@ DATA;
|
|||||||
#38=IFCPROPERTYSINGLEVALUE('SpecificHeatCapacity',$,IFCSPECIFICHEATCAPACITYMEASURE(0.03),$);
|
#38=IFCPROPERTYSINGLEVALUE('SpecificHeatCapacity',$,IFCSPECIFICHEATCAPACITYMEASURE(0.03),$);
|
||||||
#39=IFCPROPERTYSINGLEVALUE('MassDensity',$,IFCMASSDENSITYMEASURE(7850.),$);
|
#39=IFCPROPERTYSINGLEVALUE('MassDensity',$,IFCMASSDENSITYMEASURE(7850.),$);
|
||||||
#40=IFCMATERIALPROPERTIES('MaterialMechanical','A Material property description',(#33,#34,#35,#36,#37,#38,#39),#32);
|
#40=IFCMATERIALPROPERTIES('MaterialMechanical','A Material property description',(#33,#34,#35,#36,#37,#38,#39),#32);
|
||||||
#41=IFCRELASSOCIATESMATERIAL('0vr_O6t$T7vPWIa1EqEt6b',#20,'S355','Objects related to S355',(#64),#32);
|
#41=IFCRELASSOCIATESMATERIAL('1kAam6k_5CO9mOkVDyk7FN',#20,'S355','Objects related to S355',(#64),#32);
|
||||||
#42=IFCDIRECTION((0.,0.,1.));
|
#42=IFCDIRECTION((0.,0.,1.));
|
||||||
#43=IFCDIRECTION((1.,0.,0.));
|
#43=IFCDIRECTION((1.,0.,0.));
|
||||||
#44=IFCCARTESIANPOINT((0.,0.,0.));
|
#44=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
#45=IFCAXIS2PLACEMENT3D(#44,#42,#43);
|
#45=IFCAXIS2PLACEMENT3D(#44,#42,#43);
|
||||||
#46=IFCLOCALPLACEMENT(#25,#45);
|
#46=IFCLOCALPLACEMENT(#25,#45);
|
||||||
#47=IFCCARTESIANPOINTLIST2D(((0.,0.),(0.,0.1),(0.1,0.),(0.1,0.1)),$);
|
#47=IFCCARTESIANPOINTLIST2D(((0.,0.),(0.1,0.),(0.1,0.05)),$);
|
||||||
#48=IFCINDEXEDPOLYCURVE(#47,(IFCLINEINDEX((3,1)),IFCLINEINDEX((1,2)),IFCLINEINDEX((2,4)),IFCLINEINDEX((4,3))),$);
|
#48=IFCINDEXEDPOLYCURVE(#47,(IFCLINEINDEX((3,1)),IFCLINEINDEX((1,2)),IFCLINEINDEX((2,3))),$);
|
||||||
#49=IFCARBITRARYCLOSEDPROFILEDEF(.AREA.,$,#48);
|
#49=IFCARBITRARYCLOSEDPROFILEDEF(.AREA.,$,#48);
|
||||||
#50=IFCCARTESIANPOINTLIST3D(((-100.,-50.,199.610854),(-100.,-50.,200.),(-99.974928791787,-49.9996866104928,199.513897412378),(-99.9060013418686,-49.998825019006,199.441251),(-99.3499585838692,-49.9918744730554,199.093724161494),(-99.2406758662487,-49.9607719931374,198.987781372709),(-99.2000000393918,-49.8803717417563,198.861230109006),(-99.2000000393918,-49.3859696738752,198.235404705615),(-99.1121322022443,-49.2544691526923,198.068948349419),(-98.9000000393918,-49.2000000638752,198.000000135615),(-98.3,-49.1999999172672,197.999999950035)),$);
|
#50=IFCCARTESIANPOINTLIST3D(((100.,50.,-200.389146),(100.,50.,-200.),(100.025071208213,50.0003133895072,-200.486102587622),(100.093998658131,50.001174980994,-200.558749),(100.650041416131,50.0081255269446,-200.906275838506),(100.759324133751,50.0392280068626,-201.012218627291),(100.799999960608,50.1196282582437,-201.138769890994),(100.799999960608,50.6140303261248,-201.764595294385),(100.887867797756,50.7455308473077,-201.931051650581),(101.099999960608,50.7999999361248,-201.999999864385),(101.7,50.8000000827328,-202.000000049965)),$);
|
||||||
#51=IFCINDEXEDPOLYCURVE(#50,(IFCLINEINDEX((2,1)),IFCARCINDEX((1,3,4)),IFCLINEINDEX((4,5)),IFCARCINDEX((5,6,7)),IFCLINEINDEX((7,8)),IFCARCINDEX((8,9,10)),IFCLINEINDEX((10,11))),$);
|
#51=IFCINDEXEDPOLYCURVE(#50,(IFCLINEINDEX((2,1)),IFCARCINDEX((1,3,4)),IFCLINEINDEX((4,5)),IFCARCINDEX((5,6,7)),IFCLINEINDEX((7,8)),IFCARCINDEX((8,9,10)),IFCLINEINDEX((10,11))),$);
|
||||||
#52=IFCDIRECTION((1.,-0.,0.));
|
#52=IFCDIRECTION((1.,-0.,0.));
|
||||||
#53=IFCCARTESIANPOINT((0.,0.,0.));
|
#53=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
@@ -68,7 +68,7 @@ DATA;
|
|||||||
#61=IFCSURFACESTYLESHADING(#60,0.);
|
#61=IFCSURFACESTYLESHADING(#60,0.);
|
||||||
#62=IFCSURFACESTYLE('Color1',.BOTH.,(#61));
|
#62=IFCSURFACESTYLE('Color1',.BOTH.,(#61));
|
||||||
#63=IFCSTYLEDITEM(#57,(#62),'Color1');
|
#63=IFCSTYLEDITEM(#57,(#62),'Color1');
|
||||||
#64=IFCBUILDINGELEMENTPROXY('0pDZhwPyT3dRMnPGA47kAn',#20,'sweep1',$,$,#46,#59,$,$);
|
#64=IFCBUILDINGELEMENTPROXY('34gqNt7M58Z972x$etr9V2',#20,'sweep2',$,$,#46,#59,$,$);
|
||||||
#65=IFCRELCONTAINEDINSPATIALSTRUCTURE('196Q7zPfr4LxqeArD0gmJa',#20,'Physical model',$,(#64),#26);
|
#65=IFCRELCONTAINEDINSPATIALSTRUCTURE('1kbNj7znLCygBdHO9SJygX',#20,'Physical model',$,(#64),#26);
|
||||||
ENDSEC;
|
ENDSEC;
|
||||||
END-ISO-10303-21;
|
END-ISO-10303-21;
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
import pathlib
|
||||||
|
from typing import List, Sequence, Tuple
|
||||||
|
|
||||||
|
import ifcopenshell
|
||||||
|
import pytest
|
||||||
|
from OCC.Core.TopoDS import TopoDS_Shape, TopoDS_Compound
|
||||||
|
|
||||||
|
|
||||||
|
def _bbox_from_vertices(verts: List[Tuple[float, float, float]]):
|
||||||
|
if not verts:
|
||||||
|
return (0, 0, 0), (0, 0, 0)
|
||||||
|
xs = [v[0] for v in verts]
|
||||||
|
ys = [v[1] for v in verts]
|
||||||
|
zs = [v[2] for v in verts]
|
||||||
|
mn = (min(xs), min(ys), min(zs))
|
||||||
|
mx = (max(xs), max(ys), max(zs))
|
||||||
|
return mn, mx
|
||||||
|
|
||||||
|
|
||||||
|
def _size_from_bbox(mn, mx):
|
||||||
|
return (mx[0] - mn[0], mx[1] - mn[1], mx[2] - mn[2])
|
||||||
|
|
||||||
|
|
||||||
|
def _triples(flat: Sequence[float]) -> List[Tuple[float, float, float]]:
|
||||||
|
return [(float(flat[i]), float(flat[i + 1]), float(flat[i + 2])) for i in range(0, len(flat), 3)]
|
||||||
|
|
||||||
|
|
||||||
|
def load_ifc_occ_shape(ifc_path: str) -> TopoDS_Shape:
|
||||||
|
try:
|
||||||
|
import ifcopenshell.geom as geom
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError("ifcopenshell.geom not available: cannot validate IFC geometry") from e
|
||||||
|
|
||||||
|
settings = geom.settings()
|
||||||
|
settings.set(settings.USE_WORLD_COORDS, True)
|
||||||
|
settings.set("use-python-opencascade", True)
|
||||||
|
|
||||||
|
# Open the IFC file
|
||||||
|
f = ifcopenshell.open(ifc_path)
|
||||||
|
|
||||||
|
# Find the first representable product (prefer IfcBuildingElementProxy, then any product with representation)
|
||||||
|
products = f.by_type("IfcProduct")
|
||||||
|
target = None
|
||||||
|
for p in products:
|
||||||
|
if p.is_a("IfcBuildingElementProxy") and getattr(p, "Representation", None):
|
||||||
|
target = p
|
||||||
|
break
|
||||||
|
if target is None:
|
||||||
|
for p in products:
|
||||||
|
if getattr(p, "Representation", None):
|
||||||
|
target = p
|
||||||
|
break
|
||||||
|
if target is None:
|
||||||
|
raise RuntimeError("No representable product found in IFC for shape extraction")
|
||||||
|
|
||||||
|
# Create the shape using ifcopenshell.geom with opencascade geometry library
|
||||||
|
shape_result = geom.create_shape(settings, target, geometry_library="opencascade")
|
||||||
|
|
||||||
|
# Extract the OpenCASCADE TopoDS_Shape from the result
|
||||||
|
# The create_shape function returns an object with an occ_shape attribute when using opencascade
|
||||||
|
if hasattr(shape_result, "geometry") and shape_result.geometry:
|
||||||
|
occ_shape = shape_result.geometry
|
||||||
|
if isinstance(occ_shape, TopoDS_Compound):
|
||||||
|
json_data = occ_shape.DumpJson()
|
||||||
|
print(json_data)
|
||||||
|
else:
|
||||||
|
raise NotImplemented(f"Unsupported shape type: {type(occ_shape)}")
|
||||||
|
|
||||||
|
return occ_shape
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Failed to extract OpenCASCADE shape from IFC geometry")
|
||||||
|
|
||||||
|
|
||||||
|
def load_ifc_mesh_bbox(ifc_path: str):
|
||||||
|
"""Load first product's mesh from IFC using ifcopenshell.geom and return bbox and size."""
|
||||||
|
try:
|
||||||
|
import ifcopenshell.geom as geom
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError("ifcopenshell.geom not available: cannot validate IFC geometry") from e
|
||||||
|
|
||||||
|
settings = geom.settings()
|
||||||
|
settings.set(settings.USE_WORLD_COORDS, True)
|
||||||
|
|
||||||
|
f = ifcopenshell.open(ifc_path)
|
||||||
|
# Prefer the proxy we created, otherwise take any product with representation
|
||||||
|
products = f.by_type("IfcProduct")
|
||||||
|
target = None
|
||||||
|
for p in products:
|
||||||
|
if p.is_a("IfcBuildingElementProxy") and getattr(p, "Representation", None):
|
||||||
|
target = p
|
||||||
|
break
|
||||||
|
if target is None:
|
||||||
|
for p in products:
|
||||||
|
if getattr(p, "Representation", None):
|
||||||
|
target = p
|
||||||
|
break
|
||||||
|
if target is None:
|
||||||
|
raise RuntimeError("No representable product found in IFC for validation")
|
||||||
|
|
||||||
|
shape = geom.create_shape(settings, target)
|
||||||
|
verts = _triples(shape.geometry.verts)
|
||||||
|
mn, mx = _bbox_from_vertices(verts)
|
||||||
|
return mn, mx, _size_from_bbox(mn, mx)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def geom_dir():
|
||||||
|
return pathlib.Path(__file__).parent.parent.resolve().absolute() / "fixtures/geom"
|
||||||
|
|
||||||
|
|
||||||
|
def test_simple_sweep_1(geom_dir):
|
||||||
|
ifc_file_path = geom_dir / "simple_sweep_1.ifc"
|
||||||
|
ifc_mn, ifc_mx, ifc_sz = load_ifc_mesh_bbox(ifc_file_path)
|
||||||
|
occ_shape = load_ifc_occ_shape(ifc_file_path)
|
||||||
|
assert occ_shape is not None
|
||||||
|
assert ifc_sz == pytest.approx((0.8957825463853046, 0.1, 1.1))
|
||||||
|
assert ifc_mn == pytest.approx((0.0, 0, -0.1))
|
||||||
|
assert ifc_mx == pytest.approx((0.8957825463853046, 0.1, 1.0))
|
||||||
|
|
||||||
|
|
||||||
|
def test_simple_sweep_2(geom_dir):
|
||||||
|
ifc_file_path = geom_dir / "simple_sweep_2.ifc"
|
||||||
|
ifc_mn, ifc_mx, ifc_sz = load_ifc_mesh_bbox(ifc_file_path)
|
||||||
|
occ_shape = load_ifc_occ_shape(ifc_file_path)
|
||||||
|
assert occ_shape is not None
|
||||||
|
assert ifc_sz == pytest.approx((0.8764374444355525, 1.800000587893038, 2.095849252263605))
|
||||||
|
assert ifc_mn == pytest.approx((50.0, 99.9, 200.0))
|
||||||
|
assert ifc_mx == pytest.approx((50.87643744443555, 101.70000058789304, 202.0958492522636))
|
||||||
|
|
||||||
|
|
||||||
|
def test_pipe_12d(geom_dir):
|
||||||
|
ifc_file_path = geom_dir / "pipe.ifc"
|
||||||
|
ifc_mn, ifc_mx, ifc_sz = load_ifc_mesh_bbox(ifc_file_path)
|
||||||
|
occ_shape = load_ifc_occ_shape(ifc_file_path)
|
||||||
|
assert occ_shape is not None
|
||||||
|
assert ifc_sz == pytest.approx((1.205888147422229, 0.9929900508137735, 0.35776115971654576))
|
||||||
|
assert ifc_mn == pytest.approx((288.9774190979147, 582.0537006391681, 118.70711942014172))
|
||||||
|
assert ifc_mx == pytest.approx((290.18330724533695, 583.0466906899819, 119.06488057985827))
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
import pathlib
|
|
||||||
from typing import List, Sequence, Tuple
|
|
||||||
|
|
||||||
import ifcopenshell
|
|
||||||
import pytest
|
|
||||||
from OCC.Core.TopoDS import TopoDS_Shape, TopoDS_Compound
|
|
||||||
|
|
||||||
|
|
||||||
def _bbox_from_vertices(verts: List[Tuple[float, float, float]]):
|
|
||||||
if not verts:
|
|
||||||
return (0, 0, 0), (0, 0, 0)
|
|
||||||
xs = [v[0] for v in verts]
|
|
||||||
ys = [v[1] for v in verts]
|
|
||||||
zs = [v[2] for v in verts]
|
|
||||||
mn = (min(xs), min(ys), min(zs))
|
|
||||||
mx = (max(xs), max(ys), max(zs))
|
|
||||||
return mn, mx
|
|
||||||
|
|
||||||
|
|
||||||
def _size_from_bbox(mn, mx):
|
|
||||||
return (mx[0] - mn[0], mx[1] - mn[1], mx[2] - mn[2])
|
|
||||||
|
|
||||||
|
|
||||||
def _triples(flat: Sequence[float]) -> List[Tuple[float, float, float]]:
|
|
||||||
return [(float(flat[i]), float(flat[i + 1]), float(flat[i + 2])) for i in range(0, len(flat), 3)]
|
|
||||||
|
|
||||||
|
|
||||||
def _is_swept_shape(occ_shape: TopoDS_Shape) -> bool:
|
|
||||||
"""Analyze if the given OpenCASCADE shape represents a swept shape using topology exploration."""
|
|
||||||
try:
|
|
||||||
from OCC.Core.TopExp import TopExp_Explorer
|
|
||||||
from OCC.Core.TopAbs import TopAbs_FACE, TopAbs_EDGE, TopAbs_WIRE
|
|
||||||
from OCC.Core.BRep_Tool import BRep_Tool
|
|
||||||
from OCC.Core.GeomLProp_SLProps import GeomLProp_SLProps
|
|
||||||
from OCC.Core.BRepAdaptor_Surface import BRepAdaptor_Surface
|
|
||||||
from OCC.Core.GeomAbs import (
|
|
||||||
GeomAbs_Cylinder,
|
|
||||||
GeomAbs_Plane,
|
|
||||||
GeomAbs_SurfaceOfExtrusion,
|
|
||||||
GeomAbs_SurfaceOfRevolution,
|
|
||||||
)
|
|
||||||
from OCC.Core.gp import gp_Vec
|
|
||||||
import math
|
|
||||||
except ImportError as e:
|
|
||||||
raise RuntimeError("pythonocc-core not available for topology analysis") from e
|
|
||||||
|
|
||||||
if occ_shape.IsNull():
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Explore faces to look for swept surface characteristics
|
|
||||||
face_explorer = TopExp_Explorer(occ_shape, TopAbs_FACE)
|
|
||||||
swept_indicators = 0
|
|
||||||
total_faces = 0
|
|
||||||
|
|
||||||
while face_explorer.More():
|
|
||||||
face = face_explorer.Current()
|
|
||||||
total_faces += 1
|
|
||||||
|
|
||||||
# Get the surface adaptor for this face
|
|
||||||
surface_adaptor = BRepAdaptor_Surface(face)
|
|
||||||
surface_type = surface_adaptor.GetType()
|
|
||||||
|
|
||||||
# Check for surface types that indicate swept geometry
|
|
||||||
if surface_type in [GeomAbs_Cylinder, GeomAbs_SurfaceOfExtrusion, GeomAbs_SurfaceOfRevolution]:
|
|
||||||
swept_indicators += 1
|
|
||||||
|
|
||||||
# For planar surfaces, check if they form a pattern consistent with swept geometry
|
|
||||||
elif surface_type == GeomAbs_Plane:
|
|
||||||
# Analyze if planar faces are arranged in a swept pattern
|
|
||||||
# This is a simplified check - could be enhanced further
|
|
||||||
if _has_parallel_opposite_faces(occ_shape, face):
|
|
||||||
swept_indicators += 0.5 # Partial indicator
|
|
||||||
|
|
||||||
face_explorer.Next()
|
|
||||||
|
|
||||||
# Additional check: analyze edge patterns for swept characteristics
|
|
||||||
edge_analysis = _analyze_edge_patterns(occ_shape)
|
|
||||||
|
|
||||||
# Determine if this is likely a swept shape
|
|
||||||
# If more than 50% of faces show swept characteristics, or we have strong edge patterns
|
|
||||||
swept_ratio = swept_indicators / max(total_faces, 1)
|
|
||||||
is_swept = swept_ratio > 0.5 or edge_analysis
|
|
||||||
|
|
||||||
if is_swept:
|
|
||||||
print(f"Swept shape analysis: {swept_indicators}/{total_faces} faces show swept characteristics")
|
|
||||||
if edge_analysis:
|
|
||||||
print("Edge pattern analysis also indicates swept geometry")
|
|
||||||
|
|
||||||
return is_swept
|
|
||||||
|
|
||||||
|
|
||||||
def _has_parallel_opposite_faces(shape: TopoDS_Shape, reference_face) -> bool:
|
|
||||||
"""Check if the shape has faces parallel to the reference face (indicating extrusion)."""
|
|
||||||
try:
|
|
||||||
from OCC.Core.TopExp import TopExp_Explorer
|
|
||||||
from OCC.Core.TopAbs import TopAbs_FACE
|
|
||||||
from OCC.Core.BRepAdaptor_Surface import BRepAdaptor_Surface
|
|
||||||
from OCC.Core.GeomAbs import GeomAbs_Plane
|
|
||||||
from OCC.Core.gp import gp_Vec
|
|
||||||
import math
|
|
||||||
|
|
||||||
ref_adaptor = BRepAdaptor_Surface(reference_face)
|
|
||||||
if ref_adaptor.GetType() != GeomAbs_Plane:
|
|
||||||
return False
|
|
||||||
|
|
||||||
ref_normal = ref_adaptor.Plane().Axis().Direction()
|
|
||||||
|
|
||||||
face_explorer = TopExp_Explorer(shape, TopAbs_FACE)
|
|
||||||
while face_explorer.More():
|
|
||||||
face = face_explorer.Current()
|
|
||||||
if not face.IsSame(reference_face):
|
|
||||||
face_adaptor = BRepAdaptor_Surface(face)
|
|
||||||
if face_adaptor.GetType() == GeomAbs_Plane:
|
|
||||||
face_normal = face_adaptor.Plane().Axis().Direction()
|
|
||||||
# Check if normals are parallel (dot product close to ±1)
|
|
||||||
dot_product = abs(ref_normal.Dot(face_normal))
|
|
||||||
if dot_product > 0.99: # Very close to parallel
|
|
||||||
return True
|
|
||||||
face_explorer.Next()
|
|
||||||
return False
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def load_ifc_occ_shape(ifc_path: str) -> TopoDS_Shape:
|
|
||||||
try:
|
|
||||||
import ifcopenshell.geom as geom
|
|
||||||
except Exception as e:
|
|
||||||
raise RuntimeError("ifcopenshell.geom not available: cannot validate IFC geometry") from e
|
|
||||||
|
|
||||||
settings = geom.settings()
|
|
||||||
settings.set(settings.USE_WORLD_COORDS, True)
|
|
||||||
settings.set("use-python-opencascade", True)
|
|
||||||
|
|
||||||
# Open the IFC file
|
|
||||||
f = ifcopenshell.open(ifc_path)
|
|
||||||
|
|
||||||
# Find the first representable product (prefer IfcBuildingElementProxy, then any product with representation)
|
|
||||||
products = f.by_type("IfcProduct")
|
|
||||||
target = None
|
|
||||||
for p in products:
|
|
||||||
if p.is_a("IfcBuildingElementProxy") and getattr(p, "Representation", None):
|
|
||||||
target = p
|
|
||||||
break
|
|
||||||
if target is None:
|
|
||||||
for p in products:
|
|
||||||
if getattr(p, "Representation", None):
|
|
||||||
target = p
|
|
||||||
break
|
|
||||||
if target is None:
|
|
||||||
raise RuntimeError("No representable product found in IFC for shape extraction")
|
|
||||||
|
|
||||||
# Create the shape using ifcopenshell.geom with opencascade geometry library
|
|
||||||
shape_result = geom.create_shape(settings, target, geometry_library="opencascade")
|
|
||||||
|
|
||||||
# Extract the OpenCASCADE TopoDS_Shape from the result
|
|
||||||
# The create_shape function returns an object with an occ_shape attribute when using opencascade
|
|
||||||
if hasattr(shape_result, "geometry") and shape_result.geometry:
|
|
||||||
occ_shape = shape_result.geometry
|
|
||||||
if isinstance(occ_shape, TopoDS_Compound):
|
|
||||||
json_data = occ_shape.DumpJson()
|
|
||||||
print(json_data)
|
|
||||||
else:
|
|
||||||
raise NotImplemented(f"Unsupported shape type: {type(occ_shape)}")
|
|
||||||
|
|
||||||
return occ_shape
|
|
||||||
else:
|
|
||||||
raise RuntimeError("Failed to extract OpenCASCADE shape from IFC geometry")
|
|
||||||
|
|
||||||
|
|
||||||
def load_ifc_mesh_bbox(ifc_path: str):
|
|
||||||
"""Load first product's mesh from IFC using ifcopenshell.geom and return bbox and size."""
|
|
||||||
try:
|
|
||||||
import ifcopenshell.geom as geom
|
|
||||||
except Exception as e:
|
|
||||||
raise RuntimeError("ifcopenshell.geom not available: cannot validate IFC geometry") from e
|
|
||||||
|
|
||||||
settings = geom.settings()
|
|
||||||
settings.set(settings.USE_WORLD_COORDS, True)
|
|
||||||
|
|
||||||
f = ifcopenshell.open(ifc_path)
|
|
||||||
# Prefer the proxy we created, otherwise take any product with representation
|
|
||||||
products = f.by_type("IfcProduct")
|
|
||||||
target = None
|
|
||||||
for p in products:
|
|
||||||
if p.is_a("IfcBuildingElementProxy") and getattr(p, "Representation", None):
|
|
||||||
target = p
|
|
||||||
break
|
|
||||||
if target is None:
|
|
||||||
for p in products:
|
|
||||||
if getattr(p, "Representation", None):
|
|
||||||
target = p
|
|
||||||
break
|
|
||||||
if target is None:
|
|
||||||
raise RuntimeError("No representable product found in IFC for validation")
|
|
||||||
|
|
||||||
shape = geom.create_shape(settings, target)
|
|
||||||
verts = _triples(shape.geometry.verts)
|
|
||||||
mn, mx = _bbox_from_vertices(verts)
|
|
||||||
return mn, mx, _size_from_bbox(mn, mx)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def test_dir():
|
|
||||||
return pathlib.Path(__file__).parent.resolve().absolute()
|
|
||||||
|
|
||||||
|
|
||||||
def test_simple_sweep_1(test_dir):
|
|
||||||
ifc_file_path = test_dir / "input_temp/simple_sweep_1.ifc"
|
|
||||||
ifc_mn, ifc_mx, ifc_sz = load_ifc_mesh_bbox(ifc_file_path)
|
|
||||||
occ_shape = load_ifc_occ_shape(ifc_file_path)
|
|
||||||
assert occ_shape is not None
|
|
||||||
assert ifc_sz == pytest.approx((1.1, 0.1, 0.89578254))
|
|
||||||
assert ifc_mn == pytest.approx((-1.0, -1.3877787807814457e-17, 0.0))
|
|
||||||
assert ifc_mx == pytest.approx((0.10000000000000002, 0.1, 0.8957825463853046))
|
|
||||||
|
|
||||||
|
|
||||||
def test_simple_sweep_2(test_dir):
|
|
||||||
ifc_file_path = test_dir / "input_temp/simple_sweep_2.ifc"
|
|
||||||
ifc_mn, ifc_mx, ifc_sz = load_ifc_mesh_bbox(ifc_file_path)
|
|
||||||
occ_shape = load_ifc_occ_shape(ifc_file_path)
|
|
||||||
assert occ_shape is not None
|
|
||||||
assert ifc_sz == pytest.approx((1.800000679914902, 0.9243618756667757, 2.0958492522636902))
|
|
||||||
assert ifc_mn == pytest.approx((-100.1, -50.0, 197.9041507477363))
|
|
||||||
assert ifc_mx == pytest.approx((-98.29999932008509, -49.075638124333224, 200.0))
|
|
||||||
|
|
||||||
|
|
||||||
def test_pipe_12d(test_dir):
|
|
||||||
ifc_file_path = test_dir / "input_temp/pipe.ifc"
|
|
||||||
ifc_mn, ifc_mx, ifc_sz = load_ifc_mesh_bbox(ifc_file_path)
|
|
||||||
occ_shape = load_ifc_occ_shape(ifc_file_path)
|
|
||||||
assert occ_shape is not None
|
|
||||||
assert ifc_sz == pytest.approx((1.1068712115520611, 6.2215114729478955, 0.35776115971654576))
|
|
||||||
assert ifc_mn == pytest.approx((289080.64128449163, 5822851.344592881, 118.70711942014172))
|
|
||||||
assert ifc_mx == pytest.approx((289081.7481557032, 5822857.566104354, 119.06488057985827))
|
|
||||||
Reference in New Issue
Block a user