#2150 Fix bug where cartesian point offsets didn't consider object placement rotations

This commit is contained in:
Dion Moult
2022-04-21 09:09:34 +10:00
parent 3aa7f8e4c8
commit 87cb1c69f4
3 changed files with 187 additions and 31 deletions
+51 -30
View File
@@ -428,10 +428,12 @@ class IfcImporter:
props.has_blender_offset = True
def guess_absolute_coordinate(self):
# Civil BIM applications like to work in absolute coordinates, where the ObjectPlacement is 0,0,0 but each
# individual coordinate of the shape representation is in absolute values.
# Civil BIM applications like to work in absolute coordinates, where the
# ObjectPlacement is usually 0,0,0 (but not always, so we'll need to
# check for the actual transformation) but each individual coordinate of
# the shape representation is in absolute values.
offset_point = self.get_offset_point()
if not offset_point:
if offset_point is None:
return
props = bpy.context.scene.BIMGeoreferenceProperties
props.blender_eastings = str(offset_point[0])
@@ -441,34 +443,41 @@ class IfcImporter:
def get_offset_point(self):
elements_checked = 0
# If more than these points aren't far away, the file probably isn't absolutely positioned
element_checking_threshold = 100
if self.file.schema == "IFC2X3":
# IFC2X3 does not have IfcCartesianPointList3D
point_lists = []
else:
point_lists = self.file.by_type("IfcCartesianPointList3D")
for point_list in point_lists:
elements_checked += 1
if elements_checked > element_checking_threshold:
return
for i, point in enumerate(point_list.CoordList):
if len(point) == 3 and self.is_point_far_away(point, is_meters=False):
return point
for point in self.file.by_type("IfcCartesianPoint"):
is_used_in_placement = False
for inverse in self.file.get_inverse(point):
if inverse.is_a("IfcAxis2Placement3D"):
is_used_in_placement = True
break
if is_used_in_placement:
# If more than these elements aren't far away, the file probably isn't absolutely positioned
element_checking_threshold = 10
for element in self.file.by_type("IfcElement"):
if not element.Representation:
continue
elements_checked += 1
if elements_checked > element_checking_threshold:
return
if len(point.Coordinates) == 3 and self.is_point_far_away(point, is_meters=False):
return point.Coordinates
if not self.does_element_likely_have_geometry_far_away(element):
continue
shape = ifcopenshell.geom.create_shape(self.settings, element)
m = shape.transformation.matrix.data
mat = np.array(
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
)
point = np.array(
(
shape.geometry.verts[0] / self.unit_scale,
shape.geometry.verts[1] / self.unit_scale,
shape.geometry.verts[2] / self.unit_scale,
0.0,
)
)
return mat @ point
def does_element_likely_have_geometry_far_away(self, element):
for representation in element.Representation.Representations:
for subelement in self.file.traverse(representation):
if subelement.is_a("IfcCartesianPointList3D"):
for point in subelement.CoordList:
if len(point) == 3 and self.is_point_far_away(point, is_meters=False):
return True
if subelement.is_a("IfcCartesianPoint"):
if len(subelement.Coordinates) == 3 and self.is_point_far_away(subelement, is_meters=False):
return True
def apply_blender_offset_to_matrix_world(self, obj, matrix):
props = bpy.context.scene.BIMGeoreferenceProperties
@@ -1719,15 +1728,27 @@ class IfcImporter:
and geometry.verts
and self.is_point_far_away((geometry.verts[0], geometry.verts[1], geometry.verts[2]))
):
m = shape.transformation.matrix.data
mat = np.array(
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
)
offset_point = np.linalg.inv(mat) @ np.array(
(
float(props.blender_eastings),
float(props.blender_northings),
float(props.blender_orthogonal_height),
0.0,
)
)
verts = [None] * len(geometry.verts)
for i in range(0, len(geometry.verts), 3):
verts[i], verts[i + 1], verts[i + 2] = ifcopenshell.util.geolocation.enh2xyz(
geometry.verts[i],
geometry.verts[i + 1],
geometry.verts[i + 2],
float(props.blender_eastings) * self.unit_scale,
float(props.blender_northings) * self.unit_scale,
float(props.blender_orthogonal_height) * self.unit_scale,
offset_point[0] * self.unit_scale,
offset_point[1] * self.unit_scale,
offset_point[2] * self.unit_scale,
float(props.blender_x_axis_abscissa),
float(props.blender_x_axis_ordinate),
)
@@ -270,15 +270,28 @@ Scenario: Load project elements - manual offset of object placements
Given an empty Blender session
And I press "bim.load_project(filepath='{cwd}/test/files/manual-geolocation.ifc', is_advanced=True)"
When I set "scene.BIMProjectProperties.should_offset_model" to "True"
And I set "scene.BIMProjectProperties.model_offset_coordinates" to "-268388.5, -5774506.0, -21.899999618530273"
And I set "scene.BIMProjectProperties.model_offset_coordinates" to "268388500, 5774506000, 21900"
And I press "bim.load_project_elements"
Then the object "IfcPlate/1780 x 270 PRECAST WALL" is at "0,0,0"
Scenario: Load project elements - manual offset of cartesian points
Given an empty Blender session
And I press "bim.load_project(filepath='{cwd}/test/files/manual-geolocation-coords.ifc', is_advanced=True)"
When I set "scene.BIMProjectProperties.should_offset_model" to "True"
And I set "scene.BIMProjectProperties.model_offset_coordinates" to "1990711,5971553,22700"
And I press "bim.load_project_elements"
Then the object "IfcBuildingElementProxy/NAME" is at "0,0,0"
Scenario: Load project elements - auto offset of object placements
Given an empty Blender session
When I press "bim.load_project(filepath='{cwd}/test/files/auto-geolocation.ifc')"
Then the object "IfcPlate/1780 x 270 PRECAST WALL" is at "0,0,0"
Scenario: Load project elements - auto offset of cartesian points
Given an empty Blender session
When I press "bim.load_project(filepath='{cwd}/test/files/manual-geolocation-coords.ifc')"
Then the object "IfcBuildingElementProxy/NAME" is at "0,0,0"
Scenario: Unload project
Given an empty Blender session
When I press "bim.load_project(filepath='{cwd}/test/files/basic.ifc', is_advanced=True)"
@@ -0,0 +1,122 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION ((''), '2;1');
FILE_NAME ('', '2022-04-06T07:53:12', (''), (''), 'Xbim File Processor version 4.0.0.0', 'Xbim version 4.0.0.0', '');
FILE_SCHEMA (('IFC2X3'));
ENDSEC;
DATA;
#1=IFCBUILDINGELEMENTPROXY('0WxtKqfTD7uRQVvprDAkUF',#13,'NAME','PourObject','PourObject',#14,#26,'ID20ef7534-a5d3-47e1-b69f-e73d4d2ae78f',.ELEMENT.);
#2=IFCOWNERHISTORY(#5,#6,$,.ADDED.,$,$,$,0);
#3=IFCPERSON($,'Davies','Tim',$,$,$,$,$);
#4=IFCORGANIZATION($,'ORG1',$,$,$);
#5=IFCPERSONANDORGANIZATION(#3,#4,$);
#7=IFCORGANIZATION($,'ORG2',$,$,$);
#6=IFCAPPLICATION(#7,'1.1','IfcTool','1234567890');
#8=IFCOWNERHISTORY(#9,#12,$,.NOCHANGE.,$,$,$,1629180000);
#9=IFCPERSONANDORGANIZATION(#10,#11,$);
#10=IFCPERSON('PERSON_NAME','Undefined',$,$,$,$,$,$);
#11=IFCORGANIZATION($,'Trimble Solutions Corporation',$,$,$);
#12=IFCAPPLICATION(#11,'VERSION','APPLICATION','Multi material modeling');
#13=IFCOWNERHISTORY(#5,#6,$,.MODIFIED.,$,$,$,0);
#14=IFCLOCALPLACEMENT(#15,#24);
#15=IFCLOCALPLACEMENT(#16,#23);
#16=IFCLOCALPLACEMENT(#17,#22);
#17=IFCLOCALPLACEMENT($,#18);
#18=IFCAXIS2PLACEMENT3D(#19,#20,#21);
#19=IFCCARTESIANPOINT((0.,0.,0.));
#20=IFCDIRECTION((0.,0.,1.));
#21=IFCDIRECTION((1.,0.,0.));
#22=IFCAXIS2PLACEMENT3D(#19,#20,#21);
#23=IFCAXIS2PLACEMENT3D(#19,#20,#21);
#24=IFCAXIS2PLACEMENT3D(#19,#25,#21);
#25=IFCDIRECTION((0.,1.,0.));
#26=IFCPRODUCTDEFINITIONSHAPE($,$,(#27));
#27=IFCSHAPEREPRESENTATION(#28,'Body','Brep',(#32));
#28=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#29,$,.MODEL_VIEW.,$);
#29=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#30,#31);
#30=IFCAXIS2PLACEMENT3D(#19,#20,#21);
#31=IFCDIRECTION((0.,1.));
#32=IFCFACETEDBREP(#33);
#33=IFCCLOSEDSHELL((#34,#41,#46,#51,#54,#57));
#34=IFCFACE((#35));
#35=IFCFACEOUTERBOUND(#36,.T.);
#36=IFCPOLYLOOP((#37,#38,#39,#40));
#37=IFCCARTESIANPOINT((1990711.,-22699.9999999999,5971553.));
#38=IFCCARTESIANPOINT((1990711.,-24899.9999999999,5971553.));
#39=IFCCARTESIANPOINT((1989711.,-24899.9999999999,5971553.));
#40=IFCCARTESIANPOINT((1989711.,-22699.9999999999,5971553.));
#41=IFCFACE((#42));
#42=IFCFACEOUTERBOUND(#43,.T.);
#43=IFCPOLYLOOP((#38,#44,#45,#39));
#44=IFCCARTESIANPOINT((1990711.,-24899.9999999999,5972053.));
#45=IFCCARTESIANPOINT((1989711.,-24899.9999999999,5972053.));
#46=IFCFACE((#47));
#47=IFCFACEOUTERBOUND(#48,.T.);
#48=IFCPOLYLOOP((#44,#49,#50,#45));
#49=IFCCARTESIANPOINT((1990711.,-22699.9999999999,5972053.));
#50=IFCCARTESIANPOINT((1989711.,-22699.9999999999,5972053.));
#51=IFCFACE((#52));
#52=IFCFACEOUTERBOUND(#53,.T.);
#53=IFCPOLYLOOP((#49,#37,#40,#50));
#54=IFCFACE((#55));
#55=IFCFACEOUTERBOUND(#56,.T.);
#56=IFCPOLYLOOP((#45,#50,#40,#39));
#57=IFCFACE((#58));
#58=IFCFACEOUTERBOUND(#59,.T.);
#59=IFCPOLYLOOP((#49,#44,#38,#37));
#60=IFCSTYLEDITEM(#32,(#61),$);
#61=IFCPRESENTATIONSTYLEASSIGNMENT((#62));
#62=IFCSURFACESTYLE($,.BOTH.,(#63));
#63=IFCSURFACESTYLERENDERING(#64,0.,$,$,$,$,$,$,.NOTDEFINED.);
#64=IFCCOLOURRGB($,0.333333333333333,1.,0.333333333333333);
#65=IFCRELAGGREGATES('3dUdx5Kov6leXMbv_Evj3F',#13,$,$,#72,(#74));
#66=IFCOWNERHISTORY(#67,#70,$,.ADDED.,$,$,$,1648232845);
#67=IFCPERSONANDORGANIZATION(#68,#69,$);
#68=IFCPERSON($,'',$,$,$,$,$,$);
#69=IFCORGANIZATION($,'Simplebim',$,$,$);
#70=IFCAPPLICATION(#71,'Not Defined','Not Defined','Not Defined');
#71=IFCORGANIZATION($,'Datacubist',$,$,$);
#72=IFCBUILDING('1cVfOSwd0DwZRaHgXu8a_f',#13,'Undefined',$,'',#16,$,$,.ELEMENT.,$,$,$);
#73=IFCOWNERHISTORY(#9,#12,$,.NOCHANGE.,$,$,$,1648135363);
#74=IFCBUILDINGSTOREY('1PRG1GisebkPvVjTc0dKhA',#13,'Undefined',$,'',#15,$,$,.ELEMENT.,0.);
#75=IFCRELAGGREGATES('0mzX8SC9TE2x9h5brYnb63',#13,$,$,#76,(#72));
#76=IFCSITE('2AlxJCEVKnYUYG6VRdFCmy',#13,'SITE_NAME',$,'',#17,$,$,.ELEMENT.,(0,0,0),(0,0,0),$,$,$);
#77=IFCRELAGGREGATES('10$gKGL8H5nRoXtsiwnv6O',#13,$,$,#78,(#76));
#78=IFCPROJECT('2tm8iIrI_$r8NRzOY_fHvI',#13,'PROJECT',$,$,$,$,(#29,#79),#81);
#79=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',3,1.E-05,#80,$);
#80=IFCAXIS2PLACEMENT3D(#19,#20,#21);
#81=IFCUNITASSIGNMENT((#82,#83,#84,#85,#86,#87,#88,#89,#90,#91,#92,#93,#94,#98,#101,#106));
#82=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
#83=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
#84=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.);
#85=IFCSIUNIT(*,.MASSUNIT.,.KILO.,.GRAM.);
#86=IFCSIUNIT(*,.POWERUNIT.,$,.WATT.);
#87=IFCSIUNIT(*,.PRESSUREUNIT.,.KILO.,.PASCAL.);
#88=IFCSIUNIT(*,.FORCEUNIT.,.KILO.,.NEWTON.);
#89=IFCSIUNIT(*,.ELECTRICVOLTAGEUNIT.,$,.VOLT.);
#90=IFCSIUNIT(*,.ELECTRICCURRENTUNIT.,$,.AMPERE.);
#91=IFCSIUNIT(*,.THERMODYNAMICTEMPERATUREUNIT.,$,.DEGREE_CELSIUS.);
#92=IFCSIUNIT(*,.TIMEUNIT.,$,.SECOND.);
#93=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
#94=IFCDERIVEDUNIT((#95,#97),.LINEARVELOCITYUNIT.,$);
#95=IFCDERIVEDUNITELEMENT(#96,1);
#96=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#97=IFCDERIVEDUNITELEMENT(#92,-1);
#98=IFCDERIVEDUNIT((#99,#100),.VOLUMETRICFLOWRATEUNIT.,$);
#99=IFCDERIVEDUNITELEMENT(#93,1);
#100=IFCDERIVEDUNITELEMENT(#92,-1);
#101=IFCDERIVEDUNIT((#102,#103,#105),.THERMALTRANSMITTANCEUNIT.,$);
#102=IFCDERIVEDUNITELEMENT(#86,1);
#103=IFCDERIVEDUNITELEMENT(#104,-1);
#104=IFCSIUNIT(*,.THERMODYNAMICTEMPERATUREUNIT.,$,.KELVIN.);
#105=IFCDERIVEDUNITELEMENT(#83,-1);
#106=IFCDERIVEDUNIT((#107),.SOUNDPRESSUREUNIT.,$);
#107=IFCDERIVEDUNITELEMENT(#108,1);
#108=IFCSIUNIT(*,.PRESSUREUNIT.,$,.PASCAL.);
#109=IFCRELCONTAINEDINSPATIALSTRUCTURE('2et5jeJgD1OBJkFAZevcaR',#13,$,$,(#1),#74);
#159=IFCRELDEFINESBYTYPE('0l7quQJkD3hv69HoK6sZxD',#13,$,$,(#1),#160);
#160=IFCBUILDINGELEMENTPROXYTYPE('0wvZ60EKXBihi$_raqzBCm',#13,'PourObject',$,$,$,$,$,$,.NOTDEFINED.);
#161=IFCRELASSOCIATESMATERIAL('1RfoofYsj288$ePu$MZZC_',#13,$,$,(#1),#162);
#162=IFCMATERIAL('Undefined/C40/50');
ENDSEC;
END-ISO-10303-21;