mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 02:23:34 +00:00
#2150 Fix bug where cartesian point offsets didn't consider object placement rotations
This commit is contained in:
@@ -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;
|
||||
Reference in New Issue
Block a user