mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
Merge branch 'v0.7.0' into elevation_tweaks
This commit is contained in:
@@ -1,125 +1,755 @@
|
||||
{
|
||||
"Technical": {
|
||||
"render_type": "VIEWPORT",
|
||||
"raster_style": {
|
||||
"bpy.data.worlds[0].color": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"scene.render.bake_bias": 0.0010000000474974513,
|
||||
"scene.render.bake_margin": 16,
|
||||
"scene.render.bake_margin_type": "ADJACENT_FACES",
|
||||
"scene.render.bake_samples": 256,
|
||||
"scene.render.bake_type": "NORMALS",
|
||||
"scene.render.bake_user_scale": 0.0,
|
||||
"scene.render.border_max_x": 1.0,
|
||||
"scene.render.border_max_y": 1.0,
|
||||
"scene.render.border_min_x": 0.0,
|
||||
"scene.render.border_min_y": 0.0,
|
||||
"scene.render.dither_intensity": 1.0,
|
||||
"scene.render.engine": "BLENDER_WORKBENCH",
|
||||
"scene.render.film_transparent": false,
|
||||
"scene.display.shading.show_object_outline": true,
|
||||
"scene.display.shading.show_cavity": false,
|
||||
"scene.display.shading.cavity_type": "BOTH",
|
||||
"scene.display.shading.curvature_ridge_factor": 1,
|
||||
"scene.display.shading.curvature_valley_factor": 1,
|
||||
"scene.render.filter_size": 1.5,
|
||||
"scene.render.fps": 24,
|
||||
"scene.render.fps_base": 1.0,
|
||||
"scene.render.frame_map_new": 100,
|
||||
"scene.render.frame_map_old": 100,
|
||||
"scene.render.hair_subdiv": 0,
|
||||
"scene.render.hair_type": "STRAND",
|
||||
"scene.render.line_thickness": 1.0,
|
||||
"scene.render.line_thickness_mode": "ABSOLUTE",
|
||||
"scene.render.metadata_input": "SCENE",
|
||||
"scene.render.motion_blur_shutter": 0.5,
|
||||
"scene.render.pixel_aspect_x": 1.0,
|
||||
"scene.render.pixel_aspect_y": 1.0,
|
||||
"scene.render.preview_pixel_size": "AUTO",
|
||||
"scene.render.resolution_percentage": 100,
|
||||
"scene.render.resolution_x": 1000,
|
||||
"scene.render.resolution_y": 1000,
|
||||
"scene.render.sequencer_gl_preview": "SOLID",
|
||||
"scene.render.simplify_child_particles": 1.0,
|
||||
"scene.render.simplify_child_particles_render": 1.0,
|
||||
"scene.render.simplify_gpencil": false,
|
||||
"scene.render.simplify_gpencil_antialiasing": true,
|
||||
"scene.render.simplify_gpencil_modifier": true,
|
||||
"scene.render.simplify_gpencil_onplay": false,
|
||||
"scene.render.simplify_gpencil_shader_fx": true,
|
||||
"scene.render.simplify_gpencil_tint": true,
|
||||
"scene.render.simplify_gpencil_view_fill": true,
|
||||
"scene.render.simplify_shadows": 1.0,
|
||||
"scene.render.simplify_shadows_render": 1.0,
|
||||
"scene.render.simplify_subdivision": 6,
|
||||
"scene.render.simplify_subdivision_render": 6,
|
||||
"scene.render.simplify_volumes": 1.0,
|
||||
"scene.render.stamp_font_size": 12,
|
||||
"scene.render.stamp_note_text": "",
|
||||
"scene.render.threads": 20,
|
||||
"scene.render.threads_mode": "AUTO",
|
||||
"scene.render.use_bake_clear": true,
|
||||
"scene.render.use_bake_lores_mesh": false,
|
||||
"scene.render.use_bake_multires": false,
|
||||
"scene.render.use_bake_selected_to_active": false,
|
||||
"scene.render.use_bake_user_scale": false,
|
||||
"scene.render.use_border": false,
|
||||
"scene.render.use_compositing": true,
|
||||
"scene.render.use_crop_to_border": false,
|
||||
"scene.render.use_file_extension": true,
|
||||
"scene.render.use_freestyle": false,
|
||||
"scene.render.use_high_quality_normals": false,
|
||||
"scene.render.use_lock_interface": false,
|
||||
"scene.render.use_motion_blur": false,
|
||||
"scene.render.use_multiview": false,
|
||||
"scene.render.use_overwrite": true,
|
||||
"scene.render.use_persistent_data": false,
|
||||
"scene.render.use_placeholder": false,
|
||||
"scene.render.use_render_cache": false,
|
||||
"scene.render.use_sequencer": true,
|
||||
"scene.render.use_sequencer_override_scene_strip": false,
|
||||
"scene.render.use_simplify": false,
|
||||
"scene.render.use_single_layer": false,
|
||||
"scene.render.use_stamp": false,
|
||||
"scene.render.use_stamp_camera": true,
|
||||
"scene.render.use_stamp_date": true,
|
||||
"scene.render.use_stamp_filename": true,
|
||||
"scene.render.use_stamp_frame": true,
|
||||
"scene.render.use_stamp_frame_range": false,
|
||||
"scene.render.use_stamp_hostname": false,
|
||||
"scene.render.use_stamp_labels": true,
|
||||
"scene.render.use_stamp_lens": false,
|
||||
"scene.render.use_stamp_marker": false,
|
||||
"scene.render.use_stamp_memory": false,
|
||||
"scene.render.use_stamp_note": false,
|
||||
"scene.render.use_stamp_render_time": true,
|
||||
"scene.render.use_stamp_scene": true,
|
||||
"scene.render.use_stamp_sequencer_strip": false,
|
||||
"scene.render.use_stamp_time": true,
|
||||
"scene.render.views_format": "STEREO_3D",
|
||||
"scene.view_settings.exposure": 0.0,
|
||||
"scene.view_settings.gamma": 1.0,
|
||||
"scene.view_settings.look": "None",
|
||||
"scene.view_settings.use_curve_mapping": false,
|
||||
"scene.view_settings.view_transform": "Standard",
|
||||
"scene.display.shading.light": "FLAT",
|
||||
"scene.display.shading.color_type": "SINGLE",
|
||||
"scene.display.shading.single_color": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
"scene.display.shading.aov_name": "",
|
||||
"scene.display.shading.background_color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"scene.display.shading.show_shadows": false,
|
||||
"scene.display.shading.background_type": "THEME",
|
||||
"scene.display.shading.cavity_ridge_factor": 1.0,
|
||||
"scene.display.shading.cavity_type": "BOTH",
|
||||
"scene.display.shading.cavity_valley_factor": 1.0,
|
||||
"scene.display.shading.color_type": "SINGLE",
|
||||
"scene.display.shading.curvature_ridge_factor": 1.0,
|
||||
"scene.display.shading.curvature_valley_factor": 1.0,
|
||||
"scene.display.shading.light": "FLAT",
|
||||
"scene.display.shading.object_outline_color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"scene.display.shading.render_pass": "COMBINED",
|
||||
"scene.display.shading.shadow_intensity": 0.5,
|
||||
"scene.display.shading.show_backface_culling": false,
|
||||
"scene.display.shading.show_cavity": false,
|
||||
"scene.display.shading.show_object_outline": true,
|
||||
"scene.display.shading.show_shadows": false,
|
||||
"scene.display.shading.show_specular_highlight": true,
|
||||
"scene.display.shading.show_xray": false,
|
||||
"scene.display.shading.show_xray_wireframe": false,
|
||||
"scene.display.shading.single_color": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"scene.display.shading.studio_light": "Default",
|
||||
"scene.display.shading.studiolight_background_alpha": 0.0,
|
||||
"scene.display.shading.studiolight_background_blur": 0.0,
|
||||
"scene.display.shading.studiolight_intensity": 0.0,
|
||||
"scene.display.shading.studiolight_rotate_z": 0.0,
|
||||
"scene.display.shading.type": "SOLID",
|
||||
"scene.display.shading.use_compositor": "DISABLED",
|
||||
"scene.display.shading.use_dof": false,
|
||||
"scene.display.shading.use_scene_lights": false,
|
||||
"scene.display.shading.use_scene_lights_render": false,
|
||||
"scene.display.shading.use_scene_world": false,
|
||||
"scene.display.shading.use_scene_world_render": false,
|
||||
"scene.display.shading.use_studiolight_view_rotation": true,
|
||||
"scene.display.shading.use_world_space_lighting": false,
|
||||
"scene.display.shading.wireframe_color_type": "MATERIAL",
|
||||
"scene.display.shading.xray_alpha": 0.5,
|
||||
"scene.display.shading.xray_alpha_wireframe": 0.0,
|
||||
"scene.display.light_direction": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"scene.view_settings.use_curve_mapping": false,
|
||||
"space.overlay.show_wireframes": true,
|
||||
"space.overlay.wireframe_threshold": 0,
|
||||
"space.overlay.show_floor": false,
|
||||
"scene.display.matcap_ssao_attenuation": 1.0,
|
||||
"scene.display.matcap_ssao_distance": 0.20000000298023224,
|
||||
"scene.display.matcap_ssao_samples": 16,
|
||||
"scene.display.render_aa": "8",
|
||||
"scene.display.shadow_focus": 0.0,
|
||||
"scene.display.shadow_shift": 0.10000000149011612,
|
||||
"scene.display.viewport_aa": "FXAA",
|
||||
"space.overlay.backwire_opacity": 0.5,
|
||||
"space.overlay.bone_wire_alpha": 1.0,
|
||||
"space.overlay.display_handle": "SELECTED",
|
||||
"space.overlay.fade_inactive_alpha": 0.4000000059604645,
|
||||
"space.overlay.gpencil_fade_layer": 0.5,
|
||||
"space.overlay.gpencil_fade_objects": 0.5,
|
||||
"space.overlay.gpencil_grid_opacity": 0.5,
|
||||
"space.overlay.gpencil_vertex_paint_opacity": 1.0,
|
||||
"space.overlay.grid_lines": 16,
|
||||
"space.overlay.grid_scale": 1.0,
|
||||
"space.overlay.grid_subdivisions": 10,
|
||||
"space.overlay.normals_constant_screen_size": 7.0,
|
||||
"space.overlay.normals_length": 0.10000000149011612,
|
||||
"space.overlay.sculpt_curves_cage_opacity": 0.5,
|
||||
"space.overlay.sculpt_mode_face_sets_opacity": 1.0,
|
||||
"space.overlay.sculpt_mode_mask_opacity": 0.75,
|
||||
"space.overlay.show_annotation": true,
|
||||
"space.overlay.show_axis_x": false,
|
||||
"space.overlay.show_axis_y": false,
|
||||
"space.overlay.show_axis_z": false,
|
||||
"space.overlay.show_bones": true,
|
||||
"space.overlay.show_cursor": true,
|
||||
"space.overlay.show_curve_normals": false,
|
||||
"space.overlay.show_edge_bevel_weight": true,
|
||||
"space.overlay.show_edge_crease": true,
|
||||
"space.overlay.show_edge_seams": true,
|
||||
"space.overlay.show_edge_sharp": true,
|
||||
"space.overlay.show_edges": false,
|
||||
"space.overlay.show_extra_edge_angle": false,
|
||||
"space.overlay.show_extra_edge_length": false,
|
||||
"space.overlay.show_extra_face_angle": false,
|
||||
"space.overlay.show_extra_face_area": false,
|
||||
"space.overlay.show_extra_indices": false,
|
||||
"space.overlay.show_extras": true,
|
||||
"space.overlay.show_face_center": false,
|
||||
"space.overlay.show_face_normals": false,
|
||||
"space.overlay.show_face_orientation": false,
|
||||
"space.overlay.show_faces": true,
|
||||
"space.overlay.show_fade_inactive": false,
|
||||
"space.overlay.show_floor": false,
|
||||
"space.overlay.show_freestyle_edge_marks": true,
|
||||
"space.overlay.show_freestyle_face_marks": true,
|
||||
"space.overlay.show_look_dev": false,
|
||||
"space.overlay.show_motion_paths": true,
|
||||
"space.overlay.show_object_origins": false,
|
||||
"space.overlay.show_relationship_lines": false
|
||||
},
|
||||
"render_type": "VIEWPORT"
|
||||
"space.overlay.show_object_origins_all": false,
|
||||
"space.overlay.show_occlude_wire": false,
|
||||
"space.overlay.show_onion_skins": false,
|
||||
"space.overlay.show_ortho_grid": true,
|
||||
"space.overlay.show_outline_selected": true,
|
||||
"space.overlay.show_overlays": true,
|
||||
"space.overlay.show_paint_wire": false,
|
||||
"space.overlay.show_relationship_lines": false,
|
||||
"space.overlay.show_sculpt_curves_cage": true,
|
||||
"space.overlay.show_sculpt_face_sets": true,
|
||||
"space.overlay.show_sculpt_mask": true,
|
||||
"space.overlay.show_split_normals": false,
|
||||
"space.overlay.show_stats": false,
|
||||
"space.overlay.show_statvis": false,
|
||||
"space.overlay.show_text": true,
|
||||
"space.overlay.show_vertex_normals": false,
|
||||
"space.overlay.show_viewer_attribute": true,
|
||||
"space.overlay.show_weight": false,
|
||||
"space.overlay.show_wireframes": true,
|
||||
"space.overlay.show_wpaint_contours": false,
|
||||
"space.overlay.show_xray_bone": false,
|
||||
"space.overlay.texture_paint_mode_opacity": 1.0,
|
||||
"space.overlay.use_debug_freeze_view_culling": false,
|
||||
"space.overlay.use_gpencil_canvas_xray": false,
|
||||
"space.overlay.use_gpencil_edit_lines": true,
|
||||
"space.overlay.use_gpencil_fade_gp_objects": false,
|
||||
"space.overlay.use_gpencil_fade_layers": false,
|
||||
"space.overlay.use_gpencil_fade_objects": false,
|
||||
"space.overlay.use_gpencil_grid": false,
|
||||
"space.overlay.use_gpencil_multiedit_line_only": false,
|
||||
"space.overlay.use_gpencil_onion_skin": false,
|
||||
"space.overlay.use_gpencil_show_directions": false,
|
||||
"space.overlay.use_gpencil_show_material_name": false,
|
||||
"space.overlay.use_normals_constant_screen_size": false,
|
||||
"space.overlay.vertex_opacity": 1.0,
|
||||
"space.overlay.vertex_paint_mode_opacity": 1.0,
|
||||
"space.overlay.viewer_attribute_opacity": 1.0,
|
||||
"space.overlay.weight_paint_mode_opacity": 1.0,
|
||||
"space.overlay.wireframe_opacity": 1.0,
|
||||
"space.overlay.wireframe_threshold": 0.0,
|
||||
"space.overlay.xray_alpha_bone": 0.0
|
||||
}
|
||||
},
|
||||
"Shaded": {
|
||||
"render_type": "VIEWPORT",
|
||||
"raster_style": {
|
||||
"bpy.data.worlds[0].color": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"scene.render.bake_bias": 0.0010000000474974513,
|
||||
"scene.render.bake_margin": 16,
|
||||
"scene.render.bake_margin_type": "ADJACENT_FACES",
|
||||
"scene.render.bake_samples": 256,
|
||||
"scene.render.bake_type": "NORMALS",
|
||||
"scene.render.bake_user_scale": 0.0,
|
||||
"scene.render.border_max_x": 1.0,
|
||||
"scene.render.border_max_y": 1.0,
|
||||
"scene.render.border_min_x": 0.0,
|
||||
"scene.render.border_min_y": 0.0,
|
||||
"scene.render.dither_intensity": 1.0,
|
||||
"scene.render.engine": "BLENDER_WORKBENCH",
|
||||
"scene.render.film_transparent": false,
|
||||
"scene.display.shading.show_object_outline": true,
|
||||
"scene.display.shading.show_cavity": true,
|
||||
"scene.display.shading.cavity_type": "BOTH",
|
||||
"scene.display.shading.curvature_ridge_factor": 1,
|
||||
"scene.display.shading.curvature_valley_factor": 1,
|
||||
"scene.render.filter_size": 1.5,
|
||||
"scene.render.fps": 24,
|
||||
"scene.render.fps_base": 1.0,
|
||||
"scene.render.frame_map_new": 100,
|
||||
"scene.render.frame_map_old": 100,
|
||||
"scene.render.hair_subdiv": 0,
|
||||
"scene.render.hair_type": "STRAND",
|
||||
"scene.render.line_thickness": 1.0,
|
||||
"scene.render.line_thickness_mode": "ABSOLUTE",
|
||||
"scene.render.metadata_input": "SCENE",
|
||||
"scene.render.motion_blur_shutter": 0.5,
|
||||
"scene.render.pixel_aspect_x": 1.0,
|
||||
"scene.render.pixel_aspect_y": 1.0,
|
||||
"scene.render.preview_pixel_size": "AUTO",
|
||||
"scene.render.resolution_percentage": 100,
|
||||
"scene.render.resolution_x": 1000,
|
||||
"scene.render.resolution_y": 1000,
|
||||
"scene.render.sequencer_gl_preview": "SOLID",
|
||||
"scene.render.simplify_child_particles": 1.0,
|
||||
"scene.render.simplify_child_particles_render": 1.0,
|
||||
"scene.render.simplify_gpencil": false,
|
||||
"scene.render.simplify_gpencil_antialiasing": true,
|
||||
"scene.render.simplify_gpencil_modifier": true,
|
||||
"scene.render.simplify_gpencil_onplay": false,
|
||||
"scene.render.simplify_gpencil_shader_fx": true,
|
||||
"scene.render.simplify_gpencil_tint": true,
|
||||
"scene.render.simplify_gpencil_view_fill": true,
|
||||
"scene.render.simplify_shadows": 1.0,
|
||||
"scene.render.simplify_shadows_render": 1.0,
|
||||
"scene.render.simplify_subdivision": 6,
|
||||
"scene.render.simplify_subdivision_render": 6,
|
||||
"scene.render.simplify_volumes": 1.0,
|
||||
"scene.render.stamp_font_size": 12,
|
||||
"scene.render.stamp_note_text": "",
|
||||
"scene.render.threads": 20,
|
||||
"scene.render.threads_mode": "AUTO",
|
||||
"scene.render.use_bake_clear": true,
|
||||
"scene.render.use_bake_lores_mesh": false,
|
||||
"scene.render.use_bake_multires": false,
|
||||
"scene.render.use_bake_selected_to_active": false,
|
||||
"scene.render.use_bake_user_scale": false,
|
||||
"scene.render.use_border": false,
|
||||
"scene.render.use_compositing": true,
|
||||
"scene.render.use_crop_to_border": false,
|
||||
"scene.render.use_file_extension": true,
|
||||
"scene.render.use_freestyle": false,
|
||||
"scene.render.use_high_quality_normals": false,
|
||||
"scene.render.use_lock_interface": false,
|
||||
"scene.render.use_motion_blur": false,
|
||||
"scene.render.use_multiview": false,
|
||||
"scene.render.use_overwrite": true,
|
||||
"scene.render.use_persistent_data": false,
|
||||
"scene.render.use_placeholder": false,
|
||||
"scene.render.use_render_cache": false,
|
||||
"scene.render.use_sequencer": true,
|
||||
"scene.render.use_sequencer_override_scene_strip": false,
|
||||
"scene.render.use_simplify": false,
|
||||
"scene.render.use_single_layer": false,
|
||||
"scene.render.use_stamp": false,
|
||||
"scene.render.use_stamp_camera": true,
|
||||
"scene.render.use_stamp_date": true,
|
||||
"scene.render.use_stamp_filename": true,
|
||||
"scene.render.use_stamp_frame": true,
|
||||
"scene.render.use_stamp_frame_range": false,
|
||||
"scene.render.use_stamp_hostname": false,
|
||||
"scene.render.use_stamp_labels": true,
|
||||
"scene.render.use_stamp_lens": false,
|
||||
"scene.render.use_stamp_marker": false,
|
||||
"scene.render.use_stamp_memory": false,
|
||||
"scene.render.use_stamp_note": false,
|
||||
"scene.render.use_stamp_render_time": true,
|
||||
"scene.render.use_stamp_scene": true,
|
||||
"scene.render.use_stamp_sequencer_strip": false,
|
||||
"scene.render.use_stamp_time": true,
|
||||
"scene.render.views_format": "STEREO_3D",
|
||||
"scene.view_settings.exposure": 0.0,
|
||||
"scene.view_settings.gamma": 1.0,
|
||||
"scene.view_settings.look": "None",
|
||||
"scene.view_settings.use_curve_mapping": false,
|
||||
"scene.view_settings.view_transform": "Standard",
|
||||
"scene.display.shading.light": "STUDIO",
|
||||
"scene.display.shading.color_type": "MATERIAL",
|
||||
"scene.display.shading.single_color": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
"scene.display.shading.aov_name": "",
|
||||
"scene.display.shading.background_color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"scene.display.shading.show_shadows": true,
|
||||
"scene.display.shading.background_type": "THEME",
|
||||
"scene.display.shading.cavity_ridge_factor": 1.0,
|
||||
"scene.display.shading.cavity_type": "BOTH",
|
||||
"scene.display.shading.cavity_valley_factor": 1.0,
|
||||
"scene.display.shading.color_type": "MATERIAL",
|
||||
"scene.display.shading.curvature_ridge_factor": 1.0,
|
||||
"scene.display.shading.curvature_valley_factor": 1.0,
|
||||
"scene.display.shading.light": "STUDIO",
|
||||
"scene.display.shading.object_outline_color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"scene.display.shading.render_pass": "COMBINED",
|
||||
"scene.display.shading.shadow_intensity": 0.5,
|
||||
"scene.display.shading.show_backface_culling": false,
|
||||
"scene.display.shading.show_cavity": true,
|
||||
"scene.display.shading.show_object_outline": true,
|
||||
"scene.display.shading.show_shadows": true,
|
||||
"scene.display.shading.show_specular_highlight": true,
|
||||
"scene.display.shading.show_xray": false,
|
||||
"scene.display.shading.show_xray_wireframe": false,
|
||||
"scene.display.shading.single_color": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"scene.display.shading.studio_light": "Default",
|
||||
"scene.display.shading.studiolight_background_alpha": 0.0,
|
||||
"scene.display.shading.studiolight_background_blur": 0.0,
|
||||
"scene.display.shading.studiolight_intensity": 0.0,
|
||||
"scene.display.shading.studiolight_rotate_z": 0.0,
|
||||
"scene.display.shading.type": "SOLID",
|
||||
"scene.display.shading.use_compositor": "DISABLED",
|
||||
"scene.display.shading.use_dof": false,
|
||||
"scene.display.shading.use_scene_lights": false,
|
||||
"scene.display.shading.use_scene_lights_render": false,
|
||||
"scene.display.shading.use_scene_world": false,
|
||||
"scene.display.shading.use_scene_world_render": false,
|
||||
"scene.display.shading.use_studiolight_view_rotation": true,
|
||||
"scene.display.shading.use_world_space_lighting": false,
|
||||
"scene.display.shading.wireframe_color_type": "MATERIAL",
|
||||
"scene.display.shading.xray_alpha": 0.5,
|
||||
"scene.display.shading.xray_alpha_wireframe": 0.0,
|
||||
"scene.display.light_direction": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"scene.view_settings.use_curve_mapping": false,
|
||||
"space.overlay.show_wireframes": false,
|
||||
"space.overlay.wireframe_threshold": 0,
|
||||
"space.overlay.show_floor": false,
|
||||
"scene.display.matcap_ssao_attenuation": 1.0,
|
||||
"scene.display.matcap_ssao_distance": 0.20000000298023224,
|
||||
"scene.display.matcap_ssao_samples": 16,
|
||||
"scene.display.render_aa": "8",
|
||||
"scene.display.shadow_focus": 0.0,
|
||||
"scene.display.shadow_shift": 0.10000000149011612,
|
||||
"scene.display.viewport_aa": "FXAA",
|
||||
"space.overlay.backwire_opacity": 0.5,
|
||||
"space.overlay.bone_wire_alpha": 1.0,
|
||||
"space.overlay.display_handle": "SELECTED",
|
||||
"space.overlay.fade_inactive_alpha": 0.4000000059604645,
|
||||
"space.overlay.gpencil_fade_layer": 0.5,
|
||||
"space.overlay.gpencil_fade_objects": 0.5,
|
||||
"space.overlay.gpencil_grid_opacity": 0.5,
|
||||
"space.overlay.gpencil_vertex_paint_opacity": 1.0,
|
||||
"space.overlay.grid_lines": 16,
|
||||
"space.overlay.grid_scale": 1.0,
|
||||
"space.overlay.grid_subdivisions": 10,
|
||||
"space.overlay.normals_constant_screen_size": 7.0,
|
||||
"space.overlay.normals_length": 0.10000000149011612,
|
||||
"space.overlay.sculpt_curves_cage_opacity": 0.5,
|
||||
"space.overlay.sculpt_mode_face_sets_opacity": 1.0,
|
||||
"space.overlay.sculpt_mode_mask_opacity": 0.75,
|
||||
"space.overlay.show_annotation": true,
|
||||
"space.overlay.show_axis_x": false,
|
||||
"space.overlay.show_axis_y": false,
|
||||
"space.overlay.show_axis_z": false,
|
||||
"space.overlay.show_bones": true,
|
||||
"space.overlay.show_cursor": true,
|
||||
"space.overlay.show_curve_normals": false,
|
||||
"space.overlay.show_edge_bevel_weight": true,
|
||||
"space.overlay.show_edge_crease": true,
|
||||
"space.overlay.show_edge_seams": true,
|
||||
"space.overlay.show_edge_sharp": true,
|
||||
"space.overlay.show_edges": false,
|
||||
"space.overlay.show_extra_edge_angle": false,
|
||||
"space.overlay.show_extra_edge_length": false,
|
||||
"space.overlay.show_extra_face_angle": false,
|
||||
"space.overlay.show_extra_face_area": false,
|
||||
"space.overlay.show_extra_indices": false,
|
||||
"space.overlay.show_extras": true,
|
||||
"space.overlay.show_face_center": false,
|
||||
"space.overlay.show_face_normals": false,
|
||||
"space.overlay.show_face_orientation": false,
|
||||
"space.overlay.show_faces": true,
|
||||
"space.overlay.show_fade_inactive": false,
|
||||
"space.overlay.show_floor": false,
|
||||
"space.overlay.show_freestyle_edge_marks": true,
|
||||
"space.overlay.show_freestyle_face_marks": true,
|
||||
"space.overlay.show_look_dev": false,
|
||||
"space.overlay.show_motion_paths": true,
|
||||
"space.overlay.show_object_origins": false,
|
||||
"space.overlay.show_relationship_lines": false
|
||||
},
|
||||
"render_type": "VIEWPORT"
|
||||
"space.overlay.show_object_origins_all": false,
|
||||
"space.overlay.show_occlude_wire": false,
|
||||
"space.overlay.show_onion_skins": false,
|
||||
"space.overlay.show_ortho_grid": true,
|
||||
"space.overlay.show_outline_selected": true,
|
||||
"space.overlay.show_overlays": true,
|
||||
"space.overlay.show_paint_wire": false,
|
||||
"space.overlay.show_relationship_lines": false,
|
||||
"space.overlay.show_sculpt_curves_cage": true,
|
||||
"space.overlay.show_sculpt_face_sets": true,
|
||||
"space.overlay.show_sculpt_mask": true,
|
||||
"space.overlay.show_split_normals": false,
|
||||
"space.overlay.show_stats": false,
|
||||
"space.overlay.show_statvis": false,
|
||||
"space.overlay.show_text": true,
|
||||
"space.overlay.show_vertex_normals": false,
|
||||
"space.overlay.show_viewer_attribute": true,
|
||||
"space.overlay.show_weight": false,
|
||||
"space.overlay.show_wireframes": false,
|
||||
"space.overlay.show_wpaint_contours": false,
|
||||
"space.overlay.show_xray_bone": false,
|
||||
"space.overlay.texture_paint_mode_opacity": 1.0,
|
||||
"space.overlay.use_debug_freeze_view_culling": false,
|
||||
"space.overlay.use_gpencil_canvas_xray": false,
|
||||
"space.overlay.use_gpencil_edit_lines": true,
|
||||
"space.overlay.use_gpencil_fade_gp_objects": false,
|
||||
"space.overlay.use_gpencil_fade_layers": false,
|
||||
"space.overlay.use_gpencil_fade_objects": false,
|
||||
"space.overlay.use_gpencil_grid": false,
|
||||
"space.overlay.use_gpencil_multiedit_line_only": false,
|
||||
"space.overlay.use_gpencil_onion_skin": false,
|
||||
"space.overlay.use_gpencil_show_directions": false,
|
||||
"space.overlay.use_gpencil_show_material_name": false,
|
||||
"space.overlay.use_normals_constant_screen_size": false,
|
||||
"space.overlay.vertex_opacity": 1.0,
|
||||
"space.overlay.vertex_paint_mode_opacity": 1.0,
|
||||
"space.overlay.viewer_attribute_opacity": 1.0,
|
||||
"space.overlay.weight_paint_mode_opacity": 1.0,
|
||||
"space.overlay.wireframe_opacity": 1.0,
|
||||
"space.overlay.wireframe_threshold": 0.0,
|
||||
"space.overlay.xray_alpha_bone": 0.0
|
||||
}
|
||||
},
|
||||
"Blender Default": {
|
||||
"render_type": "VIEWPORT",
|
||||
"raster_style": {
|
||||
"bpy.data.worlds[0].color": [
|
||||
0.05087608844041824,
|
||||
0.05087608844041824,
|
||||
0.05087608844041824
|
||||
],
|
||||
"scene.render.bake_bias": 0.0010000000474974513,
|
||||
"scene.render.bake_margin": 16,
|
||||
"scene.render.bake_margin_type": "ADJACENT_FACES",
|
||||
"scene.render.bake_samples": 256,
|
||||
"scene.render.bake_type": "NORMALS",
|
||||
"scene.render.bake_user_scale": 0.0,
|
||||
"scene.render.border_max_x": 1.0,
|
||||
"scene.render.border_max_y": 1.0,
|
||||
"scene.render.border_min_x": 0.0,
|
||||
"scene.render.border_min_y": 0.0,
|
||||
"scene.render.dither_intensity": 1.0,
|
||||
"scene.render.engine": "BLENDER_EEVEE",
|
||||
"scene.render.film_transparent": false,
|
||||
"scene.render.filter_size": 1.5,
|
||||
"scene.render.fps": 24,
|
||||
"scene.render.fps_base": 1.0,
|
||||
"scene.render.frame_map_new": 100,
|
||||
"scene.render.frame_map_old": 100,
|
||||
"scene.render.hair_subdiv": 0,
|
||||
"scene.render.hair_type": "STRAND",
|
||||
"scene.render.line_thickness": 1.0,
|
||||
"scene.render.line_thickness_mode": "ABSOLUTE",
|
||||
"scene.render.metadata_input": "SCENE",
|
||||
"scene.render.motion_blur_shutter": 0.5,
|
||||
"scene.render.pixel_aspect_x": 1.0,
|
||||
"scene.render.pixel_aspect_y": 1.0,
|
||||
"scene.render.preview_pixel_size": "AUTO",
|
||||
"scene.render.resolution_percentage": 100,
|
||||
"scene.render.resolution_x": 1000,
|
||||
"scene.render.resolution_y": 1000,
|
||||
"scene.render.sequencer_gl_preview": "SOLID",
|
||||
"scene.render.simplify_child_particles": 1.0,
|
||||
"scene.render.simplify_child_particles_render": 1.0,
|
||||
"scene.render.simplify_gpencil": false,
|
||||
"scene.render.simplify_gpencil_antialiasing": true,
|
||||
"scene.render.simplify_gpencil_modifier": true,
|
||||
"scene.render.simplify_gpencil_onplay": false,
|
||||
"scene.render.simplify_gpencil_shader_fx": true,
|
||||
"scene.render.simplify_gpencil_tint": true,
|
||||
"scene.render.simplify_gpencil_view_fill": true,
|
||||
"scene.render.simplify_shadows": 1.0,
|
||||
"scene.render.simplify_shadows_render": 1.0,
|
||||
"scene.render.simplify_subdivision": 6,
|
||||
"scene.render.simplify_subdivision_render": 6,
|
||||
"scene.render.simplify_volumes": 1.0,
|
||||
"scene.render.stamp_font_size": 12,
|
||||
"scene.render.stamp_note_text": "",
|
||||
"scene.render.threads": 20,
|
||||
"scene.render.threads_mode": "AUTO",
|
||||
"scene.render.use_bake_clear": true,
|
||||
"scene.render.use_bake_lores_mesh": false,
|
||||
"scene.render.use_bake_multires": false,
|
||||
"scene.render.use_bake_selected_to_active": false,
|
||||
"scene.render.use_bake_user_scale": false,
|
||||
"scene.render.use_border": false,
|
||||
"scene.render.use_compositing": true,
|
||||
"scene.render.use_crop_to_border": false,
|
||||
"scene.render.use_file_extension": true,
|
||||
"scene.render.use_freestyle": false,
|
||||
"scene.render.use_high_quality_normals": false,
|
||||
"scene.render.use_lock_interface": false,
|
||||
"scene.render.use_motion_blur": false,
|
||||
"scene.render.use_multiview": false,
|
||||
"scene.render.use_overwrite": true,
|
||||
"scene.render.use_persistent_data": false,
|
||||
"scene.render.use_placeholder": false,
|
||||
"scene.render.use_render_cache": false,
|
||||
"scene.render.use_sequencer": true,
|
||||
"scene.render.use_sequencer_override_scene_strip": false,
|
||||
"scene.render.use_simplify": false,
|
||||
"scene.render.use_single_layer": false,
|
||||
"scene.render.use_stamp": false,
|
||||
"scene.render.use_stamp_camera": true,
|
||||
"scene.render.use_stamp_date": true,
|
||||
"scene.render.use_stamp_filename": true,
|
||||
"scene.render.use_stamp_frame": true,
|
||||
"scene.render.use_stamp_frame_range": false,
|
||||
"scene.render.use_stamp_hostname": false,
|
||||
"scene.render.use_stamp_labels": true,
|
||||
"scene.render.use_stamp_lens": false,
|
||||
"scene.render.use_stamp_marker": false,
|
||||
"scene.render.use_stamp_memory": false,
|
||||
"scene.render.use_stamp_note": false,
|
||||
"scene.render.use_stamp_render_time": true,
|
||||
"scene.render.use_stamp_scene": true,
|
||||
"scene.render.use_stamp_sequencer_strip": false,
|
||||
"scene.render.use_stamp_time": true,
|
||||
"scene.render.views_format": "STEREO_3D",
|
||||
"scene.view_settings.exposure": 0.0,
|
||||
"scene.view_settings.gamma": 1.0,
|
||||
"scene.view_settings.look": "None",
|
||||
"scene.view_settings.use_curve_mapping": false,
|
||||
"scene.view_settings.view_transform": "Filmic",
|
||||
"scene.display.shading.show_object_outline": false,
|
||||
"scene.display.shading.show_cavity": false,
|
||||
"scene.display.shading.aov_name": "",
|
||||
"scene.display.shading.background_color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"scene.display.shading.background_type": "THEME",
|
||||
"scene.display.shading.cavity_ridge_factor": 1.0,
|
||||
"scene.display.shading.cavity_type": "WORLD",
|
||||
"scene.display.shading.cavity_valley_factor": 1.0,
|
||||
"scene.display.shading.color_type": "MATERIAL",
|
||||
"scene.display.shading.curvature_ridge_factor": 0.0,
|
||||
"scene.display.shading.curvature_valley_factor": 0.0,
|
||||
"scene.display.shading.light": "STUDIO",
|
||||
"scene.display.shading.color_type": "MATERIAL",
|
||||
"scene.display.shading.object_outline_color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"scene.display.shading.render_pass": "COMBINED",
|
||||
"scene.display.shading.shadow_intensity": 0.5,
|
||||
"scene.display.shading.show_backface_culling": false,
|
||||
"scene.display.shading.show_cavity": false,
|
||||
"scene.display.shading.show_object_outline": false,
|
||||
"scene.display.shading.show_shadows": false,
|
||||
"scene.display.shading.show_specular_highlight": true,
|
||||
"scene.display.shading.show_xray": false,
|
||||
"scene.display.shading.show_xray_wireframe": false,
|
||||
"scene.display.shading.single_color": [
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
0.800000011920929
|
||||
],
|
||||
"scene.display.shading.show_shadows": false,
|
||||
"scene.display.shading.shadow_intensity": 0.5,
|
||||
"scene.display.shading.studio_light": "Default",
|
||||
"scene.display.shading.studiolight_background_alpha": 0.0,
|
||||
"scene.display.shading.studiolight_background_blur": 0.0,
|
||||
"scene.display.shading.studiolight_intensity": 0.0,
|
||||
"scene.display.shading.studiolight_rotate_z": 0.0,
|
||||
"scene.display.shading.type": "SOLID",
|
||||
"scene.display.shading.use_compositor": "DISABLED",
|
||||
"scene.display.shading.use_dof": false,
|
||||
"scene.display.shading.use_scene_lights": false,
|
||||
"scene.display.shading.use_scene_lights_render": false,
|
||||
"scene.display.shading.use_scene_world": false,
|
||||
"scene.display.shading.use_scene_world_render": false,
|
||||
"scene.display.shading.use_studiolight_view_rotation": true,
|
||||
"scene.display.shading.use_world_space_lighting": false,
|
||||
"scene.display.shading.wireframe_color_type": "MATERIAL",
|
||||
"scene.display.shading.xray_alpha": 0.5,
|
||||
"scene.display.shading.xray_alpha_wireframe": 0.0,
|
||||
"scene.display.light_direction": [
|
||||
0.5773502588272095,
|
||||
0.5773502588272095,
|
||||
0.5773502588272095
|
||||
],
|
||||
"scene.view_settings.use_curve_mapping": false,
|
||||
"space.overlay.show_wireframes": false,
|
||||
"space.overlay.wireframe_threshold": 1.0,
|
||||
"space.overlay.show_floor": true,
|
||||
"scene.display.matcap_ssao_attenuation": 1.0,
|
||||
"scene.display.matcap_ssao_distance": 0.20000000298023224,
|
||||
"scene.display.matcap_ssao_samples": 16,
|
||||
"scene.display.render_aa": "8",
|
||||
"scene.display.shadow_focus": 0.0,
|
||||
"scene.display.shadow_shift": 0.10000000149011612,
|
||||
"scene.display.viewport_aa": "FXAA",
|
||||
"space.overlay.backwire_opacity": 0.5,
|
||||
"space.overlay.bone_wire_alpha": 1.0,
|
||||
"space.overlay.display_handle": "SELECTED",
|
||||
"space.overlay.fade_inactive_alpha": 0.4000000059604645,
|
||||
"space.overlay.gpencil_fade_layer": 0.5,
|
||||
"space.overlay.gpencil_fade_objects": 0.5,
|
||||
"space.overlay.gpencil_grid_opacity": 0.5,
|
||||
"space.overlay.gpencil_vertex_paint_opacity": 1.0,
|
||||
"space.overlay.grid_lines": 16,
|
||||
"space.overlay.grid_scale": 1.0,
|
||||
"space.overlay.grid_subdivisions": 10,
|
||||
"space.overlay.normals_constant_screen_size": 7.0,
|
||||
"space.overlay.normals_length": 0.10000000149011612,
|
||||
"space.overlay.sculpt_curves_cage_opacity": 0.5,
|
||||
"space.overlay.sculpt_mode_face_sets_opacity": 1.0,
|
||||
"space.overlay.sculpt_mode_mask_opacity": 0.75,
|
||||
"space.overlay.show_annotation": true,
|
||||
"space.overlay.show_axis_x": true,
|
||||
"space.overlay.show_axis_y": true,
|
||||
"space.overlay.show_axis_z": false,
|
||||
"space.overlay.show_bones": true,
|
||||
"space.overlay.show_cursor": true,
|
||||
"space.overlay.show_curve_normals": false,
|
||||
"space.overlay.show_edge_bevel_weight": true,
|
||||
"space.overlay.show_edge_crease": true,
|
||||
"space.overlay.show_edge_seams": true,
|
||||
"space.overlay.show_edge_sharp": true,
|
||||
"space.overlay.show_edges": false,
|
||||
"space.overlay.show_extra_edge_angle": false,
|
||||
"space.overlay.show_extra_edge_length": false,
|
||||
"space.overlay.show_extra_face_angle": false,
|
||||
"space.overlay.show_extra_face_area": false,
|
||||
"space.overlay.show_extra_indices": false,
|
||||
"space.overlay.show_extras": true,
|
||||
"space.overlay.show_face_center": false,
|
||||
"space.overlay.show_face_normals": false,
|
||||
"space.overlay.show_face_orientation": false,
|
||||
"space.overlay.show_faces": true,
|
||||
"space.overlay.show_fade_inactive": false,
|
||||
"space.overlay.show_floor": true,
|
||||
"space.overlay.show_freestyle_edge_marks": true,
|
||||
"space.overlay.show_freestyle_face_marks": true,
|
||||
"space.overlay.show_look_dev": false,
|
||||
"space.overlay.show_motion_paths": true,
|
||||
"space.overlay.show_object_origins": true,
|
||||
"space.overlay.show_relationship_lines": true
|
||||
},
|
||||
"render_type": "VIEWPORT"
|
||||
"space.overlay.show_object_origins_all": false,
|
||||
"space.overlay.show_occlude_wire": false,
|
||||
"space.overlay.show_onion_skins": false,
|
||||
"space.overlay.show_ortho_grid": true,
|
||||
"space.overlay.show_outline_selected": true,
|
||||
"space.overlay.show_overlays": true,
|
||||
"space.overlay.show_paint_wire": false,
|
||||
"space.overlay.show_relationship_lines": true,
|
||||
"space.overlay.show_sculpt_curves_cage": true,
|
||||
"space.overlay.show_sculpt_face_sets": true,
|
||||
"space.overlay.show_sculpt_mask": true,
|
||||
"space.overlay.show_split_normals": false,
|
||||
"space.overlay.show_stats": false,
|
||||
"space.overlay.show_statvis": false,
|
||||
"space.overlay.show_text": true,
|
||||
"space.overlay.show_vertex_normals": false,
|
||||
"space.overlay.show_viewer_attribute": true,
|
||||
"space.overlay.show_weight": false,
|
||||
"space.overlay.show_wireframes": false,
|
||||
"space.overlay.show_wpaint_contours": false,
|
||||
"space.overlay.show_xray_bone": false,
|
||||
"space.overlay.texture_paint_mode_opacity": 1.0,
|
||||
"space.overlay.use_debug_freeze_view_culling": false,
|
||||
"space.overlay.use_gpencil_canvas_xray": false,
|
||||
"space.overlay.use_gpencil_edit_lines": true,
|
||||
"space.overlay.use_gpencil_fade_gp_objects": false,
|
||||
"space.overlay.use_gpencil_fade_layers": false,
|
||||
"space.overlay.use_gpencil_fade_objects": false,
|
||||
"space.overlay.use_gpencil_grid": false,
|
||||
"space.overlay.use_gpencil_multiedit_line_only": false,
|
||||
"space.overlay.use_gpencil_onion_skin": false,
|
||||
"space.overlay.use_gpencil_show_directions": false,
|
||||
"space.overlay.use_gpencil_show_material_name": false,
|
||||
"space.overlay.use_normals_constant_screen_size": false,
|
||||
"space.overlay.vertex_opacity": 1.0,
|
||||
"space.overlay.vertex_paint_mode_opacity": 1.0,
|
||||
"space.overlay.viewer_attribute_opacity": 1.0,
|
||||
"space.overlay.weight_paint_mode_opacity": 1.0,
|
||||
"space.overlay.wireframe_opacity": 1.0,
|
||||
"space.overlay.wireframe_threshold": 1.0,
|
||||
"space.overlay.xray_alpha_bone": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import addon_utils
|
||||
import ifcopenshell.api.owner.settings
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.owner as core_owner
|
||||
from blenderbim.bim.module.drawing.prop import RasterStyleProperty
|
||||
from bpy.app.handlers import persistent
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.owner.prop import get_user_person, get_user_organisation
|
||||
|
||||
@@ -381,7 +381,7 @@ class IfcImporter:
|
||||
representations = self.get_transformed_body_representations(element.Representation.Representations)
|
||||
|
||||
# Single swept disk solids (e.g. rebar) are better natively represented as beveled curves
|
||||
if self.is_native_swept_disk_solid(representations):
|
||||
if self.is_native_swept_disk_solid(element, representations):
|
||||
self.native_data[element.GlobalId] = {
|
||||
"representations": representations,
|
||||
"representation": self.get_body_representation(element.Representation.Representations),
|
||||
@@ -409,7 +409,11 @@ class IfcImporter:
|
||||
}
|
||||
return True
|
||||
|
||||
def is_native_swept_disk_solid(self, representations):
|
||||
def is_native_swept_disk_solid(self, element, representations):
|
||||
# detect BBIM Railings to represent them with meshes and not curves
|
||||
if tool.Pset.get_element_pset(element, "BBIM_Railing"):
|
||||
return False
|
||||
|
||||
for representation in representations:
|
||||
items = representation["raw"].Items or [] # Be forgiving of invalid IFCs because Revit :(
|
||||
if len(items) == 1 and items[0].is_a("IfcSweptDiskSolid"):
|
||||
|
||||
@@ -43,13 +43,29 @@ class BIM_OT_assign_object(bpy.types.Operator, Operator):
|
||||
related_object: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.assign_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
relating_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object)),
|
||||
related_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.related_object)),
|
||||
)
|
||||
for obj in bpy.context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
if self.relating_object:
|
||||
relating_obj = tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object))
|
||||
else:
|
||||
if obj == context.active_object:
|
||||
continue
|
||||
relating_obj = context.active_object
|
||||
if not relating_obj:
|
||||
continue
|
||||
result = core.assign_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
relating_obj=relating_obj,
|
||||
related_obj=obj,
|
||||
)
|
||||
if not result:
|
||||
self.report({"ERROR"}, f" Cannot aggregate {obj.name} to {relating_obj.name}")
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class BIM_OT_unassign_object(bpy.types.Operator, Operator):
|
||||
|
||||
@@ -76,6 +76,7 @@ classes = (
|
||||
operator.LoadCostItemTaskQuantities,
|
||||
operator.LoadCostItemResourceQuantities,
|
||||
operator.ChangeParentCostItem,
|
||||
operator.CopyCostItem,
|
||||
prop.CostItem,
|
||||
prop.CostItemQuantity,
|
||||
prop.CostItemType,
|
||||
|
||||
@@ -97,10 +97,9 @@ class AddSummaryCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Add Cost Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Add a summary cost item"
|
||||
cost_schedule: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_summary_cost_item(tool.Ifc, tool.Cost, cost_schedule=tool.Ifc.get().by_id(self.cost_schedule))
|
||||
core.add_summary_cost_item(tool.Ifc, tool.Cost, cost_schedule=tool.Cost.get_active_cost_schedule())
|
||||
|
||||
|
||||
class AddCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -114,6 +113,16 @@ class AddCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.add_cost_item(tool.Ifc, tool.Cost, cost_item=tool.Ifc.get().by_id(self.cost_item))
|
||||
|
||||
|
||||
class CopyCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.copy_cost_item"
|
||||
bl_label = "Copy Cost Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Copy a cost item"
|
||||
|
||||
def _execute(self, context):
|
||||
core.copy_cost_item(tool.Ifc, tool.Cost)
|
||||
|
||||
|
||||
class ExpandCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.expand_cost_item"
|
||||
bl_label = "Expand Cost Item"
|
||||
@@ -214,8 +223,9 @@ class UnassignCostItemType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.unassign_cost_item_type(
|
||||
tool.Ifc,
|
||||
tool.Cost,
|
||||
self.cost_item,
|
||||
products=[tool.Ifc.get().by_id(self.related_object)] if self.related_object else [],
|
||||
tool.Spatial,
|
||||
cost_item=tool.Ifc.get().by_id(self.cost_item),
|
||||
product_types=[tool.Ifc.get().by_id(self.related_object)] if self.related_object else [],
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -491,7 +501,7 @@ class AddCostColumn(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
def execute(self, context):
|
||||
core.add_cost_column(tool.Cost, self.name)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -619,10 +629,12 @@ class ExportCostSchedules(bpy.types.Operator):
|
||||
bl_label = "Export Cost Schedule"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Export a cost schedule to a CSV, XSLX OR ODS file"
|
||||
cost_schedule: bpy.props.IntProperty()
|
||||
format: bpy.props.EnumProperty("Format", items=(("CSV", "CSV", ""), ("XLSX", "XLSX", ""), ("ODS", "ODS", "")))
|
||||
|
||||
def execute(self, context):
|
||||
core.export_cost_schedules(tool.Cost, format=self.format)
|
||||
cost_schedule = tool.Ifc.get().by_id(self.cost_schedule) if self.cost_schedule else None
|
||||
core.export_cost_schedules(tool.Cost, format=self.format, cost_schedule=cost_schedule)
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -667,7 +679,9 @@ class LoadProductCostItems(bpy.types.Operator):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
core.load_product_cost_items(tool.Cost, product=tool.Ifc.get().by_id(context.active_object.BIMObjectProperties.ifc_definition_id))
|
||||
core.load_product_cost_items(
|
||||
tool.Cost, product=tool.Ifc.get().by_id(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -213,3 +213,4 @@ class BIMCostProperties(PropertyGroup):
|
||||
name="Show Nested Tasks", default=False, update=update_active_cost_item_resources
|
||||
)
|
||||
change_cost_item_parent: BoolProperty(name="Change Cost Item Parent", default=False, update=update_cost_item_parent)
|
||||
show_cost_item_operators: BoolProperty(name="Show Cost Item Operators", default=False)
|
||||
|
||||
@@ -43,43 +43,54 @@ class BIM_PT_cost_schedules(Panel):
|
||||
|
||||
self.props = context.scene.BIMCostProperties
|
||||
row = self.layout.row()
|
||||
if CostSchedulesData.data["total_cost_schedules"]:
|
||||
row.label(text=f"{CostSchedulesData.data['total_cost_schedules']} Cost Schedules Found", icon="TEXT")
|
||||
row.operator("bim.export_cost_schedules", text="Export as spreadsheet", icon="EXPORT")
|
||||
else:
|
||||
row.label(text="No Cost Schedules Found found.", icon="COMMUNITY")
|
||||
|
||||
row = self.layout.row()
|
||||
row.prop(self.props, "cost_schedule_predefined_types")
|
||||
row.operator("bim.add_cost_schedule", icon="ADD", text="Add")
|
||||
if not self.props.active_cost_schedule_id:
|
||||
if CostSchedulesData.data["total_cost_schedules"]:
|
||||
row.label(text=f"{CostSchedulesData.data['total_cost_schedules']} Cost Schedules Found", icon="TEXT")
|
||||
row.operator("bim.export_cost_schedules", text="Export as spreadsheet", icon="EXPORT")
|
||||
else:
|
||||
row.label(text="No Cost Schedules Found found.", icon="COMMUNITY")
|
||||
row = self.layout.row()
|
||||
row.prop(self.props, "cost_schedule_predefined_types")
|
||||
row.operator("bim.add_cost_schedule", icon="ADD", text="Add")
|
||||
|
||||
for schedule in CostSchedulesData.data["schedules"]:
|
||||
self.draw_cost_schedule_ui(schedule)
|
||||
|
||||
def draw_cost_schedule_ui(self, cost_schedule):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=cost_schedule["name"], icon="LINENUMBERS_ON")
|
||||
|
||||
if self.props.active_cost_schedule_id and self.props.active_cost_schedule_id == cost_schedule["id"]:
|
||||
op = row.operator("bim.select_cost_schedule_products", icon="RESTRICT_SELECT_OFF", text="Assigned")
|
||||
row.label(text="Currently editing: {}".format(cost_schedule["name"]), icon="LINENUMBERS_ON")
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.alignment = "LEFT"
|
||||
row1.label(text="Schedule tools")
|
||||
row1 = col.row(align=True)
|
||||
row1.alignment = "RIGHT"
|
||||
row1.operator("bim.export_cost_schedules", text="Export", icon="EXPORT").cost_schedule = cost_schedule["id"]
|
||||
row2 = col.row(align=True)
|
||||
row2.alignment = "RIGHT"
|
||||
op = row2.operator("bim.select_cost_schedule_products", icon="RESTRICT_SELECT_OFF", text="Assigned")
|
||||
op.cost_schedule = cost_schedule["id"]
|
||||
row.operator("bim.select_unassigned_products", icon="RESTRICT_SELECT_OFF", text="Unassigned")
|
||||
row2.operator("bim.select_unassigned_products", icon="RESTRICT_SELECT_OFF", text="Unassigned")
|
||||
|
||||
row.prop(self.props, "should_show_column_ui", text="", icon="SHORTDISPLAY")
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.alignment = "LEFT"
|
||||
row1.label(text="Settings")
|
||||
row1 = col.row(align=True)
|
||||
row1.alignment = "RIGHT"
|
||||
row1.prop(self.props, "should_show_column_ui", text="Schedule Columns", icon="SHORTDISPLAY")
|
||||
if self.props.is_editing == "COST_SCHEDULE_ATTRIBUTES":
|
||||
row.operator("bim.edit_cost_schedule", text="", icon="CHECKMARK")
|
||||
elif self.props.is_editing == "COST_ITEMS":
|
||||
row.operator("bim.add_summary_cost_item", text="", icon="ADD").cost_schedule = cost_schedule["id"]
|
||||
row.operator("bim.disable_editing_cost_schedule", text="", icon="CANCEL")
|
||||
elif self.props.active_cost_schedule_id:
|
||||
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule["id"]
|
||||
row.operator("bim.disable_editing_cost_schedule", text="Disable Editing", icon="CANCEL")
|
||||
else:
|
||||
row.label(text=cost_schedule["name"], icon="LINENUMBERS_ON")
|
||||
row.operator("bim.enable_editing_cost_items", text="", icon="OUTLINER").cost_schedule = cost_schedule["id"]
|
||||
row.operator(
|
||||
"bim.enable_editing_cost_schedule_attributes", text="", icon="GREASEPENCIL"
|
||||
).cost_schedule = cost_schedule["id"]
|
||||
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule["id"]
|
||||
|
||||
if self.props.active_cost_schedule_id == cost_schedule["id"]:
|
||||
if self.props.is_editing == "COST_SCHEDULE_ATTRIBUTES":
|
||||
self.draw_editable_cost_schedule_ui()
|
||||
@@ -101,30 +112,39 @@ class BIM_PT_cost_schedules(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
ifc_definition_id = None
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Cost Item Tools")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.add_summary_cost_item", text="Add Summary Cost", icon="ADD")
|
||||
row.operator("bim.expand_all_tasks", text="Expand All")
|
||||
row.operator("bim.contract_all_tasks", text="Contract All")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
if self.props.cost_items and self.props.active_cost_item_index < len(self.props.cost_items):
|
||||
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
|
||||
if ifc_definition_id:
|
||||
row.prop(self.props, "change_cost_item_parent", text="", icon="LINKED")
|
||||
row.prop(self.props, "enable_reorder", text="", icon="SORTALPHA")
|
||||
if not CostSchedulesData.data["is_editing_rates"]:
|
||||
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
|
||||
row.prop(self.props, "show_cost_item_operators", text="Edit", icon="DOWNARROW_HLT")
|
||||
row.operator("bim.add_cost_item", text="Add", icon="ADD").cost_item = ifc_definition_id
|
||||
row.operator("bim.copy_cost_item", text="Copy", icon="ADD")
|
||||
row.operator("bim.remove_cost_item", text="Delete", icon="X").cost_item = ifc_definition_id
|
||||
if self.props.show_cost_item_operators:
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.props, "change_cost_item_parent", text="", icon="LINKED")
|
||||
row.prop(self.props, "enable_reorder", text="", icon="SORTALPHA")
|
||||
if not CostSchedulesData.data["is_editing_rates"]:
|
||||
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
|
||||
op.cost_item = ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_cost_item_values", text="", icon="DISC")
|
||||
op.cost_item = ifc_definition_id
|
||||
|
||||
op = row.operator("bim.enable_editing_cost_item_values", text="", icon="DISC")
|
||||
op.cost_item = ifc_definition_id
|
||||
|
||||
row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = ifc_definition_id
|
||||
|
||||
if self.props.active_cost_item_id == ifc_definition_id:
|
||||
if self.props.cost_item_editing_type == "ATTRIBUTES":
|
||||
row.operator("bim.edit_cost_item", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL")
|
||||
else:
|
||||
op = row.operator("bim.enable_editing_cost_item_attributes", text="", icon="GREASEPENCIL")
|
||||
op.cost_item = ifc_definition_id
|
||||
|
||||
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = ifc_definition_id
|
||||
|
||||
if self.props.active_cost_item_id == ifc_definition_id:
|
||||
if self.props.cost_item_editing_type == "ATTRIBUTES":
|
||||
row.operator("bim.edit_cost_item", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL")
|
||||
else:
|
||||
op = row.operator("bim.enable_editing_cost_item_attributes", text="", icon="GREASEPENCIL")
|
||||
op.cost_item = ifc_definition_id
|
||||
self.layout.template_list(
|
||||
"BIM_UL_cost_items",
|
||||
"",
|
||||
@@ -698,9 +718,7 @@ class BIM_PT_Costing_Tools(Panel):
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMCostProperties
|
||||
row = self.layout.row()
|
||||
row.operator(
|
||||
"bim.load_product_cost_items", icon="FILE_REFRESH"
|
||||
)
|
||||
row.operator("bim.load_product_cost_items", icon="FILE_REFRESH")
|
||||
row = self.layout.row()
|
||||
row.template_list(
|
||||
"BIM_UL_product_cost_items",
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import os
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
import bmesh
|
||||
|
||||
|
||||
class Annotator:
|
||||
@@ -94,7 +95,7 @@ class Annotator:
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def add_plane_to_annotation(obj):
|
||||
def add_plane_to_annotation(obj, remove_face=False):
|
||||
# default order = bot left, top left, bot right, top right
|
||||
# therefore we redefine the order
|
||||
face_verts = [0, 2, 3, 1]
|
||||
@@ -104,7 +105,9 @@ class Annotator:
|
||||
bm = tool.Blender.get_bmesh_for_mesh(obj.data, clean=True)
|
||||
new_verts = [bm.verts.new(v) for v in verts_local]
|
||||
|
||||
bm.faces.new([new_verts[i] for i in face_verts])
|
||||
face = bm.faces.new([new_verts[i] for i in face_verts])
|
||||
if remove_face:
|
||||
bmesh.ops.delete(bm, geom=[face], context="FACES_ONLY")
|
||||
tool.Blender.apply_bmesh(obj.data, bm, obj)
|
||||
return obj
|
||||
|
||||
|
||||
@@ -32,13 +32,38 @@ from bpy.types import SpaceView3D
|
||||
from mathutils import Vector, Matrix
|
||||
from bpy_extras.view3d_utils import location_3d_to_region_2d
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from blenderbim.bim.module.drawing.data import DecoratorData
|
||||
from blenderbim.bim.module.drawing.shaders import BASE_LIB_GLSL, BASE_DEF_GLSL, add_verts_sequence, add_offsets
|
||||
|
||||
from blenderbim.bim.module.drawing.data import DecoratorData, DrawingsData
|
||||
from blenderbim.bim.module.drawing.shaders import add_verts_sequence, add_offsets
|
||||
from blenderbim.bim.module.drawing.helper import format_distance
|
||||
from timeit import default_timer as timer
|
||||
from functools import lru_cache
|
||||
|
||||
UNSPECIAL_ELEMENT_COLOR = (0.2, 0.2, 0.2, 1) # GREY # TODO: move back to 0.2
|
||||
|
||||
|
||||
class profile_consequential:
|
||||
start_time = None
|
||||
lines = []
|
||||
|
||||
@classmethod
|
||||
def __init__(cls, test_name):
|
||||
cls.log()
|
||||
cls.start_time = timer()
|
||||
cls.test_name = test_name
|
||||
|
||||
@classmethod
|
||||
def log(cls):
|
||||
if cls.start_time is not None:
|
||||
cls.lines.append(f"{cls.test_name}\t{timer() - cls.start_time:.10f}")
|
||||
|
||||
@classmethod
|
||||
def stop(cls):
|
||||
cls.log()
|
||||
cls.start_time = None
|
||||
print('\n'.join(cls.lines))
|
||||
cls.lines = []
|
||||
|
||||
|
||||
def ccw(A, B, C):
|
||||
"""whether a-b-c located in counter-clockwise order in 2d space"""
|
||||
return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x)
|
||||
@@ -167,56 +192,6 @@ class BaseDecorator:
|
||||
def camera_zoom_to_factor(self, zoom):
|
||||
return math.pow(((zoom / 50) + math.sqrt(2)) / 2, 2)
|
||||
|
||||
def get_objects(self, collection):
|
||||
"""find relevant objects
|
||||
using class.objecttype
|
||||
|
||||
returns: iterable of blender objects
|
||||
"""
|
||||
results = []
|
||||
# NOTE: if the ObjectType is not on the list
|
||||
# it will be also drawn with MiscDecorator
|
||||
decoration_presets = (
|
||||
"DIMENSION",
|
||||
"TEXT_LEADER",
|
||||
"STAIR_ARROW",
|
||||
"HIDDEN_LINE",
|
||||
"PLAN_LEVEL",
|
||||
"SECTION_LEVEL",
|
||||
"BREAKLINE",
|
||||
"GRID",
|
||||
"ELEVATION",
|
||||
"SECTION",
|
||||
"TEXT",
|
||||
"BATTING",
|
||||
)
|
||||
for obj in collection.all_objects:
|
||||
if obj.hide_get():
|
||||
continue
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
|
||||
if element.is_a("IfcAnnotation"):
|
||||
if element.ObjectType == self.objecttype:
|
||||
results.append(obj)
|
||||
|
||||
elif (
|
||||
self.objecttype == "MISC"
|
||||
and element.ObjectType not in decoration_presets
|
||||
and isinstance(obj.data, bpy.types.Mesh)
|
||||
):
|
||||
results.append(obj)
|
||||
|
||||
elif self.objecttype == "FALL" and element.ObjectType in (
|
||||
"SLOPE_ANGLE",
|
||||
"SLOPE_FRACTION",
|
||||
"SLOPE_PERCENT",
|
||||
):
|
||||
results.append(obj)
|
||||
return results
|
||||
|
||||
def get_splines(self, obj):
|
||||
"""Iterates through splines
|
||||
Args:
|
||||
@@ -343,7 +318,7 @@ class BaseDecorator:
|
||||
shader.uniform_float("color", color)
|
||||
batch.draw(shader)
|
||||
|
||||
def draw_lines(self, context, obj, vertices, indices, color=None):
|
||||
def draw_lines(self, context, obj, vertices, indices, color=None, line_width=1.0):
|
||||
"""`verts` should be in winspace with `(0,0,0)` in the screen left bottom corner, not in the center"""
|
||||
region = context.region
|
||||
if not color:
|
||||
@@ -352,7 +327,7 @@ class BaseDecorator:
|
||||
self.line_shader.bind()
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (region.width, region.height))
|
||||
self.line_shader.uniform_float("lineWidth", 1.0)
|
||||
self.line_shader.uniform_float("lineWidth", line_width)
|
||||
gpu.state.blend_set("ALPHA")
|
||||
self.draw_batch("LINES", vertices, color, indices)
|
||||
|
||||
@@ -486,14 +461,15 @@ class BaseDecorator:
|
||||
blf.draw(font_id, text)
|
||||
blf.disable(font_id, blf.ROTATION)
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def format_value(self, context, value):
|
||||
return bpy.utils.units.to_string(
|
||||
context.scene.unit_settings.system,
|
||||
"LENGTH",
|
||||
value,
|
||||
precision=4,
|
||||
split_unit=context.scene.unit_settings.system == "IMPERIAL",
|
||||
)
|
||||
drawing_pset_data = DrawingsData.data["active_drawing_pset_data"]
|
||||
precision = drawing_pset_data.get("MetricPrecision", None)
|
||||
if not precision:
|
||||
precision = drawing_pset_data.get("ImperialPrecision", None)
|
||||
|
||||
decimal_places = drawing_pset_data.get("DecimalPlaces", None)
|
||||
return format_distance(value, precision=precision, decimal_places=decimal_places)
|
||||
|
||||
def draw_asterisk(self, context, obj):
|
||||
# gather geometry data and convert to winspace
|
||||
@@ -520,6 +496,7 @@ class BaseDecorator:
|
||||
|
||||
def draw_text(self, context, obj, text_world_position=None, reverse_lines_order=False):
|
||||
"""if `text_world_position` is not provided, the object's location will be used"""
|
||||
|
||||
if not text_world_position:
|
||||
text_world_position = obj.location
|
||||
|
||||
@@ -570,6 +547,7 @@ class BaseDecorator:
|
||||
line_i += 1 if not reverse_lines_order else -1
|
||||
|
||||
|
||||
|
||||
class DimensionDecorator(BaseDecorator):
|
||||
"""Decorator for dimension objects
|
||||
- each edge of a segment with arrow
|
||||
@@ -657,6 +635,7 @@ class DimensionDecorator(BaseDecorator):
|
||||
text_offset_value = viewportDrawingScale * 3
|
||||
|
||||
for i0, i1 in indices:
|
||||
|
||||
v0 = Vector(vertices[i0])
|
||||
v1 = Vector(vertices[i1])
|
||||
p0 = location_3d_to_region_2d(region, region3d, v0)
|
||||
@@ -669,8 +648,6 @@ class DimensionDecorator(BaseDecorator):
|
||||
|
||||
if not show_description_only:
|
||||
length = (v1 - v0).length
|
||||
# TODO: same distance format function as in svg?
|
||||
# requires storing drawing precision and decimal_places from pset to data.py
|
||||
text = self.format_value(context, length)
|
||||
text = text_prefix + text + text_suffix
|
||||
|
||||
@@ -982,15 +959,13 @@ class StairDecorator(BaseDecorator):
|
||||
# circle head on first vert
|
||||
if edge[0] == 0:
|
||||
circle_head = get_circle_head(circle_size)
|
||||
add_verts_sequence([v + v0 for v in circle_head], start_i, **out_kwargs, closed=True)
|
||||
start_i += 12
|
||||
start_i = add_verts_sequence([v + v0 for v in circle_head], start_i, **out_kwargs, closed=True)
|
||||
|
||||
# arrow head on last vert
|
||||
if edge[1] == last_vert:
|
||||
edge_dir = (v1 - v0).normalized()
|
||||
arrow_head = get_arrow_head(edge_dir, arrow_size, rot_matrix_cw, rot_matrix_ccw)
|
||||
add_verts_sequence([v1 - arrow_head[1], v1, v1 - arrow_head[2]], start_i, **out_kwargs)
|
||||
start_i += 3
|
||||
start_i = add_verts_sequence([v1 - arrow_head[1], v1, v1 - arrow_head[2]], start_i, **out_kwargs)
|
||||
|
||||
# stem with gaps for arrows
|
||||
add_verts_sequence([v0, v1], start_i, **out_kwargs)
|
||||
@@ -1010,6 +985,19 @@ class MiscDecorator(BaseDecorator):
|
||||
self.draw_lines(context, obj, winspace_verts, idxs)
|
||||
|
||||
|
||||
class RevisionCloudDecorator(BaseDecorator):
|
||||
objecttype = "REVISION_CLOUD"
|
||||
|
||||
def decorate(self, context, obj):
|
||||
if obj.data.is_editmode:
|
||||
verts, idxs = self.get_editmesh_geom(obj)
|
||||
else:
|
||||
verts, idxs = self.get_mesh_geom(obj)
|
||||
winspace_verts = worldspace_to_winspace(verts, context)
|
||||
# TODO: draw revision clouds inside viewport?
|
||||
self.draw_lines(context, obj, winspace_verts, idxs, color=(1, 0, 0, 1), line_width=2)
|
||||
|
||||
|
||||
# TODO: custom frag shader to support dashed lines?
|
||||
class HiddenDecorator(BaseDecorator):
|
||||
objecttype = "HIDDEN_LINE"
|
||||
@@ -1883,6 +1871,7 @@ class DecorationsHandler:
|
||||
PlanLevelDecorator,
|
||||
SectionLevelDecorator,
|
||||
StairDecorator,
|
||||
RevisionCloudDecorator,
|
||||
BreakDecorator,
|
||||
SectionDecorator,
|
||||
ElevationDecorator,
|
||||
@@ -1898,7 +1887,7 @@ class DecorationsHandler:
|
||||
if cls.installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
# NOTE: that we USE POST_PIXEL here so that we can draw use both 3D_POLYLINE_UNIFORM_COLOR
|
||||
# NOTE: we USE POST_PIXEL here so that we can draw use both 3D_POLYLINE_UNIFORM_COLOR
|
||||
# and drawing text in the same handler. BUT this means that we supply coordinates in WINSPACE
|
||||
cls.installed = SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_PIXEL")
|
||||
|
||||
@@ -1911,13 +1900,44 @@ class DecorationsHandler:
|
||||
cls.installed = None
|
||||
|
||||
def __init__(self):
|
||||
self.decorators = [cls() for cls in self.decorators_classes]
|
||||
self.decorators = {cls.objecttype: cls() for cls in self.decorators_classes}
|
||||
for object_type in ("SLOPE_ANGLE", "SLOPE_FRACTION", "SLOPE_PERCENT"):
|
||||
self.decorators[object_type] = self.decorators["FALL"]
|
||||
|
||||
def get_objects_and_decorators(self, collection):
|
||||
results = []
|
||||
|
||||
for obj in collection.all_objects:
|
||||
if obj.hide_get():
|
||||
continue
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
|
||||
if not element.is_a("IfcAnnotation"):
|
||||
continue
|
||||
|
||||
object_type = element.ObjectType
|
||||
if object_type == "DRAWING":
|
||||
continue
|
||||
|
||||
if (dec := self.decorators.get(object_type, None)):
|
||||
results.append((obj, dec))
|
||||
|
||||
elif isinstance(obj.data, bpy.types.Mesh):
|
||||
results.append((obj, self.decorators["MISC"]))
|
||||
|
||||
return results
|
||||
|
||||
def __call__(self, context):
|
||||
collection, _ = helper.get_active_drawing(context.scene)
|
||||
if collection is None:
|
||||
return
|
||||
|
||||
for decorator in self.decorators:
|
||||
for obj in decorator.get_objects(collection):
|
||||
decorator.decorate(context, obj)
|
||||
if not DrawingsData.is_loaded:
|
||||
DrawingsData.load()
|
||||
|
||||
object_decorators = self.get_objects_and_decorators(collection)
|
||||
for obj, decorator in object_decorators:
|
||||
decorator.decorate(context, obj)
|
||||
|
||||
@@ -154,13 +154,14 @@ def format_distance(
|
||||
# Separate ft and inches
|
||||
# Unless Inches are the specified Length Unit
|
||||
if unit_length != "INCHES":
|
||||
feet = math.floor(decInches / inPerFoot)
|
||||
feet = int(decInches / inPerFoot) # remove decimal
|
||||
decInches -= feet * inPerFoot
|
||||
else:
|
||||
feet = 0
|
||||
|
||||
# Separate Fractional Inches
|
||||
inches = math.floor(decInches)
|
||||
decInches = abs(decInches) # ignore the sign for inches
|
||||
inches = math.floor(decInches) # remove decimal
|
||||
if inches != 0:
|
||||
frac = round(base * (decInches - inches))
|
||||
else:
|
||||
|
||||
@@ -44,9 +44,9 @@ from blenderbim.bim.module.drawing.decoration import CutDecorator
|
||||
from blenderbim.bim.module.drawing.data import DecoratorData, DrawingsData
|
||||
import blenderbim.bim.export_ifc
|
||||
from lxml import etree
|
||||
from mathutils import Vector
|
||||
from mathutils import Vector, Color
|
||||
from timeit import default_timer as timer
|
||||
from blenderbim.bim.module.drawing.prop import RasterStyleProperty, Literal
|
||||
from blenderbim.bim.module.drawing.prop import RasterStyleProperty, Literal, RASTER_STYLE_PROPERTIES_EXCLUDE
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from pathlib import Path
|
||||
|
||||
@@ -481,6 +481,14 @@ class CreateDrawing(bpy.types.Operator):
|
||||
tree.add_element(elem)
|
||||
elements -= processed
|
||||
|
||||
if self.camera_element not in drawing_elements:
|
||||
with profile("Camera element"):
|
||||
# The camera must always be included, regardless of any include/exclude filters.
|
||||
geom_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True)
|
||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, include=[self.camera_element])
|
||||
for elem in self.yield_from_iterator(it):
|
||||
self.serialiser.write(elem)
|
||||
|
||||
with profile("Finalizing"):
|
||||
self.serialiser.finalize()
|
||||
results = self.svg_buffer.get_value()
|
||||
@@ -836,15 +844,15 @@ class CreateDrawing(bpy.types.Operator):
|
||||
ifc = tool.Ifc.get()
|
||||
for el in root.findall(".//{http://www.w3.org/2000/svg}g[@{http://www.ifcopenshell.org/ns}guid]"):
|
||||
element = ifc.by_guid(el.get("{http://www.ifcopenshell.org/ns}guid"))
|
||||
# Architectural convention only merges these objects. E.g. pipe segments and fittings shouldn't merge.
|
||||
if not element.is_a("IfcWall") and not element.is_a("IfcSlab"):
|
||||
continue
|
||||
|
||||
classes = self.get_svg_classes(element)
|
||||
classes.append("cut")
|
||||
|
||||
el.set("class", " ".join(classes))
|
||||
|
||||
# Architectural convention only merges these objects. E.g. pipe segments and fittings shouldn't merge.
|
||||
if not element.is_a("IfcWall") and not element.is_a("IfcSlab"):
|
||||
continue
|
||||
|
||||
keys = []
|
||||
for query in join_criteria:
|
||||
key = ifcopenshell.util.selector.get_element_value(element, query)
|
||||
@@ -1055,7 +1063,7 @@ class AddSheet(bpy.types.Operator, Operator):
|
||||
|
||||
class OpenSheet(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.open_sheet"
|
||||
bl_label = "Open Sheet"
|
||||
bl_label = "Open Sheet Layout"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
@@ -1138,6 +1146,7 @@ class RemoveDrawingFromSheet(bpy.types.Operator, Operator):
|
||||
class CreateSheets(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.create_sheets"
|
||||
bl_label = "Create Sheets"
|
||||
bl_description = "Build a sheet from the sheet layout"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
@@ -1174,7 +1183,7 @@ class CreateSheets(bpy.types.Operator, Operator):
|
||||
has_sheet_reference = True
|
||||
elif reference.Description == "RASTER":
|
||||
if reference.Location in raster_references:
|
||||
del raster_references[reference.Location]
|
||||
raster_references.remove(reference.Location)
|
||||
else:
|
||||
tool.Ifc.run("document.remove_reference", reference=reference)
|
||||
|
||||
@@ -1298,9 +1307,13 @@ class ActivateModel(bpy.types.Operator):
|
||||
bl_description = "Activates the model view"
|
||||
|
||||
def execute(self, context):
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
dprops.active_drawing_id = 0
|
||||
|
||||
CutDecorator.uninstall()
|
||||
|
||||
bpy.ops.object.hide_view_clear()
|
||||
if not bpy.app.background:
|
||||
bpy.ops.object.hide_view_clear()
|
||||
|
||||
subcontext = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
@@ -1341,6 +1354,8 @@ class ActivateDrawing(bpy.types.Operator):
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
core.activate_drawing_view(tool.Ifc, tool.Drawing, drawing=drawing)
|
||||
dprops.active_drawing_id = self.drawing
|
||||
# reset DrawingsData to reload_drawing_styles work correctly
|
||||
DrawingsData.is_loaded = False
|
||||
dprops.drawing_styles.clear()
|
||||
if ifcopenshell.util.element.get_pset(drawing, "EPset_Drawing", "HasUnderlay"):
|
||||
bpy.ops.bim.reload_drawing_styles()
|
||||
@@ -1408,7 +1423,7 @@ class RemoveDrawing(bpy.types.Operator, Operator):
|
||||
else:
|
||||
drawings = [tool.Ifc.get().by_id(self.drawing)]
|
||||
|
||||
print("Removing drawings: {}".format([d for d in drawings]))
|
||||
removed_drawings = [drawing.id() for drawing in drawings]
|
||||
|
||||
for drawing in drawings:
|
||||
core.remove_drawing(tool.Ifc, tool.Drawing, drawing=drawing)
|
||||
@@ -1418,6 +1433,7 @@ class ReloadDrawingStyles(bpy.types.Operator):
|
||||
bl_idname = "bim.reload_drawing_styles"
|
||||
bl_label = "Reload Drawing Styles"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Reload drawing styles for the active camera"
|
||||
|
||||
def execute(self, context):
|
||||
if not DrawingsData.is_loaded:
|
||||
@@ -1514,14 +1530,38 @@ class SaveDrawingStyle(bpy.types.Operator, Operator):
|
||||
space = self.get_view_3d(context) # Do not remove. It is used later in eval
|
||||
scene = context.scene
|
||||
style = {}
|
||||
for prop in RasterStyleProperty:
|
||||
value = eval(prop.value)
|
||||
eval_namespace = {"context": context, "scene": scene, "space": space}
|
||||
|
||||
def add_prop_to_style(prop_path, context, scene, space):
|
||||
value = eval(prop_path)
|
||||
if not isinstance(value, str):
|
||||
try:
|
||||
value = tuple(value)
|
||||
except TypeError:
|
||||
pass
|
||||
style[prop.value] = value
|
||||
style[prop_path] = value
|
||||
|
||||
for prop in RasterStyleProperty:
|
||||
if prop.name.startswith("EVAL_PROP"):
|
||||
prop_path = prop.value
|
||||
add_prop_to_style(prop_path, **eval_namespace)
|
||||
else:
|
||||
props_source_path = prop.value
|
||||
props_source = eval(props_source_path)
|
||||
for prop_name in dir(props_source):
|
||||
if prop_name.startswith("__"):
|
||||
continue
|
||||
|
||||
prop_path = f"{props_source_path}.{prop_name}"
|
||||
prop_value = eval(prop_path)
|
||||
if (
|
||||
not isinstance(prop_value, (int, float, bool, str, Color, Vector))
|
||||
or props_source.is_property_readonly(prop_name)
|
||||
or prop_path in RASTER_STYLE_PROPERTIES_EXCLUDE
|
||||
):
|
||||
continue
|
||||
|
||||
add_prop_to_style(prop_path, **eval_namespace)
|
||||
|
||||
if self.index:
|
||||
index = int(self.index)
|
||||
@@ -1606,7 +1646,8 @@ class ActivateDrawingStyle(bpy.types.Operator, Operator):
|
||||
active_drawing_style_index = scene.camera.data.BIMCameraProperties.active_drawing_style_index
|
||||
|
||||
if active_drawing_style_index >= len(scene.DocProperties.drawing_styles):
|
||||
return
|
||||
self.report({"ERROR"}, "Could not find active drawing style")
|
||||
return {"CANCELLED"}
|
||||
|
||||
self.drawing_style = scene.DocProperties.drawing_styles[active_drawing_style_index]
|
||||
|
||||
|
||||
@@ -178,6 +178,10 @@ def update_schedule_name(self, context):
|
||||
|
||||
def update_has_underlay(self, context):
|
||||
update_layer(self, context, "HasUnderlay", self.has_underlay)
|
||||
# making sure that camera is active
|
||||
if self.has_underlay and (context.active_object and context.active_object.data == self.id_data):
|
||||
bpy.ops.bim.reload_drawing_styles()
|
||||
bpy.ops.bim.activate_drawing_style()
|
||||
|
||||
|
||||
def update_has_linework(self, context):
|
||||
@@ -271,30 +275,20 @@ class DrawingStyle(PropertyGroup):
|
||||
|
||||
|
||||
class RasterStyleProperty(enum.Enum):
|
||||
WORLD_COLOR = "bpy.data.worlds[0].color"
|
||||
RENDER_ENGINE = "scene.render.engine"
|
||||
RENDER_TRANSPARENT = "scene.render.film_transparent"
|
||||
VIEW_TRANSFORM = "scene.view_settings.view_transform"
|
||||
SHADING_SHOW_OBJECT_OUTLINE = "scene.display.shading.show_object_outline"
|
||||
SHADING_SHOW_CAVITY = "scene.display.shading.show_cavity"
|
||||
SHADING_CAVITY_TYPE = "scene.display.shading.cavity_type"
|
||||
SHADING_CURVATURE_RIDGE_FACTOR = "scene.display.shading.curvature_ridge_factor"
|
||||
SHADING_CURVATURE_VALLEY_FACTOR = "scene.display.shading.curvature_valley_factor"
|
||||
SHADING_LIGHT = "scene.display.shading.light"
|
||||
SHADING_COLOR_TYPE = "scene.display.shading.color_type"
|
||||
SHADING_SINGLE_COLOR = "scene.display.shading.single_color"
|
||||
SHADING_SHOW_SHADOWS = "scene.display.shading.show_shadows"
|
||||
SHADING_SHADOW_INTENSITY = "scene.display.shading.shadow_intensity"
|
||||
DISPLAY_LIGHT_DIRECTION = "scene.display.light_direction"
|
||||
VIEW_USE_CURVE_MAPPING = "scene.view_settings.use_curve_mapping"
|
||||
OVERLAY_SHOW_WIREFRAMES = "space.overlay.show_wireframes"
|
||||
OVERLAY_WIREFRAME_THRESHOLD = "space.overlay.wireframe_threshold"
|
||||
OVERLAY_SHOW_FLOOR = "space.overlay.show_floor"
|
||||
OVERLAY_SHOW_AXIS_X = "space.overlay.show_axis_x"
|
||||
OVERLAY_SHOW_AXIS_Y = "space.overlay.show_axis_y"
|
||||
OVERLAY_SHOW_AXIS_Z = "space.overlay.show_axis_z"
|
||||
OVERLAY_SHOW_OBJECT_ORIGINS = "space.overlay.show_object_origins"
|
||||
OVERLAY_SHOW_RELATIONSHIP_LINES = "space.overlay.show_relationship_lines"
|
||||
# EVAL_PROP_ props will be evaluated explicitly
|
||||
EVAL_PROP_WORLD_COLOR = "bpy.data.worlds[0].color"
|
||||
|
||||
# those props attributes used as a source for shading style properties
|
||||
RENDER = "scene.render"
|
||||
VIEW_SETTINGS = "scene.view_settings"
|
||||
SHADING = "scene.display.shading"
|
||||
DISPLAY = "scene.display"
|
||||
OVERLAY = "space.overlay"
|
||||
|
||||
|
||||
RASTER_STYLE_PROPERTIES_EXCLUDE = (
|
||||
"scene.render.filepath",
|
||||
)
|
||||
|
||||
|
||||
class DocProperties(PropertyGroup):
|
||||
@@ -492,6 +486,7 @@ ANNOTATION_TYPES_DATA = {
|
||||
"BREAKLINE": ("Breakline", "", "FCURVE", "mesh"),
|
||||
"LINEWORK": ("Line", "", "MESH_MONKEY", "mesh"),
|
||||
"BATTING": ("Batting", "Add batting annotation.\nThickness could be changed through Thickness property of BBIM_Batting property set", "FORCE_FORCE", "mesh"),
|
||||
"REVISION_CLOUD":("Revision Cloud", "Add revision cloud", "VOLUME_DATA", "mesh"),
|
||||
"FILL_AREA": ("Fill Area", "", "NODE_TEXTURE", "mesh"),
|
||||
"FALL": ("Fall", "", "SORT_ASC", "curve"),
|
||||
}
|
||||
@@ -514,6 +509,8 @@ def update_annotation_object_type(self, context):
|
||||
# changing enum doesn't trigger refresh by itself
|
||||
AnnotationData.is_loaded = False
|
||||
|
||||
def update_sheet_data(self, context):
|
||||
SheetsData.is_loaded = False
|
||||
|
||||
class BIMAnnotationProperties(PropertyGroup):
|
||||
object_type: bpy.props.EnumProperty(
|
||||
|
||||
@@ -225,17 +225,24 @@ class SvgWriter:
|
||||
)
|
||||
)
|
||||
# TODO: allow metric to be configurable
|
||||
rl = (matrix_world @ points[0].co.xyz).z
|
||||
rl_value = (matrix_world @ points[0].co.xyz).z
|
||||
if bpy.context.scene.unit_settings.system == "IMPERIAL":
|
||||
rl = helper.format_distance(rl, precision=self.precision, decimal_places=self.decimal_places)
|
||||
rl = helper.format_distance(rl_value, precision=self.precision, decimal_places=self.decimal_places)
|
||||
else:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
rl /= unit_scale
|
||||
rl = rl_value / unit_scale
|
||||
rl = ifcopenshell.util.geolocation.auto_z2e(tool.Ifc.get(), rl)
|
||||
rl *= unit_scale
|
||||
rl = "{:.3f}m".format(rl)
|
||||
text_style = SvgWriter.get_box_alignment_parameters("bottom-left")
|
||||
self.svg.add(self.svg.text(f"RL +{rl}", insert=tuple(text_position), class_="SECTIONLEVEL", **text_style))
|
||||
self.svg.add(
|
||||
self.svg.text(
|
||||
"RL {}{}".format("" if rl_value < 0 else "+", rl),
|
||||
insert=tuple(text_position),
|
||||
class_="SECTIONLEVEL",
|
||||
**text_style,
|
||||
)
|
||||
)
|
||||
if tag:
|
||||
self.svg.add(self.svg.text(tag, insert=(text_position[0], text_position[1] - 5), **text_style))
|
||||
|
||||
@@ -396,8 +403,7 @@ class SvgWriter:
|
||||
self.svg.line(start=start * self.svg_scale, end=end * self.svg_scale, class_=" ".join(classes))
|
||||
)
|
||||
|
||||
# BATTING ANNOTATIONS
|
||||
if predefined_type == "BATTING":
|
||||
def draw_batting_annotation():
|
||||
v0_global = matrix_world @ obj.data.vertices[0].co.xyz
|
||||
v1_global = matrix_world @ obj.data.vertices[1].co.xyz
|
||||
v0 = self.project_point_onto_camera(v0_global)
|
||||
@@ -463,7 +469,94 @@ class SvgWriter:
|
||||
)
|
||||
self.svg.add(self.svg.polyline(points=points, class_=" ".join(classes), style=polyline_style))
|
||||
|
||||
elif predefined_type == "SECTION":
|
||||
def draw_revision_cloud_annotation():
|
||||
segment_width = 15.0
|
||||
base_height = 1
|
||||
width = 5
|
||||
|
||||
def get_svg_half_circle(height, width):
|
||||
cp0 = f"0,-{height}"
|
||||
cp1 = f"{width},-{height}"
|
||||
end_point = f"{width},0"
|
||||
circle = f"c{cp0} {cp1} {end_point}"
|
||||
return circle
|
||||
|
||||
def get_revision_pattern(base_offset):
|
||||
pattern = f"m{base_offset.x},{base_offset.y}"
|
||||
pattern += " " + get_svg_half_circle(2 * base_height, width)
|
||||
pattern += " " + get_svg_half_circle(2.5 * base_height, width)
|
||||
pattern += " " + get_svg_half_circle(1.5 * base_height, width)
|
||||
return pattern
|
||||
|
||||
def get_scale(size, direction):
|
||||
vector = direction * size
|
||||
shrinked_vector = size // segment_width * segment_width * direction
|
||||
scale = [1 if vector[i] == 0 else vector[i] / shrinked_vector[i] for i in range(2)]
|
||||
return "scale(%f, %f)" % (scale[0], scale[1])
|
||||
|
||||
def poly_to_edges(poly):
|
||||
edges = []
|
||||
n_verts = len(poly)
|
||||
lats_index = n_verts - 1
|
||||
for i in range(len(poly)):
|
||||
edge = [poly[i], (poly[i + 1]) if i != lats_index else poly[0]]
|
||||
edges.append(edge)
|
||||
return edges
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
safe_offset_x = 2.0
|
||||
marker_width = segment_width + safe_offset_x * 2
|
||||
market_height = 15.0
|
||||
ref_y = 5.0
|
||||
revision_pattern = get_revision_pattern(Vector([safe_offset_x, ref_y]))
|
||||
|
||||
bm = tool.Blender.get_bmesh_for_mesh(obj.data).copy()
|
||||
bmesh.ops.contextual_create(bm, geom=bm.edges[:])
|
||||
faces = bm.faces[:]
|
||||
assert len(faces) == 1, "Revision cloud edges must form just 1 polygon"
|
||||
|
||||
# ensure clockwise order of polygon verts
|
||||
# given default blender counter-clockwise order
|
||||
polygon = faces[0]
|
||||
if polygon.normal.z > 0:
|
||||
polygon.normal_flip()
|
||||
|
||||
marker_id = f"revision-cloud-{element.GlobalId}"
|
||||
svg_path = self.svg.path(style="fill: none; stroke:red; stroke-width:0.20", d=revision_pattern)
|
||||
marker = self.svg.marker(
|
||||
markerUnits="userSpaceOnUse",
|
||||
insert=(safe_offset_x, ref_y),
|
||||
size=(marker_width, market_height),
|
||||
orient="auto",
|
||||
id=marker_id,
|
||||
)
|
||||
marker.add(svg_path)
|
||||
self.svg.add(marker)
|
||||
|
||||
for v0, v1 in poly_to_edges(polygon.verts):
|
||||
v0_global = matrix_world @ v0.co.xyz
|
||||
v1_global = matrix_world @ v1.co.xyz
|
||||
v0 = self.project_point_onto_camera(v0_global)
|
||||
v1 = self.project_point_onto_camera(v1_global)
|
||||
start_svg = Vector(((x_offset + v0.x), (y_offset - v0.y))) * self.svg_scale
|
||||
end_svg = Vector(((x_offset + v1.x), (y_offset - v1.y))) * self.svg_scale
|
||||
|
||||
pattern_edge = end_svg - start_svg
|
||||
pattern_dir = pattern_edge.normalized()
|
||||
pattern_length = pattern_edge.length
|
||||
|
||||
segments = int(pattern_length // segment_width)
|
||||
pattern_dir_step = pattern_dir * segment_width
|
||||
points = [pattern_dir_step * i for i in range(segments)]
|
||||
|
||||
polyline_style = f"marker: url(#{marker_id}); stroke: none;"
|
||||
polyline_transform = f"translate({start_svg.x}, {start_svg.y}) {get_scale(pattern_length, pattern_dir)}"
|
||||
polyline = self.svg.polyline(
|
||||
points=points, class_=" ".join(classes), style=polyline_style, transform=polyline_transform
|
||||
)
|
||||
self.svg.add(polyline)
|
||||
|
||||
def draw_section_annotation():
|
||||
display_data = DecoratorData.get_section_markers_display_data(obj)
|
||||
connect_markers = display_data["connect_markers"]
|
||||
|
||||
@@ -504,6 +597,12 @@ class SvgWriter:
|
||||
)
|
||||
)
|
||||
|
||||
if predefined_type == "BATTING":
|
||||
draw_batting_annotation()
|
||||
elif predefined_type == "REVISIONCLOUD":
|
||||
draw_revision_cloud_annotation()
|
||||
elif predefined_type == "SECTION":
|
||||
draw_section_annotation()
|
||||
else:
|
||||
for edge in obj.data.edges:
|
||||
draw_simple_edge_annotation(*edge.vertices[:])
|
||||
@@ -787,12 +886,12 @@ class SvgWriter:
|
||||
)
|
||||
)
|
||||
# TODO: allow metric to be configurable
|
||||
rl = (matrix_world @ points[0].co).z
|
||||
rl_value = (matrix_world @ points[0].co).z
|
||||
if bpy.context.scene.unit_settings.system == "IMPERIAL":
|
||||
rl = helper.format_distance(rl, precision=self.precision, decimal_places=self.decimal_places)
|
||||
rl = helper.format_distance(rl_value, precision=self.precision, decimal_places=self.decimal_places)
|
||||
else:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
rl /= unit_scale
|
||||
rl = rl_value / unit_scale
|
||||
rl = ifcopenshell.util.geolocation.auto_z2e(tool.Ifc.get(), rl)
|
||||
rl *= unit_scale
|
||||
rl = "{:.3f}m".format(rl)
|
||||
@@ -801,7 +900,7 @@ class SvgWriter:
|
||||
text_style = SvgWriter.get_box_alignment_parameters(box_alignment)
|
||||
self.svg.add(
|
||||
self.svg.text(
|
||||
"{}".format(rl),
|
||||
"{}{}".format("" if rl_value < 0 else "+", rl),
|
||||
insert=tuple(text_position),
|
||||
class_="PLANLEVEL",
|
||||
**text_style,
|
||||
|
||||
@@ -27,6 +27,7 @@ from blenderbim.bim.module.drawing.data import (
|
||||
DrawingsData,
|
||||
DecoratorData,
|
||||
)
|
||||
from blenderbim.bim.module.drawing.prop import ANNOTATION_TYPES_DATA
|
||||
|
||||
|
||||
class BIM_PT_camera(Panel):
|
||||
@@ -471,55 +472,55 @@ class BIM_PT_annotation_utilities(Panel):
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.add_annotation", text="Dimension", icon="FIXED_SIZE")
|
||||
op.object_type = "DIMENSION"
|
||||
op.data_type = "curve"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
op = row.operator("bim.add_annotation", text="Angle", icon="DRIVER_ROTATIONAL_DIFFERENCE")
|
||||
op.object_type = "ANGLE"
|
||||
op.data_type = "curve"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.add_annotation", text="Radius", icon="FORWARD")
|
||||
op.object_type = "RADIUS"
|
||||
op.data_type = "curve"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
op = row.operator("bim.add_annotation", text="Diameter", icon="ARROW_LEFTRIGHT")
|
||||
op.object_type = "DIAMETER"
|
||||
op.data_type = "curve"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.add_annotation", text="Text", icon="SMALL_CAPS")
|
||||
op.object_type = "TEXT"
|
||||
op.data_type = "empty"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
op = row.operator("bim.add_annotation", text="Leader", icon="TRACKING_BACKWARDS")
|
||||
op.object_type = "TEXT_LEADER"
|
||||
op.data_type = "curve"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.add_annotation", text="Stair Arrow", icon="SCREEN_BACK")
|
||||
op.object_type = "STAIR_ARROW"
|
||||
op.data_type = "curve"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
op = row.operator("bim.add_annotation", text="Hidden", icon="CON_TRACKTO")
|
||||
op.object_type = "HIDDEN_LINE"
|
||||
op.data_type = "mesh"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.add_annotation", text="Level (Plan)", icon="SORTBYEXT")
|
||||
op.object_type = "PLAN_LEVEL"
|
||||
op.data_type = "curve"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
op = row.operator("bim.add_annotation", text="Level (Section)", icon="TRIA_DOWN")
|
||||
op.object_type = "SECTION_LEVEL"
|
||||
op.data_type = "curve"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.add_annotation", text="Breakline", icon="FCURVE")
|
||||
op.object_type = "BREAKLINE"
|
||||
op.data_type = "mesh"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
op = row.operator("bim.add_annotation", text="Line", icon="MESH_MONKEY")
|
||||
op.object_type = "LINEWORK"
|
||||
op.data_type = "mesh"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.add_annotation", text="Batting", icon="FORCE_FORCE")
|
||||
op.object_type = "BATTING"
|
||||
op.data_type = "mesh"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
op.description = "Add batting annotation.\nThickness could be changed through Thickness property of BBIM_Batting property set"
|
||||
op = row.operator("bim.add_annotation", text="Fill Area", icon="NODE_TEXTURE")
|
||||
op.object_type = "FILL_AREA"
|
||||
@@ -527,7 +528,10 @@ class BIM_PT_annotation_utilities(Panel):
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.add_annotation", text="Fall", icon="SORT_ASC")
|
||||
op.object_type = "FALL"
|
||||
op.data_type = "curve"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
op = row.operator("bim.add_annotation", text="Revision", icon="VOLUME_DATA")
|
||||
op.object_type = "REVISION_CLOUD"
|
||||
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(self.props, "should_draw_decorations", text="Viewport Annotations")
|
||||
|
||||
@@ -28,7 +28,9 @@ classes = (
|
||||
operator.DiscardUncommitted,
|
||||
operator.DisplayRevision,
|
||||
operator.DisplayUncommitted,
|
||||
operator.Fetch,
|
||||
operator.Merge,
|
||||
operator.Push,
|
||||
operator.RefreshGit,
|
||||
operator.SwitchRevision,
|
||||
prop.IfcGitTag,
|
||||
|
||||
@@ -19,7 +19,10 @@ class IfcGitData:
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"repo": cls.repo(),
|
||||
"remotes": cls.remotes(),
|
||||
"branch_names": cls.branch_names(),
|
||||
"remote_names": cls.remote_names(),
|
||||
"remote_urls": cls.remote_urls(),
|
||||
"path_ifc": cls.path_ifc(),
|
||||
"branches_by_hexsha": cls.branches_by_hexsha(),
|
||||
"tags_by_hexsha": cls.tags_by_hexsha(),
|
||||
@@ -42,10 +45,28 @@ class IfcGitData:
|
||||
return tool.IfcGit.repo_from_path(path_ifc)
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def remotes(cls):
|
||||
if cls.repo():
|
||||
return cls.repo().remotes
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def branch_names(cls):
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def remote_names(cls):
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def remote_urls(cls):
|
||||
result = {}
|
||||
if cls.repo():
|
||||
for remote in cls.repo().remotes:
|
||||
result[remote.name] = remote.url
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def path_ifc(cls):
|
||||
path_ifc = tool.Ifc.get_path()
|
||||
|
||||
@@ -137,7 +137,7 @@ class CommitChanges(bpy.types.Operator):
|
||||
class AddTag(bpy.types.Operator):
|
||||
"""Tag selected revision"""
|
||||
|
||||
bl_label = "Add tag"
|
||||
bl_label = "Tag selected revision"
|
||||
bl_idname = "ifcgit.add_tag"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@@ -246,3 +246,35 @@ class Merge(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
else:
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
class Push(bpy.types.Operator):
|
||||
"""Pushes the working branch to selected remote"""
|
||||
|
||||
bl_label = "Push working branch"
|
||||
bl_idname = "ifcgit.push"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
props = context.scene.IfcGitProperties
|
||||
repo = IfcGitData.data["repo"]
|
||||
remote = repo.remotes[props.select_remote]
|
||||
remote.push(refspec=IfcGitData.data["repo"].active_branch.name)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class Fetch(bpy.types.Operator):
|
||||
"""Fetches from the selected remote"""
|
||||
|
||||
bl_label = "Fetch from remote"
|
||||
bl_idname = "ifcgit.fetch"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
props = context.scene.IfcGitProperties
|
||||
repo = IfcGitData.data["repo"]
|
||||
remote = repo.remotes[props.select_remote]
|
||||
remote.fetch()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -21,9 +21,25 @@ def git_branches(self, context):
|
||||
IfcGitData.data["branch_names"].remove("main")
|
||||
IfcGitData.data["branch_names"] = ["main"] + IfcGitData.data["branch_names"]
|
||||
|
||||
if IfcGitData.data["remotes"]:
|
||||
props = context.scene.IfcGitProperties
|
||||
IfcGitData.data["branch_names"] += [r.name for r in IfcGitData.data["remotes"][props.select_remote].refs]
|
||||
|
||||
return [(myname, myname, myname) for myname in IfcGitData.data["branch_names"]]
|
||||
|
||||
|
||||
def git_remotes(self, context):
|
||||
"""remotes enum"""
|
||||
|
||||
IfcGitData.data["remote_names"] = sorted([remote.name for remote in IfcGitData.data["remotes"]])
|
||||
|
||||
if "origin" in IfcGitData.data["remote_names"]:
|
||||
IfcGitData.data["remote_names"].remove("origin")
|
||||
IfcGitData.data["remote_names"] = ["origin"] + IfcGitData.data["remote_names"]
|
||||
|
||||
return [(myname, myname, myname) for myname in IfcGitData.data["remote_names"]]
|
||||
|
||||
|
||||
def update_revlist(self, context):
|
||||
"""wrapper to trigger update of the revision list"""
|
||||
|
||||
@@ -109,6 +125,7 @@ class IfcGitProperties(PropertyGroup):
|
||||
subtype="DIR_PATH",
|
||||
)
|
||||
display_branch: EnumProperty(items=git_branches, update=update_revlist)
|
||||
select_remote: EnumProperty(items=git_remotes)
|
||||
ifcgit_filter: EnumProperty(
|
||||
items=[
|
||||
("all", "All", "All revisions"),
|
||||
|
||||
@@ -154,13 +154,23 @@ class IFCGIT_PT_panel(bpy.types.Panel):
|
||||
# TODO
|
||||
# item.operator("ifcgit.delete_tag", icon="PANEL_CLOSE")
|
||||
|
||||
row = layout.row()
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.prop(props, "new_tag_name")
|
||||
row = layout.row()
|
||||
row = box.row()
|
||||
row.prop(props, "new_tag_message")
|
||||
row = layout.row()
|
||||
row = box.row()
|
||||
row.operator("ifcgit.add_tag", icon="GREASEPENCIL")
|
||||
|
||||
if IfcGitData.data["remotes"]:
|
||||
row = layout.row()
|
||||
row.prop(props, "select_remote", text="Select remote")
|
||||
urls = IfcGitData.data["remote_urls"]
|
||||
row.label(text=urls[props.select_remote])
|
||||
row = layout.row()
|
||||
row.operator("ifcgit.push", icon="EXPERIMENTAL")
|
||||
row.operator("ifcgit.fetch", icon="IMPORT")
|
||||
|
||||
|
||||
class COMMIT_UL_List(bpy.types.UIList):
|
||||
"""List of Git commits"""
|
||||
|
||||
@@ -31,13 +31,16 @@ classes = (
|
||||
operator.ContractMaterialCategory,
|
||||
operator.CopyMaterial,
|
||||
operator.DisableEditingAssignedMaterial,
|
||||
operator.DisableEditingMaterial,
|
||||
operator.DisableEditingMaterials,
|
||||
operator.DisableEditingMaterialSetItem,
|
||||
operator.DisableEditingMaterialSetItemProfile,
|
||||
operator.DisableEditingMaterials,
|
||||
operator.EditAssignedMaterial,
|
||||
operator.EditMaterial,
|
||||
operator.EditMaterialSetItem,
|
||||
operator.EditMaterialSetItemProfile,
|
||||
operator.EnableEditingAssignedMaterial,
|
||||
operator.EnableEditingMaterial,
|
||||
operator.EnableEditingMaterialSetItem,
|
||||
operator.EnableEditingMaterialSetItemProfile,
|
||||
operator.ExpandMaterialCategory,
|
||||
|
||||
@@ -96,6 +96,7 @@ class ObjectMaterialData:
|
||||
cls.data["total_thickness"] = cls.total_thickness()
|
||||
cls.data["materials"] = cls.materials()
|
||||
cls.data["type_material"] = cls.type_material()
|
||||
cls.data["material_type"] = cls.material_type()
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
@@ -244,3 +245,17 @@ class ObjectMaterialData:
|
||||
else:
|
||||
name_attr = "Name"
|
||||
return getattr(material, name_attr, "Unnamed") or "Unnamed"
|
||||
|
||||
@classmethod
|
||||
def material_type(cls):
|
||||
material_types = [
|
||||
"IfcMaterial",
|
||||
"IfcMaterialConstituentSet",
|
||||
"IfcMaterialLayerSet",
|
||||
"IfcMaterialProfileSet",
|
||||
"IfcMaterialList",
|
||||
]
|
||||
version = tool.Ifc.get_schema()
|
||||
if version == "IFC2X3":
|
||||
material_types = ["IfcMaterial", "IfcMaterialLayerSet", "IfcMaterialList"]
|
||||
return [(m, m, ifcopenshell.util.doc.get_entity_doc(version, m).get("description", "")) for m in material_types]
|
||||
|
||||
@@ -59,6 +59,36 @@ class SelectByMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.select_by_material(tool.Material, material=tool.Ifc.get().by_id(self.material))
|
||||
|
||||
|
||||
class EnableEditingMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_material"
|
||||
bl_label = "Enable Editing Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
material: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.enable_editing_material(tool.Material, material=tool.Ifc.get().by_id(self.material))
|
||||
|
||||
|
||||
class EditMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_material"
|
||||
bl_label = "Edit Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
material: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.edit_material(tool.Ifc, tool.Material, material=tool.Ifc.get().by_id(self.material))
|
||||
|
||||
|
||||
class DisableEditingMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_material"
|
||||
bl_label = "Disable Editing Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
material: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.disable_editing_material(tool.Material)
|
||||
|
||||
|
||||
class AssignParameterizedProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_parameterized_profile"
|
||||
bl_label = "Assign Parameterized Profile"
|
||||
@@ -159,14 +189,22 @@ class AssignMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
material=material,
|
||||
)
|
||||
assigned_material = ifcopenshell.util.element.get_material(element)
|
||||
if assigned_material.is_a("IfcMaterialLayerSet"):
|
||||
if assigned_material.is_a("IfcMaterialConstituentSet"):
|
||||
if not assigned_material.MaterialConstituents:
|
||||
ifcopenshell.api.run(
|
||||
"material.add_constituent",
|
||||
tool.Ifc.get(),
|
||||
constituent_set=assigned_material,
|
||||
material=material,
|
||||
)
|
||||
elif assigned_material.is_a() == "IfcMaterialLayerSet":
|
||||
if not assigned_material.MaterialLayers:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
layer = ifcopenshell.api.run(
|
||||
"material.add_layer",
|
||||
tool.Ifc.get(),
|
||||
layer_set=assigned_material,
|
||||
material=tool.Ifc.get().by_type("IfcMaterial")[0],
|
||||
material=material,
|
||||
)
|
||||
thickness = 0.1 # Arbitrary metric thickness for now
|
||||
layer.LayerThickness = thickness / unit_scale
|
||||
@@ -243,12 +281,12 @@ class RemoveConstituent(bpy.types.Operator, tool.Ifc.Operator):
|
||||
constituent: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
for inverse in tool.Ifc.get().get_inverse(tool.Ifc.get().by_id(self.constituent)):
|
||||
if inverse.is_a("IfcMaterialConstituentSet") and len(inverse.MaterialConstituents) == 1:
|
||||
return
|
||||
ifcopenshell.api.run(
|
||||
"material.remove_constituent", tool.Ifc.get(), constituent=tool.Ifc.get().by_id(self.constituent)
|
||||
)
|
||||
constituent = tool.Ifc.get().by_id(self.constituent)
|
||||
for material_set in layer.ToMaterialConstituentSet:
|
||||
if len(material_set.MaterialConstituents) == 1:
|
||||
self.report({"ERROR"}, "At least one constituent must exist")
|
||||
return {"ERROR"}
|
||||
ifcopenshell.api.run("material.remove_constituent", tool.Ifc.get(), constituent=constituent)
|
||||
|
||||
|
||||
class AddProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -278,10 +316,12 @@ class RemoveProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
profile: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
for inverse in tool.Ifc.get().get_inverse(tool.Ifc.get().by_id(self.profile)):
|
||||
if inverse.is_a("IfcMaterialProfileSet") and len(inverse.MaterialProfiles) == 1:
|
||||
return
|
||||
ifcopenshell.api.run("material.remove_profile", tool.Ifc.get(), profile=tool.Ifc.get().by_id(self.profile))
|
||||
profile = tool.Ifc.get().by_id(self.profile)
|
||||
for material_set in profile.ToMaterialProfileSet:
|
||||
if len(material_set.MaterialProfiles) == 1:
|
||||
self.report({"ERROR"}, "At least one profile must exist")
|
||||
return {"ERROR"}
|
||||
ifcopenshell.api.run("material.remove_profile", tool.Ifc.get(), profile=profile)
|
||||
|
||||
|
||||
class AddLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -294,7 +334,7 @@ class AddLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
layer = ifcopenshell.api.run(
|
||||
"material.add_layer",
|
||||
self.file,
|
||||
**{
|
||||
@@ -303,6 +343,10 @@ class AddLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
},
|
||||
)
|
||||
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
thickness = 0.1 # Arbitrary metric thickness for now
|
||||
layer.LayerThickness = thickness / unit_scale
|
||||
|
||||
|
||||
class ReorderMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.reorder_material_set_item"
|
||||
@@ -336,10 +380,12 @@ class RemoveLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
layer: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
for inverse in tool.Ifc.get().get_inverse(tool.Ifc.get().by_id(self.layer)):
|
||||
if inverse.is_a("IfcMaterialLayerSet") and len(inverse.MaterialLayers) == 1:
|
||||
return
|
||||
ifcopenshell.api.run("material.remove_layer", tool.Ifc.get(), layer=tool.Ifc.get().by_id(self.layer))
|
||||
layer = tool.Ifc.get().by_id(self.layer)
|
||||
for material_set in layer.ToMaterialLayerSet:
|
||||
if len(material_set.MaterialLayers) == 1:
|
||||
self.report({"ERROR"}, "At least one layer must exist")
|
||||
return {"ERROR"}
|
||||
ifcopenshell.api.run("material.remove_layer", tool.Ifc.get(), layer=layer)
|
||||
|
||||
|
||||
class AddListItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@@ -85,24 +85,10 @@ def get_materials(self, context):
|
||||
return ObjectMaterialData.data["materials"]
|
||||
|
||||
|
||||
def get_object_material_types(self, context):
|
||||
global materialtypes_enum
|
||||
if len(materialtypes_enum) == 0 and IfcStore.get_file():
|
||||
material_types = [
|
||||
"IfcMaterial",
|
||||
"IfcMaterialConstituentSet",
|
||||
"IfcMaterialLayerSet",
|
||||
"IfcMaterialLayerSetUsage",
|
||||
"IfcMaterialProfileSet",
|
||||
"IfcMaterialProfileSetUsage",
|
||||
"IfcMaterialList",
|
||||
]
|
||||
version = tool.Ifc.get_schema()
|
||||
if version == "IFC2X3":
|
||||
material_types = ["IfcMaterial", "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage", "IfcMaterialList"]
|
||||
materialtypes_enum.clear()
|
||||
materialtypes_enum = [(m, m, get_entity_doc(version, m).get("description", "")) for m in material_types]
|
||||
return materialtypes_enum
|
||||
def get_object_material_type(self, context):
|
||||
if not ObjectMaterialData.is_loaded:
|
||||
ObjectMaterialData.load()
|
||||
return ObjectMaterialData.data["material_type"]
|
||||
|
||||
|
||||
def get_material_types(self, context):
|
||||
@@ -131,10 +117,13 @@ class BIMMaterialProperties(PropertyGroup):
|
||||
materials: CollectionProperty(name="Materials", type=Material)
|
||||
active_material_index: IntProperty(name="Active Material Index")
|
||||
profiles: EnumProperty(items=get_profiles, name="Profiles")
|
||||
active_material_id: IntProperty(name="Active Material ID")
|
||||
material_attributes: CollectionProperty(name="Material Attributes", type=Attribute)
|
||||
editing_material_type = StringProperty(name="Editing Material Type")
|
||||
|
||||
|
||||
class BIMObjectMaterialProperties(PropertyGroup):
|
||||
material_type: EnumProperty(items=get_object_material_types, name="Material Type")
|
||||
material_type: EnumProperty(items=get_object_material_type, name="Material Type")
|
||||
material: EnumProperty(items=get_materials, name="Material")
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
material_set_usage_attributes: CollectionProperty(name="Material Set Usage Attributes", type=Attribute)
|
||||
|
||||
@@ -57,13 +57,21 @@ class BIM_PT_materials(Panel):
|
||||
row.alignment = "RIGHT"
|
||||
|
||||
if self.props.material_type == "IfcMaterial":
|
||||
row.operator("bim.add_material", text="", icon="ADD")
|
||||
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||
material = self.props.materials[self.props.active_material_index]
|
||||
if material.ifc_definition_id:
|
||||
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.material = material.ifc_definition_id
|
||||
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
|
||||
if self.props.active_material_id:
|
||||
row.operator("bim.edit_material", text="", icon="CHECKMARK").material = material.ifc_definition_id
|
||||
row.operator("bim.disable_editing_material", text="", icon="CANCEL").material = material.ifc_definition_id
|
||||
self.draw_editable_material_attributes_ui()
|
||||
else:
|
||||
row.operator("bim.add_material", text="", icon="ADD")
|
||||
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.material = material.ifc_definition_id
|
||||
row.operator("bim.enable_editing_material", text="", icon="GREASEPENCIL").material = material.ifc_definition_id
|
||||
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
|
||||
else:
|
||||
row.operator("bim.add_material", text="", icon="ADD")
|
||||
else:
|
||||
row.operator("bim.add_material_set", text="", icon="ADD").set_type = self.props.material_type
|
||||
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||
@@ -76,6 +84,9 @@ class BIM_PT_materials(Panel):
|
||||
self.layout.template_list("BIM_UL_materials", "", self.props, "materials", self.props, "active_material_index")
|
||||
|
||||
|
||||
def draw_editable_material_attributes_ui(self):
|
||||
blenderbim.bim.helper.draw_attributes(self.props.material_attributes, self.layout)
|
||||
|
||||
class BIM_PT_material(Panel):
|
||||
bl_label = "IFC Material"
|
||||
bl_idname = "BIM_PT_material"
|
||||
@@ -144,8 +155,7 @@ class BIM_PT_object_material(Panel):
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "material_type", text="")
|
||||
if self.props.material_type == "IfcMaterial" or self.props.material_type == "IfcMaterialList":
|
||||
prop_with_search(row, self.props, "material", text="")
|
||||
prop_with_search(row, self.props, "material", text="")
|
||||
row.operator("bim.assign_material", icon="ADD", text="")
|
||||
|
||||
def draw_material_ui(self):
|
||||
|
||||
@@ -126,7 +126,6 @@ classes = (
|
||||
pie.OpenPieClass,
|
||||
pie.PieUpdateContainer,
|
||||
pie.PieAddOpening,
|
||||
pie.PieAssignObjectAggregation,
|
||||
pie.VIEW3D_MT_PIE_bim,
|
||||
pie.VIEW3D_MT_PIE_bim_class,
|
||||
sverchok_modifier.CreateNewSverchokGraph,
|
||||
|
||||
@@ -86,9 +86,10 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
|
||||
props = obj.BIMArrayProperties
|
||||
props.count = data["count"]
|
||||
props.x = data["x"]
|
||||
props.y = data["y"]
|
||||
props.z = data["z"]
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
props.x = data["x"] * si_conversion
|
||||
props.y = data["y"] * si_conversion
|
||||
props.z = data["z"] * si_conversion
|
||||
props.use_local_space = data.get("use_local_space", False)
|
||||
props.sync_children = data.get("sync_children", False)
|
||||
props.method = data.get("method", "OFFSET")
|
||||
@@ -106,15 +107,16 @@ class EditArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMArrayProperties
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
data = json.loads(pset["Data"])
|
||||
data[self.item] = {
|
||||
"children": data[self.item]["children"],
|
||||
"count": props.count,
|
||||
"x": props.x,
|
||||
"y": props.y,
|
||||
"z": props.z,
|
||||
"x": props.x / si_conversion,
|
||||
"y": props.y / si_conversion,
|
||||
"z": props.z / si_conversion,
|
||||
"use_local_space": props.use_local_space,
|
||||
"sync_children": props.sync_children,
|
||||
"method": props.method,
|
||||
@@ -187,6 +189,7 @@ class SelectArrayParent(bpy.types.Operator):
|
||||
obj.select_set(True)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class Input3DCursorXArray(bpy.types.Operator):
|
||||
bl_idname = "bim.input_cursor_x_array"
|
||||
bl_label = "Get 3d Cursor X Input for Array"
|
||||
@@ -202,6 +205,7 @@ class Input3DCursorXArray(bpy.types.Operator):
|
||||
props.x = cursor.location.x - obj.location.x
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class Input3DCursorYArray(bpy.types.Operator):
|
||||
bl_idname = "bim.input_cursor_y_array"
|
||||
bl_label = "Get 3d Cursor Y Input for Array"
|
||||
@@ -217,6 +221,7 @@ class Input3DCursorYArray(bpy.types.Operator):
|
||||
props.y = cursor.location.y - obj.location.y
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class Input3DCursorZArray(bpy.types.Operator):
|
||||
bl_idname = "bim.input_cursor_z_array"
|
||||
bl_label = "Get 3d Cursor Z Input for Array"
|
||||
|
||||
@@ -76,22 +76,6 @@ class PieUpdateContainer(bpy.types.Operator):
|
||||
break
|
||||
return {"FINISHED"}
|
||||
|
||||
class PieAssignObjectAggregation(bpy.types.Operator,tool.Ifc.Operator):
|
||||
bl_idname = "bim.pie_assign_object_aggregation"
|
||||
bl_label = "Assign Parts to Active Object"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
if obj == context.active_object:
|
||||
continue
|
||||
blenderbim.core.aggregate.assign_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
relating_obj=context.active_object,
|
||||
related_obj=obj,
|
||||
)
|
||||
|
||||
class VIEW3D_MT_PIE_bim(bpy.types.Menu):
|
||||
bl_label = "Geometry"
|
||||
@@ -103,7 +87,8 @@ class VIEW3D_MT_PIE_bim(bpy.types.Menu):
|
||||
pie.operator("bim.pie_add_opening")
|
||||
pie.operator("bim.pie_update_container")
|
||||
pie.operator("bim.open_pie_class", text="Assign IFC Class")
|
||||
pie.operator("bim.pie_assign_object_aggregation", text ="Assign Aggregation")
|
||||
pie.operator("bim.assign_object", text ="Assign Aggregation")
|
||||
pie.operator("bim.unassign_object", text ="Unassign Aggregation")
|
||||
|
||||
|
||||
class VIEW3D_MT_PIE_bim_class(bpy.types.Menu):
|
||||
|
||||
@@ -57,7 +57,7 @@ def get_type_predefined_type(self, context):
|
||||
|
||||
def update_ifc_class(self, context):
|
||||
bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class)
|
||||
AuthoringData.data["relating_type_id"] = AuthoringData.relating_types()
|
||||
AuthoringData.data["relating_type_id"] = AuthoringData.relating_type_id()
|
||||
AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail()
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ def update_type_page(self, context):
|
||||
class BIMModelProperties(PropertyGroup):
|
||||
ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="Construction Class", update=update_ifc_class)
|
||||
relating_type_id: bpy.props.EnumProperty(
|
||||
items=get_relating_type_id, name="Construction Type", update=update_relating_type_id
|
||||
items=get_relating_type_id, name="Relating Type", update=update_relating_type_id
|
||||
)
|
||||
icon_id: bpy.props.IntProperty()
|
||||
updating: bpy.props.BoolProperty(default=False)
|
||||
@@ -155,9 +155,9 @@ class BIMArrayProperties(PropertyGroup):
|
||||
default=-1, description="Currently edited array index. -1 if not in array editing mode."
|
||||
)
|
||||
count: bpy.props.IntProperty(name="Count", default=0, min=0)
|
||||
x: bpy.props.FloatProperty(name="X", default=0)
|
||||
y: bpy.props.FloatProperty(name="Y", default=0)
|
||||
z: bpy.props.FloatProperty(name="Z", default=0)
|
||||
x: bpy.props.FloatProperty(name="X", default=0, subtype="DISTANCE")
|
||||
y: bpy.props.FloatProperty(name="Y", default=0, subtype="DISTANCE")
|
||||
z: bpy.props.FloatProperty(name="Z", default=0, subtype="DISTANCE")
|
||||
use_local_space: bpy.props.BoolProperty(
|
||||
name="Use Local Space",
|
||||
description="Use local space for array items offset instead of world space",
|
||||
@@ -176,6 +176,7 @@ class BIMArrayProperties(PropertyGroup):
|
||||
|
||||
|
||||
class BIMStairProperties(PropertyGroup):
|
||||
non_si_units_props = ("is_editing", "number_of_treads", "has_top_nib", "stair_type")
|
||||
stair_types = (
|
||||
("CONCRETE", "Concrete", ""),
|
||||
("WOOD/STEEL", "Wood / Steel", ""),
|
||||
@@ -184,17 +185,17 @@ class BIMStairProperties(PropertyGroup):
|
||||
|
||||
stair_added_previously: bpy.props.BoolProperty(default=False)
|
||||
is_editing: bpy.props.IntProperty(default=-1)
|
||||
width: bpy.props.FloatProperty(name="Width", default=1.2, soft_min=0.01)
|
||||
height: bpy.props.FloatProperty(name="Height", default=1.0, soft_min=0.01)
|
||||
width: bpy.props.FloatProperty(name="Width", default=1.2, soft_min=0.01, subtype="DISTANCE")
|
||||
height: bpy.props.FloatProperty(name="Height", default=1.0, soft_min=0.01, subtype="DISTANCE")
|
||||
number_of_treads: bpy.props.IntProperty(name="Number of treads", default=6, soft_min=1)
|
||||
tread_depth: bpy.props.FloatProperty(name="Tread Depth", default=0.25, soft_min=0.01)
|
||||
tread_run: bpy.props.FloatProperty(name="Tread Run", default=0.3, soft_min=0.01)
|
||||
base_slab_depth: bpy.props.FloatProperty(name="Base slab depth", default=0.25, soft_min=0)
|
||||
top_slab_depth: bpy.props.FloatProperty(name="Top slab depth", default=0.25, soft_min=0)
|
||||
tread_depth: bpy.props.FloatProperty(name="Tread Depth", default=0.25, soft_min=0.01, subtype="DISTANCE")
|
||||
tread_run: bpy.props.FloatProperty(name="Tread Run", default=0.3, soft_min=0.01, subtype="DISTANCE")
|
||||
base_slab_depth: bpy.props.FloatProperty(name="Base slab depth", default=0.25, soft_min=0, subtype="DISTANCE")
|
||||
top_slab_depth: bpy.props.FloatProperty(name="Top slab depth", default=0.25, soft_min=0, subtype="DISTANCE")
|
||||
has_top_nib: bpy.props.BoolProperty(name="Has top nib", default=True)
|
||||
stair_type: bpy.props.EnumProperty(name="Stair type", items=stair_types, default="CONCRETE")
|
||||
|
||||
def get_props_kwargs(self):
|
||||
def get_props_kwargs(self, convert_to_project_units=False):
|
||||
stair_kwargs = {
|
||||
"stair_type": self.stair_type,
|
||||
"width": self.width,
|
||||
@@ -204,27 +205,34 @@ class BIMStairProperties(PropertyGroup):
|
||||
}
|
||||
|
||||
if self.stair_type == "CONCRETE":
|
||||
stair_kwargs.update(
|
||||
{
|
||||
"base_slab_depth": self.base_slab_depth,
|
||||
"top_slab_depth": self.top_slab_depth,
|
||||
"has_top_nib": self.has_top_nib,
|
||||
"tread_depth": self.tread_depth,
|
||||
}
|
||||
)
|
||||
return stair_kwargs
|
||||
concrete_props = {
|
||||
"base_slab_depth": self.base_slab_depth,
|
||||
"top_slab_depth": self.top_slab_depth,
|
||||
"has_top_nib": self.has_top_nib,
|
||||
"tread_depth": self.tread_depth,
|
||||
}
|
||||
stair_kwargs.update(concrete_props)
|
||||
|
||||
elif self.stair_type == "WOOD/STEEL":
|
||||
stair_kwargs.update(
|
||||
{
|
||||
"tread_depth": self.tread_depth,
|
||||
}
|
||||
)
|
||||
return stair_kwargs
|
||||
wood_steel_props = {
|
||||
"tread_depth": self.tread_depth,
|
||||
}
|
||||
stair_kwargs.update(wood_steel_props)
|
||||
|
||||
elif self.stair_type == "GENERIC":
|
||||
pass
|
||||
|
||||
if not convert_to_project_units:
|
||||
return stair_kwargs
|
||||
|
||||
stair_kwargs = tool.Model.convert_data_to_project_units(stair_kwargs, self.non_si_units_props)
|
||||
return stair_kwargs
|
||||
|
||||
def set_props_kwargs_from_ifc_data(self, kwargs):
|
||||
kwargs = tool.Model.convert_data_to_si_units(kwargs, self.non_si_units_props)
|
||||
for prop_name in kwargs:
|
||||
setattr(self, prop_name, kwargs[prop_name])
|
||||
|
||||
|
||||
class BIMSverchokProperties(PropertyGroup):
|
||||
node_group: bpy.props.PointerProperty(name="Node Group", type=NodeTree)
|
||||
@@ -232,15 +240,13 @@ class BIMSverchokProperties(PropertyGroup):
|
||||
|
||||
def window_type_prop_update(self, context):
|
||||
number_of_panels, panels_data = self.window_types_panels[self.window_type]
|
||||
|
||||
si_coversion = 1.0 / ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
panels_data = [[v * si_coversion for v in data] for data in panels_data]
|
||||
self.first_mullion_offset, self.second_mullion_offset = panels_data[0]
|
||||
self.first_transom_offset, self.second_transom_offset = panels_data[1]
|
||||
|
||||
|
||||
# default prop values are in mm and converted later
|
||||
class BIMWindowProperties(PropertyGroup):
|
||||
non_si_units_props = ("is_editing", "window_type", "window_added_previously")
|
||||
window_types = (
|
||||
("SINGLE_PANEL", "SINGLE_PANEL", ""),
|
||||
("DOUBLE_PANEL_HORIZONTAL", "DOUBLE_PANEL_HORIZONTAL", ""),
|
||||
@@ -273,50 +279,63 @@ class BIMWindowProperties(PropertyGroup):
|
||||
window_type: bpy.props.EnumProperty(
|
||||
name="Window Type", items=window_types, default="SINGLE_PANEL", update=window_type_prop_update
|
||||
)
|
||||
overall_height: bpy.props.FloatProperty(name="Overall Height", default=0.9)
|
||||
overall_width: bpy.props.FloatProperty(name="Overall Width", default=0.6)
|
||||
overall_height: bpy.props.FloatProperty(name="Overall Height", default=0.9, subtype="DISTANCE")
|
||||
overall_width: bpy.props.FloatProperty(name="Overall Width", default=0.6, subtype="DISTANCE")
|
||||
|
||||
# lining properties
|
||||
lining_depth: bpy.props.FloatProperty(name="Lining Depth", default=0.050)
|
||||
lining_thickness: bpy.props.FloatProperty(name="Lining Thickness", default=0.050)
|
||||
lining_offset: bpy.props.FloatProperty(name="Lining Offset", default=0.050)
|
||||
lining_to_panel_offset_x: bpy.props.FloatProperty(name="Lining to Panel Offset X", default=0.025)
|
||||
lining_to_panel_offset_y: bpy.props.FloatProperty(name="Lining to Panel Offset Y", default=0.025)
|
||||
mullion_thickness: bpy.props.FloatProperty(name="Mullion Thickness", default=0.050)
|
||||
lining_depth: bpy.props.FloatProperty(name="Lining Depth", default=0.050, subtype="DISTANCE")
|
||||
lining_thickness: bpy.props.FloatProperty(name="Lining Thickness", default=0.050, subtype="DISTANCE")
|
||||
lining_offset: bpy.props.FloatProperty(name="Lining Offset", default=0.050, subtype="DISTANCE")
|
||||
lining_to_panel_offset_x: bpy.props.FloatProperty(
|
||||
name="Lining to Panel Offset X", default=0.025, subtype="DISTANCE"
|
||||
)
|
||||
lining_to_panel_offset_y: bpy.props.FloatProperty(
|
||||
name="Lining to Panel Offset Y", default=0.025, subtype="DISTANCE"
|
||||
)
|
||||
mullion_thickness: bpy.props.FloatProperty(name="Mullion Thickness", default=0.050, subtype="DISTANCE")
|
||||
first_mullion_offset: bpy.props.FloatProperty(
|
||||
name="First Mullion Offset",
|
||||
description="Distance from the first lining to the first mullion center",
|
||||
default=0.3,
|
||||
subtype="DISTANCE",
|
||||
)
|
||||
second_mullion_offset: bpy.props.FloatProperty(
|
||||
name="Second Mullion Offset",
|
||||
description="Distance from the first lining to the second mullion center",
|
||||
default=0.45,
|
||||
subtype="DISTANCE",
|
||||
)
|
||||
transom_thickness: bpy.props.FloatProperty(name="Transom Thickness", default=0.050)
|
||||
transom_thickness: bpy.props.FloatProperty(name="Transom Thickness", default=0.050, subtype="DISTANCE")
|
||||
first_transom_offset: bpy.props.FloatProperty(
|
||||
name="First Transom Offset",
|
||||
description="Distance from the first lining to the first transom center",
|
||||
default=0.3,
|
||||
subtype="DISTANCE",
|
||||
)
|
||||
second_transom_offset: bpy.props.FloatProperty(
|
||||
name="Second Transom Offset",
|
||||
description="Distance from the first lining to the second transom center",
|
||||
default=0.6,
|
||||
subtype="DISTANCE",
|
||||
)
|
||||
|
||||
# panel properties
|
||||
frame_depth: bpy.props.FloatVectorProperty(name="Frame Depth", size=3, default=[0.035] * 3)
|
||||
frame_thickness: bpy.props.FloatVectorProperty(name="Frame Thickness", size=3, default=[0.035] * 3)
|
||||
frame_depth: bpy.props.FloatVectorProperty(name="Frame Depth", size=3, default=[0.035] * 3, subtype="TRANSLATION")
|
||||
frame_thickness: bpy.props.FloatVectorProperty(
|
||||
name="Frame Thickness", size=3, default=[0.035] * 3, subtype="TRANSLATION"
|
||||
)
|
||||
|
||||
def get_general_kwargs(self):
|
||||
return {
|
||||
def get_general_kwargs(self, convert_to_project_units=False):
|
||||
kwargs = {
|
||||
"window_type": self.window_type,
|
||||
"overall_height": self.overall_height,
|
||||
"overall_width": self.overall_width,
|
||||
}
|
||||
if not convert_to_project_units:
|
||||
return kwargs
|
||||
return tool.Model.convert_data_to_project_units(kwargs, ["window_type"])
|
||||
|
||||
def get_lining_kwargs(self):
|
||||
def get_lining_kwargs(self, convert_to_project_units=False):
|
||||
kwargs = {
|
||||
"lining_depth": self.lining_depth,
|
||||
"lining_thickness": self.lining_thickness,
|
||||
@@ -353,13 +372,23 @@ class BIMWindowProperties(PropertyGroup):
|
||||
if self.window_type in ("TRIPLE_PANEL_HORIZONTAL",):
|
||||
kwargs["second_transom_offset"] = self.second_transom_offset
|
||||
|
||||
return kwargs
|
||||
if not convert_to_project_units:
|
||||
return kwargs
|
||||
return tool.Model.convert_data_to_project_units(kwargs)
|
||||
|
||||
def get_panel_kwargs(self):
|
||||
return {
|
||||
def get_panel_kwargs(self, convert_to_project_units=False):
|
||||
kwargs = {
|
||||
"frame_depth": self.frame_depth,
|
||||
"frame_thickness": self.frame_thickness,
|
||||
}
|
||||
if not convert_to_project_units:
|
||||
return kwargs
|
||||
return tool.Model.convert_data_to_project_units(kwargs)
|
||||
|
||||
def set_props_kwargs_from_ifc_data(self, kwargs):
|
||||
kwargs = tool.Model.convert_data_to_si_units(kwargs, self.non_si_units_props)
|
||||
for prop_name in kwargs:
|
||||
setattr(self, prop_name, kwargs[prop_name])
|
||||
|
||||
|
||||
class BIMDoorProperties(PropertyGroup):
|
||||
|
||||
@@ -87,11 +87,29 @@ class GenerateHippedRoof(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"CANCELLED"}
|
||||
|
||||
bm = tool.Blender.get_bmesh_for_mesh(obj.data)
|
||||
op_status, error_message = is_valid_roof_footprint(bm)
|
||||
if error_message:
|
||||
self.report(op_status, error_message)
|
||||
return {"CANCELLED"}
|
||||
|
||||
generate_hiped_roof_bmesh(bm, self.mode, self.height, self.angle)
|
||||
tool.Blender.apply_bmesh(obj.data, bm)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def is_valid_roof_footprint(bm):
|
||||
# should be bmesh to support edit mode
|
||||
bm.verts.ensure_lookup_table()
|
||||
base_z = bm.verts[0].co.z
|
||||
all_verts_same_level = all([float_is_zero(v.co.z - base_z) for v in bm.verts[1:]])
|
||||
if not all_verts_same_level:
|
||||
return (
|
||||
{"ERROR"},
|
||||
"\nAll roof footprint vertices should have same Z-level.\nCurrently Z-level doesn't completely match",
|
||||
)
|
||||
return ({"FINISHED"}, "")
|
||||
|
||||
|
||||
def generate_hiped_roof_bmesh(bm, mode="ANGLE", height=1.0, angle=pi / 18, mutate_current_bmesh=True):
|
||||
"""return bmesh with gable roof geometry
|
||||
|
||||
@@ -584,6 +602,11 @@ class EnableEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
|
||||
# copying to make sure not to mutate the edit mode bmesh
|
||||
bm = tool.Blender.get_bmesh_for_mesh(obj.data)
|
||||
main_bm = bm.copy()
|
||||
op_status, error_message = is_valid_roof_footprint(main_bm)
|
||||
if error_message:
|
||||
print("Error: %s" % error_message)
|
||||
return main_bm
|
||||
|
||||
main_bm.edges.layers.int.new("BBIM_preview")
|
||||
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
@@ -637,6 +660,12 @@ class FinishEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMRoofProperties
|
||||
|
||||
bm = tool.Blender.get_bmesh_for_mesh(obj.data)
|
||||
op_status, error_message = is_valid_roof_footprint(bm)
|
||||
if error_message:
|
||||
self.report(op_status, error_message)
|
||||
return {"CANCELLED"}
|
||||
|
||||
roof_data = props.get_general_kwargs()
|
||||
path_data = get_path_data(obj)
|
||||
roof_data["path_data"] = path_data
|
||||
@@ -646,6 +675,7 @@ class FinishEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
|
||||
update_bbim_roof_pset(element, roof_data)
|
||||
refresh() # RoofData has to be updated before run update_roof_modifier_bmesh
|
||||
update_roof_modifier_bmesh(context)
|
||||
|
||||
update_roof_modifier_ifc_data(context)
|
||||
if bpy.context.object.mode == "EDIT":
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
@@ -252,9 +252,17 @@ class DumbSlabPlaner:
|
||||
new_material = ifcopenshell.util.element.get_material(settings["relating_type"])
|
||||
if not new_material or not new_material.is_a("IfcMaterialLayerSet"):
|
||||
return
|
||||
parametric = ifcopenshell.util.element.get_psets(settings["relating_type"]).get("EPset_Parametric")
|
||||
layer_set_direction = None
|
||||
if parametric:
|
||||
layer_set_direction = parametric.get("LayerSetDirection", layer_set_direction)
|
||||
new_thickness = sum([l.LayerThickness for l in new_material.MaterialLayers])
|
||||
material = ifcopenshell.util.element.get_material(settings["related_object"])
|
||||
if material and material.is_a("IfcMaterialLayerSetUsage") and material.LayerSetDirection == "AXIS3":
|
||||
if not material or not material.is_a("IfcMaterialLayerSetUsage"):
|
||||
return
|
||||
if layer_set_direction:
|
||||
material.LayerSetDirection = layer_set_direction
|
||||
if material.LayerSetDirection == "AXIS3":
|
||||
self.change_thickness(settings["related_object"], new_thickness)
|
||||
|
||||
def change_thickness(self, element, thickness):
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import FloatProperty, IntProperty, BoolProperty
|
||||
from bpy.props import FloatProperty, IntProperty
|
||||
from bpy_extras.object_utils import AddObjectHelper, object_data_add
|
||||
|
||||
import bmesh
|
||||
@@ -28,7 +28,6 @@ import ifcopenshell
|
||||
from ifcopenshell.util.shape_builder import V, ShapeBuilder
|
||||
import blenderbim
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.helper import convert_property_group_from_si
|
||||
|
||||
from mathutils import Vector
|
||||
from pprint import pprint
|
||||
@@ -214,14 +213,6 @@ def generate_stair_2d_profile(
|
||||
def update_stair_modifier(context):
|
||||
obj = context.active_object
|
||||
props_kwargs = obj.BIMStairProperties.get_props_kwargs()
|
||||
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
for prop_name in props_kwargs:
|
||||
if prop_name in ("is_editing", "number_of_treads", "has_top_nib", "stair_type"):
|
||||
continue
|
||||
prop_value = props_kwargs[prop_name]
|
||||
props_kwargs[prop_name] = prop_value * si_conversion
|
||||
|
||||
vertices, edges, faces = generate_stair_2d_profile(**props_kwargs)
|
||||
|
||||
obj = context.object
|
||||
@@ -360,14 +351,7 @@ class AddStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.report({"ERROR"}, "Object has to be IfcStairFlight/IfcStairFlightType to add a stair.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
# need to make sure all default props will have correct units
|
||||
if not props.stair_added_previously:
|
||||
convert_property_group_from_si(
|
||||
props,
|
||||
skip_props=("stair_added_previously", "is_editing", "number_of_treads", "has_top_nib", "stair_type"),
|
||||
)
|
||||
|
||||
stair_data = props.get_props_kwargs()
|
||||
stair_data = props.get_props_kwargs(convert_to_project_units=True)
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Stair")
|
||||
if not pset:
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc_file, product=element, name="BBIM_Stair")
|
||||
@@ -393,8 +377,7 @@ class CancelEditingStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Stair", "Data"))
|
||||
props = obj.BIMStairProperties
|
||||
# restore previous settings since editing was canceled
|
||||
for prop_name in data:
|
||||
setattr(props, prop_name, data[prop_name])
|
||||
props.set_props_kwargs_from_ifc_data(data)
|
||||
update_stair_modifier(context)
|
||||
|
||||
props.is_editing = -1
|
||||
@@ -412,7 +395,7 @@ class FinishEditingStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMStairProperties
|
||||
|
||||
data = props.get_props_kwargs()
|
||||
data = props.get_props_kwargs(convert_to_project_units=True)
|
||||
props.is_editing = -1
|
||||
update_stair_modifier(context)
|
||||
|
||||
@@ -436,15 +419,7 @@ class EnableEditingStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Stair", "Data"))
|
||||
# required since we could load pset from .ifc and BIMStairProperties won't be set
|
||||
for prop_name in data:
|
||||
setattr(props, prop_name, data[prop_name])
|
||||
|
||||
# need to make sure all props that weren't used before
|
||||
# will have correct units
|
||||
skip_props = ("stair_added_previously", "is_editing", "number_of_treads", "has_top_nib", "stair_type")
|
||||
skip_props += tuple(data.keys())
|
||||
convert_property_group_from_si(props, skip_props=skip_props)
|
||||
|
||||
props.set_props_kwargs_from_ifc_data(data)
|
||||
props.is_editing = 1
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ class LaunchTypeManager(bpy.types.Operator):
|
||||
|
||||
op = row.operator("bim.select_type", icon="OBJECT_DATA", text="")
|
||||
op.relating_type = relating_type["id"]
|
||||
op = row.operator("bim.duplicate_type", icon="COPYDOWN", text="")
|
||||
op = row.operator("bim.duplicate_type", icon="DUPLICATE", text="")
|
||||
op.element = relating_type["id"]
|
||||
op = row.operator("bim.remove_type", icon="X", text="")
|
||||
op.element = relating_type["id"]
|
||||
|
||||
@@ -699,8 +699,16 @@ class DumbWallPlaner:
|
||||
new_material = ifcopenshell.util.element.get_material(settings["relating_type"])
|
||||
if not new_material or not new_material.is_a("IfcMaterialLayerSet"):
|
||||
return
|
||||
parametric = ifcopenshell.util.element.get_psets(settings["relating_type"]).get("EPset_Parametric")
|
||||
layer_set_direction = None
|
||||
if parametric:
|
||||
layer_set_direction = parametric.get("LayerSetDirection", layer_set_direction)
|
||||
material = ifcopenshell.util.element.get_material(settings["related_object"])
|
||||
if material and material.is_a("IfcMaterialLayerSetUsage") and material.LayerSetDirection == "AXIS2":
|
||||
if not material or not material.is_a("IfcMaterialLayerSetUsage"):
|
||||
return
|
||||
if layer_set_direction:
|
||||
material.LayerSetDirection = layer_set_direction
|
||||
if material.LayerSetDirection == "AXIS2":
|
||||
DumbWallRecalculator().recalculate([obj])
|
||||
|
||||
|
||||
|
||||
@@ -27,11 +27,7 @@ import blenderbim.core.root
|
||||
import blenderbim.core.geometry
|
||||
from ifcopenshell.api.geometry.add_window_representation import DEFAULT_PANEL_SCHEMAS
|
||||
from ifcopenshell.util.shape_builder import V
|
||||
from bpy.types import Operator
|
||||
from bpy.props import FloatProperty, IntProperty, BoolProperty
|
||||
from bmesh.types import BMVert
|
||||
from blenderbim.bim.helper import convert_property_group_from_si
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
@@ -105,31 +101,32 @@ def update_window_modifier_representation(context):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMWindowProperties
|
||||
ifc_file = tool.Ifc.get()
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
|
||||
representation_data = {
|
||||
"partition_type": props.window_type,
|
||||
"overall_height": props.overall_height,
|
||||
"overall_width": props.overall_width,
|
||||
"overall_height": props.overall_height / si_conversion,
|
||||
"overall_width": props.overall_width / si_conversion,
|
||||
"lining_properties": {
|
||||
"LiningDepth": props.lining_depth,
|
||||
"LiningThickness": props.lining_thickness,
|
||||
"LiningOffset": props.lining_offset,
|
||||
"LiningToPanelOffsetX": props.lining_to_panel_offset_x,
|
||||
"LiningToPanelOffsetY": props.lining_to_panel_offset_y,
|
||||
"MullionThickness": props.mullion_thickness,
|
||||
"FirstMullionOffset": props.first_mullion_offset,
|
||||
"SecondMullionOffset": props.second_mullion_offset,
|
||||
"TransomThickness": props.transom_thickness,
|
||||
"FirstTransomOffset": props.first_transom_offset,
|
||||
"SecondTransomOffset": props.second_transom_offset,
|
||||
"LiningDepth": props.lining_depth / si_conversion,
|
||||
"LiningThickness": props.lining_thickness / si_conversion,
|
||||
"LiningOffset": props.lining_offset / si_conversion,
|
||||
"LiningToPanelOffsetX": props.lining_to_panel_offset_x / si_conversion,
|
||||
"LiningToPanelOffsetY": props.lining_to_panel_offset_y / si_conversion,
|
||||
"MullionThickness": props.mullion_thickness / si_conversion,
|
||||
"FirstMullionOffset": props.first_mullion_offset / si_conversion,
|
||||
"SecondMullionOffset": props.second_mullion_offset / si_conversion,
|
||||
"TransomThickness": props.transom_thickness / si_conversion,
|
||||
"FirstTransomOffset": props.first_transom_offset / si_conversion,
|
||||
"SecondTransomOffset": props.second_transom_offset / si_conversion,
|
||||
},
|
||||
"panel_properties": [],
|
||||
}
|
||||
number_of_panels, panels_data = props.window_types_panels[props.window_type]
|
||||
for panel_i in range(number_of_panels):
|
||||
panel_data = {
|
||||
"FrameDepth": props.frame_depth[panel_i],
|
||||
"FrameThickness": props.frame_thickness[panel_i],
|
||||
"FrameDepth": props.frame_depth[panel_i] / si_conversion,
|
||||
"FrameThickness": props.frame_thickness[panel_i] / si_conversion,
|
||||
}
|
||||
representation_data["panel_properties"].append(panel_data)
|
||||
|
||||
@@ -175,10 +172,10 @@ def update_window_modifier_representation(context):
|
||||
# occurences attributes
|
||||
occurences = tool.Ifc.get_all_element_occurences(element)
|
||||
for occurence in occurences:
|
||||
occurence.OverallWidth = props.overall_width
|
||||
occurence.OverallHeight = props.overall_height
|
||||
occurence.OverallWidth = props.overall_width / si_conversion
|
||||
occurence.OverallHeight = props.overall_height / si_conversion
|
||||
|
||||
update_simple_openings(element, props.overall_width, props.overall_height)
|
||||
update_simple_openings(element, props.overall_width / si_conversion, props.overall_height / si_conversion)
|
||||
|
||||
|
||||
def create_bm_window_frame(bm, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()):
|
||||
@@ -291,27 +288,26 @@ def create_bm_window(
|
||||
def update_window_modifier_bmesh(context):
|
||||
obj = context.object
|
||||
props = obj.BIMWindowProperties
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
panel_schema = DEFAULT_PANEL_SCHEMAS[props.window_type]
|
||||
accumulated_height = [0] * len(panel_schema[0])
|
||||
built_panels = []
|
||||
|
||||
overall_width = props.overall_width * si_conversion
|
||||
lining_depth = props.lining_depth * si_conversion
|
||||
overall_height = props.overall_height * si_conversion
|
||||
lining_to_panel_offset_x = props.lining_to_panel_offset_x * si_conversion
|
||||
lining_to_panel_offset_y = props.lining_to_panel_offset_y * si_conversion
|
||||
lining_thickness = props.lining_thickness * si_conversion
|
||||
lining_offset = props.lining_offset * si_conversion
|
||||
overall_width = props.overall_width
|
||||
lining_depth = props.lining_depth
|
||||
overall_height = props.overall_height
|
||||
lining_to_panel_offset_x = props.lining_to_panel_offset_x
|
||||
lining_to_panel_offset_y = props.lining_to_panel_offset_y
|
||||
lining_thickness = props.lining_thickness
|
||||
lining_offset = props.lining_offset
|
||||
|
||||
mullion_thickness = props.mullion_thickness * si_conversion / 2
|
||||
first_mullion_offset = props.first_mullion_offset * si_conversion
|
||||
second_mullion_offset = props.second_mullion_offset * si_conversion
|
||||
transom_thickness = props.transom_thickness * si_conversion / 2
|
||||
first_transom_offset = props.first_transom_offset * si_conversion
|
||||
second_transom_offset = props.second_transom_offset * si_conversion
|
||||
mullion_thickness = props.mullion_thickness / 2
|
||||
first_mullion_offset = props.first_mullion_offset
|
||||
second_mullion_offset = props.second_mullion_offset
|
||||
transom_thickness = props.transom_thickness / 2
|
||||
first_transom_offset = props.first_transom_offset
|
||||
second_transom_offset = props.second_transom_offset
|
||||
|
||||
glass_thickness = 0.01 * si_conversion
|
||||
glass_thickness = 0.01
|
||||
|
||||
bm = bmesh.new()
|
||||
panel_schema = list(reversed(panel_schema))
|
||||
@@ -349,8 +345,8 @@ def update_window_modifier_bmesh(context):
|
||||
accumulated_width += panel_width
|
||||
continue
|
||||
|
||||
frame_depth = props.frame_depth[panel_i] * si_conversion
|
||||
frame_thickness = props.frame_thickness[panel_i] * si_conversion
|
||||
frame_depth = props.frame_depth[panel_i]
|
||||
frame_thickness = props.frame_thickness[panel_i]
|
||||
|
||||
# add window
|
||||
window_lining_size = V(
|
||||
@@ -456,13 +452,9 @@ class AddWindow(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.report({"ERROR"}, "Object has to be IfcWindow/IfcWindowType type to add a window.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
# need to make sure all default props will have correct units
|
||||
if not props.window_added_previously:
|
||||
convert_property_group_from_si(props, skip_props=("is_editing", "window_type", "window_added_previously"))
|
||||
|
||||
window_data = props.get_general_kwargs()
|
||||
lining_props = props.get_lining_kwargs()
|
||||
panel_props = props.get_panel_kwargs()
|
||||
window_data = props.get_general_kwargs(convert_to_project_units=True)
|
||||
lining_props = props.get_lining_kwargs(convert_to_project_units=True)
|
||||
panel_props = props.get_panel_kwargs(convert_to_project_units=True)
|
||||
|
||||
window_data["lining_properties"] = lining_props
|
||||
window_data["panel_properties"] = panel_props
|
||||
@@ -490,8 +482,7 @@ class CancelEditingWindow(bpy.types.Operator, tool.Ifc.Operator):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Window", "Data"))
|
||||
props = obj.BIMWindowProperties
|
||||
for prop_name in data:
|
||||
setattr(props, prop_name, data[prop_name])
|
||||
props.set_props_kwargs_from_ifc_data(data)
|
||||
|
||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
@@ -518,9 +509,9 @@ class FinishEditingWindow(bpy.types.Operator, tool.Ifc.Operator):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMWindowProperties
|
||||
|
||||
window_data = props.get_general_kwargs()
|
||||
lining_props = props.get_lining_kwargs()
|
||||
panel_props = props.get_panel_kwargs()
|
||||
window_data = props.get_general_kwargs(convert_to_project_units=True)
|
||||
lining_props = props.get_lining_kwargs(convert_to_project_units=True)
|
||||
panel_props = props.get_panel_kwargs(convert_to_project_units=True)
|
||||
|
||||
window_data["lining_properties"] = lining_props
|
||||
window_data["panel_properties"] = panel_props
|
||||
@@ -549,14 +540,7 @@ class EnableEditingWindow(bpy.types.Operator, tool.Ifc.Operator):
|
||||
data.update(data.pop("panel_properties"))
|
||||
|
||||
# required since we could load pset from .ifc and BIMWindowProperties won't be set
|
||||
for prop_name in data:
|
||||
setattr(props, prop_name, data[prop_name])
|
||||
|
||||
# need to make sure all props that weren't used before
|
||||
# will have correct units
|
||||
skip_props = ("is_editing", "window_type", "window_added_previously")
|
||||
skip_props += tuple(data.keys())
|
||||
convert_property_group_from_si(props, skip_props=skip_props)
|
||||
props.set_props_kwargs_from_ifc_data(data)
|
||||
|
||||
props.is_editing = 1
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -62,6 +62,8 @@ class BimTool(WorkSpaceTool):
|
||||
("bim.hotkey", {"type": "D", "value": "PRESS", "alt": True}, {"properties": [("hotkey", "A_D")]}),
|
||||
("bim.hotkey", {"type": "E", "value": "PRESS", "alt": True}, {"properties": [("hotkey", "A_E")]}),
|
||||
("bim.hotkey", {"type": "O", "value": "PRESS", "alt": True}, {"properties": [("hotkey", "A_O")]}),
|
||||
("bim.hotkey", {"type": "P", "value": "PRESS", "ctrl": True}, {"properties": [("hotkey", "C_P")]}),
|
||||
("bim.hotkey", {"type": "P", "value": "PRESS", "alt": True}, {"properties": [("hotkey", "A_P")]}),
|
||||
)
|
||||
|
||||
def draw_settings(context, layout, ws_tool):
|
||||
@@ -323,15 +325,15 @@ class BimToolUI:
|
||||
def draw_basic_bim_tool_interface(cls):
|
||||
cls.draw_type_selection_interface()
|
||||
|
||||
if cls.props.ifc_class:
|
||||
box = cls.layout.box()
|
||||
if AuthoringData.data["type_thumbnail"]:
|
||||
box.template_icon(icon_value=AuthoringData.data["type_thumbnail"], scale=5)
|
||||
else:
|
||||
op = box.operator("bim.load_type_thumbnails", text="Load Thumbnails", icon="FILE_REFRESH")
|
||||
op.ifc_class = cls.props.ifc_class
|
||||
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
if cls.props.ifc_class:
|
||||
box = cls.layout.box()
|
||||
if AuthoringData.data["type_thumbnail"]:
|
||||
box.template_icon(icon_value=AuthoringData.data["type_thumbnail"], scale=5)
|
||||
else:
|
||||
op = box.operator("bim.load_type_thumbnails", text="Load Thumbnails", icon="FILE_REFRESH")
|
||||
op.ifc_class = cls.props.ifc_class
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
@@ -404,6 +406,16 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
bpy.ops.bim.calculate_all_quantities()
|
||||
|
||||
def hotkey_C_P(self):
|
||||
if not bpy.context.selected_objects:
|
||||
return
|
||||
bpy.ops.bim.assign_object()
|
||||
|
||||
def hotkey_A_P(self):
|
||||
if not bpy.context.selected_objects:
|
||||
return
|
||||
bpy.ops.bim.unassign_object()
|
||||
|
||||
def hotkey_S_C(self):
|
||||
if not bpy.context.selected_objects:
|
||||
return
|
||||
|
||||
@@ -44,6 +44,7 @@ class IfcClassData:
|
||||
cls.data["name"] = cls.name()
|
||||
cls.data["ifc_class"] = cls.ifc_class()
|
||||
cls.data["ifc_predefined_types"] = cls.ifc_predefined_types()
|
||||
cls.data["can_reassign_class"] = cls.can_reassign_class()
|
||||
|
||||
@classmethod
|
||||
def ifc_products(cls):
|
||||
@@ -172,3 +173,13 @@ class IfcClassData:
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if element:
|
||||
return element.is_a()
|
||||
|
||||
@classmethod
|
||||
def can_reassign_class(cls):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if element:
|
||||
if element.is_a("IfcOpeningElement") or element.is_a("IfcOpeningStandardCase"):
|
||||
return False
|
||||
for product in cls.ifc_products():
|
||||
if element.is_a(product[0]):
|
||||
return True
|
||||
|
||||
@@ -156,6 +156,7 @@ class UnlinkObject(bpy.types.Operator):
|
||||
bl_label = "Unlink Object"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
should_delete: bpy.props.BoolProperty(name="Should Delete", default=True)
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
@@ -167,7 +168,14 @@ class UnlinkObject(bpy.types.Operator):
|
||||
else:
|
||||
objects = context.selected_objects
|
||||
for obj in objects:
|
||||
if obj.BIMObjectProperties.ifc_definition_id:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
if self.should_delete:
|
||||
obj_copy = obj.copy()
|
||||
for collection in obj.users_collection:
|
||||
collection.objects.link(obj_copy)
|
||||
tool.Geometry.delete_ifc_object(obj)
|
||||
obj = obj_copy
|
||||
if obj in IfcStore.edited_objs:
|
||||
IfcStore.edited_objs.remove(obj)
|
||||
IfcStore.unlink_element(obj=obj)
|
||||
@@ -182,6 +190,13 @@ class UnlinkObject(bpy.types.Operator):
|
||||
obj.name = "/".join(obj.name.split("/")[1:])
|
||||
return {"FINISHED"}
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout.row()
|
||||
row.prop(self, "should_delete")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class CopyClass(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.copy_class"
|
||||
|
||||
@@ -62,9 +62,8 @@ class BIM_PT_class(Panel):
|
||||
row.label(text=IfcClassData.data["name"])
|
||||
op = row.operator("bim.select_ifc_class", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.ifc_class = IfcClassData.data["ifc_class"]
|
||||
row.operator("bim.copy_class", icon="DUPLICATE", text="")
|
||||
row.operator("bim.unlink_object", icon="UNLINKED", text="")
|
||||
if IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcRoot"):
|
||||
if IfcClassData.data["can_reassign_class"]:
|
||||
row.operator("bim.enable_reassign_class", icon="GREASEPENCIL", text="")
|
||||
else:
|
||||
ifc_predefined_types = root_prop.get_ifc_predefined_types(context.scene.BIMRootProperties, context)
|
||||
|
||||
@@ -380,6 +380,7 @@ class BIM_PT_work_schedules(Panel):
|
||||
row.operator("bim.add_summary_task", text="Add Summary Task", icon="ADD").work_schedule = work_schedule_id
|
||||
row.operator("bim.expand_all_tasks", text="Expand All")
|
||||
row.operator("bim.contract_all_tasks", text="Contract All")
|
||||
row = self.layout.row(align=True)
|
||||
self.draw_task_operators()
|
||||
self.layout.template_list(
|
||||
"BIM_UL_tasks",
|
||||
@@ -438,6 +439,11 @@ class BIM_PT_work_schedules(Panel):
|
||||
op.lag_time = sequence["TimeLag"]
|
||||
op = row.operator("bim.enable_editing_sequence_attributes", text="Edit Sequence", icon="GREASEPENCIL")
|
||||
op.sequence = sequence["id"]
|
||||
if process_type == "RelatingProcess":
|
||||
op = row.operator("bim.unassign_predecessor", text="", icon="X")
|
||||
elif process_type == "RelatedProcess":
|
||||
op = row.operator("bim.unassign_successor", text="", icon="X")
|
||||
op.task = task["id"]
|
||||
|
||||
def draw_editable_sequence_attributes_ui(self):
|
||||
blenderbim.bim.helper.draw_attributes(self.props.sequence_attributes, self.layout)
|
||||
|
||||
@@ -51,7 +51,6 @@ class AssignType(bpy.types.Operator, Operator):
|
||||
)
|
||||
for obj in related_objects:
|
||||
core.assign_type(tool.Ifc, tool.Type, element=tool.Ifc.get_entity(obj), type=type)
|
||||
oprops = obj.BIMObjectProperties
|
||||
|
||||
|
||||
class UnassignType(bpy.types.Operator):
|
||||
@@ -118,6 +117,7 @@ class EnableEditingType(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
context.active_object.BIMTypeProperties.is_editing_type = True
|
||||
context.active_object.BIMTypeProperties.relating_type_object = None
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -143,8 +143,12 @@ class SelectType(bpy.types.Operator):
|
||||
element = tool.Ifc.get().by_id(self.relating_type)
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
if obj in context.selectable_objects:
|
||||
tool.Blender.select_and_activate_single_object(context, obj)
|
||||
else:
|
||||
self.report({"INFO"}, "Type object can't be selected : It may be hidden or in an excluded collection.")
|
||||
context.scene.BIMModelProperties.ifc_class = element.is_a()
|
||||
context.scene.BIMModelProperties.relating_type_id = str(self.relating_type)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -177,10 +181,17 @@ class SelectTypeObjects(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
relating_type = bpy.data.objects.get(self.relating_type) if self.relating_type else context.active_object
|
||||
at_least_one_selectable_typed_object = False
|
||||
for element in ifcopenshell.util.element.get_types(tool.Ifc.get_entity(relating_type)):
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
if obj and obj in context.selectable_objects:
|
||||
obj.select_set(True)
|
||||
at_least_one_selectable_typed_object = True
|
||||
if at_least_one_selectable_typed_object:
|
||||
context.active_object.select_set(False)
|
||||
context.view_layer.objects.active = context.selected_objects[0]
|
||||
else:
|
||||
self.report({"INFO"}, "Typed objects can't be selected : They may be hidden or in an excluded collection.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -451,6 +462,12 @@ class DuplicateType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
new = blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj)
|
||||
new.Name += " Copy"
|
||||
bpy.ops.bim.load_type_thumbnails(ifc_class=new.is_a())
|
||||
if obj in context.selectable_objects:
|
||||
tool.Blender.select_and_activate_single_object(context, new_obj)
|
||||
else:
|
||||
self.report({"INFO"}, "Type object can't be selected : It may be hidden or in an excluded collection.")
|
||||
context.scene.BIMModelProperties.ifc_class = new.is_a()
|
||||
context.scene.BIMModelProperties.relating_type_id = str(new_obj.BIMObjectProperties.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import ifcopenshell.util.type
|
||||
from blenderbim.bim.module.type.data import TypeData
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -50,9 +51,42 @@ def update_relating_type_class(self, context):
|
||||
TypeData.is_loaded = False
|
||||
|
||||
|
||||
def update_relating_type_from_object(self, context):
|
||||
if self.relating_type_object is None:
|
||||
return
|
||||
element = tool.Ifc.get_entity(self.relating_type_object)
|
||||
if not element:
|
||||
return
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
if not element_type:
|
||||
return
|
||||
self.relating_type = str(element_type.id())
|
||||
bpy.ops.bim.assign_type()
|
||||
|
||||
|
||||
def is_object_class_applicable(self, obj):
|
||||
if not TypeData.is_loaded:
|
||||
TypeData.load()
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
if element_type is None:
|
||||
return False
|
||||
return str(element_type.is_a()) in (r_t_c[0] for r_t_c in TypeData.data["relating_type_classes"])
|
||||
|
||||
|
||||
class BIMTypeProperties(PropertyGroup):
|
||||
is_editing_type: BoolProperty(name="Is Editing Type")
|
||||
relating_type_class: EnumProperty(
|
||||
items=get_relating_type_class, name="Relating Type Class", update=update_relating_type_class
|
||||
items=get_relating_type_class,
|
||||
name="Relating Type Class",
|
||||
update=update_relating_type_class,
|
||||
)
|
||||
relating_type: EnumProperty(items=get_relating_type, name="Relating Type")
|
||||
relating_type_object: PointerProperty(
|
||||
type=bpy.types.Object,
|
||||
name="Copy Class",
|
||||
update=update_relating_type_from_object,
|
||||
poll=is_object_class_applicable,
|
||||
)
|
||||
|
||||
@@ -59,24 +59,31 @@ class BIM_PT_type(Panel):
|
||||
oprops = context.active_object.BIMObjectProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{TypeData.data['total_instances']} Typed Objects")
|
||||
row.operator("bim.select_type_objects", icon="RESTRICT_SELECT_OFF", text="")
|
||||
select_type_objects_row = row.row(align=True)
|
||||
select_type_objects_row.operator("bim.select_type_objects", icon="RESTRICT_SELECT_OFF", text="")
|
||||
select_type_objects_row.enabled = int(TypeData.data["total_instances"]) > 0
|
||||
op = row.operator("bim.duplicate_type", icon="DUPLICATE", text="")
|
||||
op.element = context.active_object.BIMObjectProperties.ifc_definition_id
|
||||
|
||||
def draw_product_ui(self, context):
|
||||
layout = self.layout
|
||||
props = context.active_object.BIMTypeProperties
|
||||
oprops = context.active_object.BIMObjectProperties
|
||||
|
||||
if props.is_editing_type:
|
||||
row = self.layout.row(align=True)
|
||||
row = layout.row(align=True)
|
||||
row_object = layout.row(align=True)
|
||||
|
||||
row.prop(props, "relating_type_class", text="")
|
||||
if type_prop.get_relating_type(None, context):
|
||||
prop_with_search(row, props, "relating_type", text="")
|
||||
row.operator("bim.assign_type", icon="CHECKMARK", text="")
|
||||
row_object.prop(props, "relating_type_object", icon="COPYDOWN")
|
||||
else:
|
||||
row.label(text="No Types Found")
|
||||
row.operator("bim.disable_editing_type", icon="CANCEL", text="")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row = layout.row(align=True)
|
||||
if TypeData.data["relating_type"]:
|
||||
row.label(text=TypeData.data["relating_type"]["name"])
|
||||
op = row.operator("bim.select_type", icon="OBJECT_DATA", text="")
|
||||
|
||||
@@ -34,25 +34,34 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props = context.scene.BIMModelProperties
|
||||
if len(context.selected_objects) != 2:
|
||||
return {"FINISHED"}
|
||||
|
||||
# The convention is that element1 is the element and element2 is the opening.
|
||||
obj1, obj2 = context.selected_objects
|
||||
element1 = tool.Ifc.get_entity(obj1)
|
||||
element2 = tool.Ifc.get_entity(obj2)
|
||||
if type(element1) == type(element2):
|
||||
if (
|
||||
element1
|
||||
and element2
|
||||
and not element1.is_a("IfcOpeningElement")
|
||||
and not element2.is_a("IfcOpeningElement")
|
||||
):
|
||||
if element1.is_a("IfcWindow") or element1.is_a("IfcDoor"):
|
||||
|
||||
if not element1 and not element2:
|
||||
return {"FINISHED"} # Both are not IFC objects.
|
||||
|
||||
if element1 and element2:
|
||||
if element1.is_a("IfcOpeningElement") and element2.is_a("IfcOpeningElement"):
|
||||
return {"FINISHED"} # You can't add an opening to another opening.
|
||||
elif not element1.is_a("IfcOpeningElement") and not element2.is_a("IfcOpeningElement"):
|
||||
if element1.is_a("IfcWindow") or element1.is_a("IfcDoor"): # Add a fill to an element.
|
||||
obj1, obj2 = obj2, obj1
|
||||
FilledOpeningGenerator().generate(obj2, obj1, target=obj2.matrix_world.translation)
|
||||
return {"FINISHED"}
|
||||
return {"FINISHED"}
|
||||
elif element1.is_a("IfcOpeningElement") or element2.is_a("IfcOpeningElement"):
|
||||
if element1.is_a("IfcOpeningElement"): # Reassign an opening to another element.
|
||||
obj1, obj2 = obj2, obj1
|
||||
element1, element2 = element2, element1
|
||||
|
||||
if element2 and not element1:
|
||||
obj1, obj2 = obj2, obj1
|
||||
element1, element2 = element2, element1
|
||||
|
||||
if element1.is_a("IfcOpeningElement"):
|
||||
return {"FINISHED"}
|
||||
return {"FINISHED"} # You can't add an opening to another opening.
|
||||
|
||||
if tool.Ifc.is_moved(obj1):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj1)
|
||||
@@ -64,17 +73,21 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
|
||||
break
|
||||
|
||||
body_context = ifcopenshell.util.representation.get_context(IfcStore.get_file(), "Model", "Body")
|
||||
element2 = blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj2,
|
||||
ifc_class="IfcOpeningElement",
|
||||
should_add_representation=True,
|
||||
context=body_context,
|
||||
)
|
||||
if not element2:
|
||||
element2 = blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj2,
|
||||
ifc_class="IfcOpeningElement",
|
||||
should_add_representation=True,
|
||||
context=body_context,
|
||||
)
|
||||
ifcopenshell.api.run("void.add_opening", tool.Ifc.get(), opening=element2, element=element1)
|
||||
|
||||
if tool.Ifc.is_moved(obj2):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj2)
|
||||
|
||||
voided_objs = [obj1]
|
||||
for subelement in ifcopenshell.util.element.get_decomposition(element1):
|
||||
subobj = tool.Ifc.get_object(subelement)
|
||||
|
||||
@@ -40,11 +40,16 @@ def assign_object(ifc, aggregator, collector, relating_obj=None, related_obj=Non
|
||||
def unassign_object(ifc, aggregate, collector, relating_obj=None, related_obj=None):
|
||||
related_element = ifc.get_entity(related_obj)
|
||||
container = aggregate.get_container(related_element)
|
||||
ifc.run("aggregate.unassign_object", product=related_element)
|
||||
if container:
|
||||
ifc.run("spatial.assign_container", product=related_element, relating_structure=container)
|
||||
collector.assign(relating_obj)
|
||||
collector.assign(related_obj)
|
||||
if not relating_obj:
|
||||
relating_element = aggregate.get_relating_object(related_element)
|
||||
if related_element:
|
||||
relating_obj = ifc.get_object(relating_element)
|
||||
if relating_obj:
|
||||
ifc.run("aggregate.unassign_object", product=related_element)
|
||||
if container:
|
||||
ifc.run("spatial.assign_container", product=related_element, relating_structure=container)
|
||||
collector.assign(relating_obj)
|
||||
collector.assign(related_obj)
|
||||
|
||||
|
||||
def add_part_to_object(ifc, aggregator, collector, blender, obj, part_class, part_name=None):
|
||||
|
||||
@@ -130,7 +130,7 @@ def assign_cost_value(ifc, cost_item, cost_rate):
|
||||
ifc.run("cost.assign_cost_value", cost_item=cost_item, cost_rate=cost_rate)
|
||||
|
||||
def load_schedule_of_rates(cost, schedule_of_rates):
|
||||
cost.load_schedule_of_rates(schedule_of_rates)
|
||||
cost.load_schedule_of_rates_tree(schedule_of_rates)
|
||||
|
||||
def unassign_cost_item_quantity(ifc, cost, cost_item, products):
|
||||
ifc.run("cost.unassign_cost_item_quantity", cost_item=cost_item, products=products)
|
||||
@@ -236,8 +236,8 @@ def calculate_cost_item_resource_value(ifc, cost_item):
|
||||
ifc.run("cost.calculate_cost_item_resource_value", cost_item=cost_item)
|
||||
|
||||
|
||||
def export_cost_schedules(cost, format):
|
||||
cost.export_cost_schedules(format)
|
||||
def export_cost_schedules(cost, format, cost_schedule=None):
|
||||
cost.export_cost_schedules(format, cost_schedule)
|
||||
|
||||
|
||||
def clear_cost_item_assignments(ifc, cost, cost_item, related_object_type):
|
||||
@@ -249,7 +249,7 @@ def clear_cost_item_assignments(ifc, cost, cost_item, related_object_type):
|
||||
|
||||
|
||||
def select_unassigned_products(ifc, cost, spatial):
|
||||
spatial.deselect_all()
|
||||
spatial.deselect_objects()
|
||||
products = ifc.get().by_type("IfcElement")
|
||||
cost_schedule = cost.get_active_cost_schedule()
|
||||
selection = [product for product in products if not cost.has_cost_assignments(product, cost_schedule)]
|
||||
@@ -276,4 +276,11 @@ def change_parent_cost_item(ifc, cost, new_parent):
|
||||
if cost_item :
|
||||
ifc.run("nest.change_nest", item=cost_item, new_parent=new_parent)
|
||||
cost.disable_editing_cost_item_parent()
|
||||
cost.load_cost_schedule_tree()
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
def copy_cost_item(ifc, cost):
|
||||
cost_item = cost.get_highlighted_cost_item()
|
||||
if cost_item:
|
||||
cost_item = ifc.run("cost.copy_cost_item", cost_item=cost_item)
|
||||
cost.disable_editing_cost_item_parent()
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
@@ -270,6 +270,9 @@ def duplicate_drawing(ifc, drawing_tool, drawing=None, should_duplicate_annotati
|
||||
|
||||
|
||||
def remove_drawing(ifc, drawing_tool, drawing=None):
|
||||
if drawing_tool.is_active_drawing(drawing):
|
||||
drawing_tool.run_drawing_activate_model()
|
||||
|
||||
collection = drawing_tool.get_drawing_collection(drawing)
|
||||
if collection:
|
||||
drawing_tool.delete_collection(collection)
|
||||
|
||||
@@ -72,3 +72,18 @@ def disable_editing_materials(material):
|
||||
|
||||
def select_by_material(material_tool, material=None):
|
||||
material_tool.select_elements(material_tool.get_elements_by_material(material))
|
||||
|
||||
def enable_editing_material(material_tool, material):
|
||||
material_tool.load_material_attributes(material)
|
||||
material_tool.enable_editing_material(material)
|
||||
|
||||
def edit_material(ifc, material_tool, material):
|
||||
attributes = material_tool.get_material_attributes()
|
||||
ifc.run("material.edit_material", material=material, attributes=attributes)
|
||||
material_tool.disable_editing_material()
|
||||
material_type = material_tool.get_active_material_type()
|
||||
material_tool.import_material_definitions(material_type)
|
||||
material_tool.enable_editing_materials()
|
||||
|
||||
def disable_editing_material(material_tool):
|
||||
material_tool.disable_editing_material()
|
||||
|
||||
@@ -56,6 +56,9 @@ def create_project(ifc, project, schema=None, template=None):
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Annotation", target_view="MODEL_VIEW", parent=model
|
||||
)
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Annotation", target_view="PLAN_VIEW", parent=model
|
||||
)
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Profile", target_view="ELEVATION_VIEW", parent=model
|
||||
)
|
||||
|
||||
@@ -443,7 +443,7 @@ def select_work_schedule_products(sequence, spatial, work_schedule=None):
|
||||
|
||||
|
||||
def select_unassigned_work_schedule_products(ifc, sequence, spatial):
|
||||
spatial.deselect_all()
|
||||
spatial.deselect_objects()
|
||||
products = ifc.get().by_type("IfcElement")
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
schedule_products = sequence.get_work_schedule_products(work_schedule)
|
||||
|
||||
@@ -165,8 +165,7 @@ class Cost:
|
||||
def expand_cost_item_rate(cls, cost_item): pass
|
||||
def expand_cost_item(cls, cost_item): pass
|
||||
def expand_cost_items(cls): pass
|
||||
def export_cost_schedules(cls, format): pass
|
||||
def export_cost_schedules(cls, format): pass
|
||||
def export_cost_schedules(cls, format, cost_schedule): pass
|
||||
def format_unit(cls, unit): pass
|
||||
def get_active_cost_item(cls): pass
|
||||
def get_active_cost_schedule(cls): pass
|
||||
@@ -274,9 +273,8 @@ class Drawing:
|
||||
def get_body_context(cls): pass
|
||||
def get_default_drawing_path(cls, name): pass
|
||||
def get_default_drawing_resource_path(cls, resource): pass
|
||||
def get_default_shading_style(cls): pass
|
||||
def setup_shading_styles_path(cls, resource_path): pass
|
||||
def get_default_layout_path(cls, identification, name): pass
|
||||
def get_default_shading_style(cls): pass
|
||||
def get_default_sheet_path(cls, identification, name): pass
|
||||
def get_default_sheet_path(cls, identification, name): pass
|
||||
def get_default_titleblock_path(cls, name): pass
|
||||
@@ -302,17 +300,20 @@ class Drawing:
|
||||
def import_schedules(cls): pass
|
||||
def import_sheets(cls): pass
|
||||
def import_text_attributes(cls, obj): pass
|
||||
def is_active_drawing(cls, drawing): pass
|
||||
def is_camera_orthographic(cls): pass
|
||||
def is_drawing_active(cls): pass
|
||||
def move_file(cls, src, dest): pass
|
||||
def open_spreadsheet(cls, uri): pass
|
||||
def open_svg(cls, filepath): pass
|
||||
def remove_literal_from_annotation(cls, obj, literal): pass
|
||||
def run_drawing_activate_model(cls): pass
|
||||
def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass
|
||||
def select_assigned_product(cls, drawing): pass
|
||||
def set_drawing_collection_name(cls, group, collection): pass
|
||||
def set_name(cls, element, name): pass
|
||||
def setup_annotation_object(cls, obj, object_type): pass
|
||||
def setup_shading_styles_path(cls, resource_path): pass
|
||||
def show_decorations(cls): pass
|
||||
def sync_object_placement(cls, obj): pass
|
||||
def synchronise_ifc_and_text_attributes(cls, obj): pass
|
||||
@@ -432,14 +433,19 @@ class Loader:
|
||||
class Material:
|
||||
def add_default_material_object(cls): pass
|
||||
def delete_object(cls, obj): pass
|
||||
def disable_editing_material(cls): pass
|
||||
def disable_editing_materials(cls): pass
|
||||
def enable_editing_material(cls, material): pass
|
||||
def enable_editing_materials(cls): pass
|
||||
def get_active_material_type(cls): pass
|
||||
def get_active_material_type(cls): pass
|
||||
def get_elements_by_material(cls, material): pass
|
||||
def get_material_attributes(cls): pass
|
||||
def get_name(cls, obj): pass
|
||||
def import_material_definitions(cls, material_type): pass
|
||||
def is_editing_materials(cls): pass
|
||||
def is_material_used_in_sets(cls, material): pass
|
||||
def load_material_attributes(cls, material): pass
|
||||
def select_elements(cls, elements): pass
|
||||
|
||||
|
||||
|
||||
@@ -55,3 +55,9 @@ class Aggregate(blenderbim.core.tool.Aggregate):
|
||||
@classmethod
|
||||
def get_container(cls, element):
|
||||
return ifcopenshell.util.element.get_container(element)
|
||||
|
||||
@classmethod
|
||||
def get_relating_object(cls, related_element):
|
||||
for rel in related_element.Decomposes:
|
||||
if rel.is_a("IfcRelAggregates"):
|
||||
return rel.RelatingObject
|
||||
@@ -452,7 +452,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates)
|
||||
contracted_cost_item_rates.remove(cost_item)
|
||||
props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates)
|
||||
cls.load_schedule_of_rates(schedule_of_rates=tool.Ifc.get().by_id(int(props.schedule_of_rates)))
|
||||
cls.load_schedule_of_rates_tree(schedule_of_rates=tool.Ifc.get().by_id(int(props.schedule_of_rates)))
|
||||
|
||||
@classmethod
|
||||
def contract_cost_item_rate(cls, cost_item):
|
||||
@@ -460,7 +460,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates)
|
||||
contracted_cost_item_rates.append(cost_item)
|
||||
props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates)
|
||||
cls.load_schedule_of_rates(schedule_of_rates=tool.Ifc.get().by_id(int(props.schedule_of_rates)))
|
||||
cls.load_schedule_of_rates_tree(schedule_of_rates=tool.Ifc.get().by_id(int(props.schedule_of_rates)))
|
||||
|
||||
@classmethod
|
||||
def create_new_cost_item_li(cls, props_collection, cost_item, level_index, type="cost_rate"):
|
||||
@@ -498,24 +498,23 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
props.is_cost_update_enabled = True
|
||||
|
||||
@classmethod
|
||||
def export_cost_schedules(cls, format=None):
|
||||
def export_cost_schedules(cls, format=None, cost_schedule=None):
|
||||
path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", "cost_schedules")
|
||||
if format == "CSV":
|
||||
from ifc5d.ifc5Dspreadsheet import Ifc5DCsvWriter
|
||||
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
writer = Ifc5DCsvWriter(file=tool.Ifc.get(), output=path)
|
||||
writer = Ifc5DCsvWriter(file=tool.Ifc.get(), output=path, cost_schedule=cost_schedule)
|
||||
writer.write()
|
||||
elif format == "ODS":
|
||||
from ifc5d.ifc5Dspreadsheet import Ifc5DOdsWriter
|
||||
|
||||
writer = Ifc5DOdsWriter(file=tool.Ifc.get(), output=path)
|
||||
writer = Ifc5DOdsWriter(file=tool.Ifc.get(), output=path, cost_schedule=cost_schedule)
|
||||
writer.write()
|
||||
elif format == "XLSX":
|
||||
from ifc5d.ifc5Dspreadsheet import Ifc5DXlsxWriter
|
||||
|
||||
writer = Ifc5DXlsxWriter(file=tool.Ifc.get(), output=path)
|
||||
writer = Ifc5DXlsxWriter(file=tool.Ifc.get(), output=path, cost_schedule=cost_schedule )
|
||||
writer.write()
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -37,7 +37,7 @@ import blenderbim.bim.module.drawing.annotation as annotation
|
||||
import blenderbim.bim.module.drawing.helper as helper
|
||||
|
||||
from blenderbim.bim.module.drawing.data import FONT_SIZES, DecoratorData
|
||||
from blenderbim.bim.module.drawing.prop import get_diagram_scales, BOX_ALIGNMENT_POSITIONS
|
||||
from blenderbim.bim.module.drawing.prop import get_diagram_scales, BOX_ALIGNMENT_POSITIONS, ANNOTATION_TYPES_DATA
|
||||
|
||||
from mathutils import Vector
|
||||
import collections
|
||||
@@ -55,28 +55,18 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
tool.Ifc.get(), source.Representation, exclude=["IfcGeometricRepresentationContext"]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_annotation_data_type(cls, object_type):
|
||||
return ANNOTATION_TYPES_DATA[object_type][3]
|
||||
|
||||
@classmethod
|
||||
def create_annotation_object(cls, drawing, object_type):
|
||||
data_type = {
|
||||
"ANGLE": "curve",
|
||||
"BATTING": "mesh",
|
||||
"BREAKLINE": "mesh",
|
||||
"DIAMETER": "curve",
|
||||
"DIMENSION": "curve",
|
||||
"FALL": "curve",
|
||||
"FILL_AREA": "mesh",
|
||||
"HIDDEN_LINE": "mesh",
|
||||
"LINEWORK": "mesh",
|
||||
"PLAN_LEVEL": "curve",
|
||||
"RADIUS": "curve",
|
||||
"SECTION_LEVEL": "curve",
|
||||
"STAIR_ARROW": "curve",
|
||||
"TEXT": "empty",
|
||||
"TEXT_LEADER": "curve",
|
||||
}[object_type]
|
||||
data_type = cls.get_annotation_data_type(object_type)
|
||||
obj = annotation.Annotator.get_annotation_obj(drawing, object_type, data_type)
|
||||
if object_type == "FILL_AREA":
|
||||
obj = annotation.Annotator.add_plane_to_annotation(obj)
|
||||
elif object_type == "REVISION_CLOUD":
|
||||
obj = annotation.Annotator.add_plane_to_annotation(obj, remove_face=True)
|
||||
elif object_type == "TEXT_LEADER":
|
||||
co1, _, co2, _ = annotation.Annotator.get_placeholder_coords()
|
||||
obj = annotation.Annotator.add_line_to_annotation(obj, co2, co1)
|
||||
@@ -217,8 +207,8 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
@classmethod
|
||||
def delete_drawing_elements(cls, elements):
|
||||
for element in elements:
|
||||
ifcopenshell.api.run("root.remove_product", tool.Ifc.get(), product=element)
|
||||
obj = tool.Ifc.get_object(element)
|
||||
ifcopenshell.api.run("root.remove_product", tool.Ifc.get(), product=element)
|
||||
if obj:
|
||||
obj_data = obj.data
|
||||
bpy.data.objects.remove(obj)
|
||||
@@ -1442,6 +1432,14 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
camera = bpy.context.scene.camera
|
||||
return True if (camera and camera.data.type == "ORTHO") else False
|
||||
|
||||
@classmethod
|
||||
def is_active_drawing(cls, drawing):
|
||||
return drawing.id() == bpy.context.scene.DocProperties.active_drawing_id
|
||||
|
||||
@classmethod
|
||||
def run_drawing_activate_model(cls):
|
||||
bpy.ops.bim.activate_model()
|
||||
|
||||
@classmethod
|
||||
def activate_drawing(cls, camera):
|
||||
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
||||
|
||||
@@ -3,7 +3,10 @@ import re
|
||||
|
||||
# allows git import even if git executable isn't found
|
||||
os.environ["GIT_PYTHON_REFRESH"] = "quiet"
|
||||
import git
|
||||
try:
|
||||
import git
|
||||
except:
|
||||
print("Warning: GitPython not available.")
|
||||
import bpy
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
import blenderbim.tool as tool
|
||||
@@ -13,10 +16,15 @@ class IfcGit:
|
||||
@classmethod
|
||||
def init_repo(cls, path_dir):
|
||||
IfcGitRepo.repo = git.Repo.init(path_dir)
|
||||
cls.config_info_attributes(IfcGitRepo.repo)
|
||||
|
||||
@classmethod
|
||||
def clone_repo(cls, remote_url, local_folder):
|
||||
IfcGitRepo.repo = git.Repo.clone_from(remote_url, local_folder)
|
||||
IfcGitRepo.repo = git.Repo.clone_from(
|
||||
url=remote_url,
|
||||
to_path=local_folder,
|
||||
)
|
||||
cls.config_info_attributes(IfcGitRepo.repo)
|
||||
return IfcGitRepo.repo
|
||||
|
||||
@classmethod
|
||||
@@ -61,6 +69,8 @@ class IfcGit:
|
||||
|
||||
@classmethod
|
||||
def add_file_to_repo(cls, repo, path_file):
|
||||
if os.name == "nt":
|
||||
cls.dos2unix(path_file)
|
||||
repo.index.add(path_file)
|
||||
repo.index.commit(message="Added " + os.path.relpath(path_file, repo.working_dir))
|
||||
bpy.ops.ifcgit.refresh()
|
||||
@@ -73,6 +83,8 @@ class IfcGit:
|
||||
def git_commit(cls, path_file):
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
repo = IfcGitRepo.repo
|
||||
if os.name == "nt":
|
||||
cls.dos2unix(path_file)
|
||||
repo.index.add(path_file)
|
||||
repo.index.commit(message=props.commit_message)
|
||||
props.commit_message = ""
|
||||
@@ -180,6 +192,7 @@ class IfcGit:
|
||||
bpy.data.orphans_purge(do_recursive=True)
|
||||
|
||||
bpy.ops.bim.load_project(filepath=path_ifc)
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
@classmethod
|
||||
def branches_by_hexsha(cls, repo):
|
||||
@@ -191,6 +204,13 @@ class IfcGit:
|
||||
result[branch.commit.hexsha].append(branch)
|
||||
else:
|
||||
result[branch.commit.hexsha] = [branch]
|
||||
if repo.remotes:
|
||||
for remote in repo.remotes:
|
||||
for ref in remote.refs:
|
||||
if ref.commit.hexsha in result:
|
||||
result[ref.commit.hexsha].append(ref)
|
||||
else:
|
||||
result[ref.commit.hexsha] = [ref]
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
@@ -292,6 +312,7 @@ class IfcGit:
|
||||
def colourise(cls, step_ids):
|
||||
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
||||
area.spaces[0].shading.color_type = "OBJECT"
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
for obj in bpy.context.visible_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
@@ -299,10 +320,13 @@ class IfcGit:
|
||||
step_id = obj.BIMObjectProperties.ifc_definition_id
|
||||
if step_id in step_ids["modified"]:
|
||||
obj.color = (0.3, 0.3, 1.0, 1)
|
||||
obj.select_set(True)
|
||||
elif step_id in step_ids["added"]:
|
||||
obj.color = (0.2, 0.8, 0.2, 1)
|
||||
obj.select_set(True)
|
||||
elif step_id in step_ids["removed"]:
|
||||
obj.color = (1.0, 0.2, 0.2, 1)
|
||||
obj.select_set(True)
|
||||
else:
|
||||
obj.color = (1.0, 1.0, 1.0, 0.5)
|
||||
|
||||
@@ -317,9 +341,9 @@ class IfcGit:
|
||||
for branch in lookup[item.hexsha]:
|
||||
if branch.name == props.display_branch:
|
||||
branch.checkout()
|
||||
else:
|
||||
# NOTE this is calling the git binary in a subprocess
|
||||
repo.git.checkout(item.hexsha)
|
||||
return
|
||||
# NOTE this is calling the git binary in a subprocess
|
||||
repo.git.checkout(item.hexsha)
|
||||
|
||||
@classmethod
|
||||
def delete_collection(cls, blender_collection):
|
||||
@@ -345,6 +369,23 @@ class IfcGit:
|
||||
config_writer.set_value(section, "cmd", "ifcmerge $BASE $LOCAL $REMOTE $MERGED")
|
||||
config_writer.set_value(section, "trustExitCode", True)
|
||||
|
||||
@classmethod
|
||||
def config_info_attributes(cls, repo):
|
||||
"""Set IFC files as text in .git/info/attributes"""
|
||||
path_attributes = os.path.join(repo.git_dir, "info", "attributes")
|
||||
if not os.path.exists(path_attributes):
|
||||
with open(path_attributes, "w") as f:
|
||||
# attributes patterns are case-insensitive
|
||||
f.write("*.ifc text")
|
||||
|
||||
@classmethod
|
||||
def dos2unix(cls, path_file):
|
||||
with open(path_file, "rb") as infile:
|
||||
content = infile.read()
|
||||
with open(path_file, "wb") as output:
|
||||
for line in content.splitlines():
|
||||
output.write(line + b"\n")
|
||||
|
||||
@classmethod
|
||||
def execute_merge(cls, path_ifc, operator):
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
|
||||
@@ -107,3 +107,29 @@ class Material(blenderbim.core.tool.Material):
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
|
||||
@classmethod
|
||||
def get_active_material_type(cls):
|
||||
return bpy.context.scene.BIMMaterialProperties.material_type
|
||||
|
||||
@classmethod
|
||||
def load_material_attributes(cls, material):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.material_attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(material, props.material_attributes)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_material(cls, material):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.active_material_id = material.id()
|
||||
props.editing_material_type = "ATTRIBUTES"
|
||||
|
||||
@classmethod
|
||||
def get_material_attributes(cls):
|
||||
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMMaterialProperties.material_attributes)
|
||||
|
||||
@classmethod
|
||||
def disable_editing_material(cls):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.active_material_id = 0
|
||||
props.editing_material_type = ""
|
||||
|
||||
@@ -28,6 +28,7 @@ import blenderbim.core.geometry as geometry
|
||||
from mathutils import Matrix, Vector
|
||||
from blenderbim.bim import import_ifc
|
||||
from blenderbim.bim.module.geometry.helper import Helper
|
||||
import collections
|
||||
|
||||
|
||||
class Model(blenderbim.core.tool.Model):
|
||||
@@ -43,6 +44,33 @@ class Model(blenderbim.core.tool.Model):
|
||||
return [v * cls.unit_scale for v in value]
|
||||
return value * cls.unit_scale
|
||||
|
||||
@classmethod
|
||||
def convert_data_to_project_units(cls, data, non_si_props=[]):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
for prop_name in data:
|
||||
if prop_name in non_si_props:
|
||||
continue
|
||||
prop_value = data[prop_name]
|
||||
if isinstance(prop_value, collections.abc.Iterable):
|
||||
data[prop_name] = [v / si_conversion for v in prop_value]
|
||||
else:
|
||||
data[prop_name] = prop_value / si_conversion
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def convert_data_to_si_units(cls, data, non_si_props=[]):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
for prop_name in data:
|
||||
if prop_name in non_si_props:
|
||||
continue
|
||||
prop_value = data[prop_name]
|
||||
print(prop_name, prop_value)
|
||||
if isinstance(prop_value, collections.abc.Iterable):
|
||||
data[prop_name] = [v * si_conversion for v in prop_value]
|
||||
else:
|
||||
data[prop_name] = prop_value * si_conversion
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def export_curve(cls, position, edge_indices, points=None):
|
||||
position_i = position.inverted()
|
||||
@@ -147,11 +175,13 @@ class Model(blenderbim.core.tool.Model):
|
||||
cls.bm.edges.ensure_lookup_table()
|
||||
|
||||
surface = tool.Ifc.get().createIfcCurveBoundedPlane()
|
||||
surface.BasisSurface = tool.Ifc.get().createIfcPlane(tool.Ifc.get().createIfcAxis2Placement3D(
|
||||
tool.Ifc.get().createIfcCartesianPoint([o / cls.unit_scale for o in p1]),
|
||||
tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]),
|
||||
tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]),
|
||||
))
|
||||
surface.BasisSurface = tool.Ifc.get().createIfcPlane(
|
||||
tool.Ifc.get().createIfcAxis2Placement3D(
|
||||
tool.Ifc.get().createIfcCartesianPoint([o / cls.unit_scale for o in p1]),
|
||||
tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]),
|
||||
tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]),
|
||||
)
|
||||
)
|
||||
|
||||
if tool.Ifc.get().schema != "IFC2X3":
|
||||
cls.points = cls.export_points(position, indices["points"])
|
||||
|
||||
@@ -154,7 +154,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
if column_type == "IfcTask":
|
||||
return task.get_info(task)[name] or ""
|
||||
elif column_type == "IfcTaskTime" and task.TaskTime:
|
||||
return task.TaskTime.get_info(task)[name]
|
||||
return task.TaskTime.get_info(task)[name] if task.TaskTime.get_info(task)[name] else ""
|
||||
return task.Identification or ""
|
||||
|
||||
def natural_sort_key(i, _nsre=re.compile("([0-9]+)")):
|
||||
@@ -212,7 +212,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
item.calendar = ""
|
||||
item.derived_calendar = calendar.Name or "Unnamed" if calendar else ""
|
||||
|
||||
if task.TaskTime:
|
||||
if task.TaskTime and (task.TaskTime.ScheduleStart or task.TaskTime.ScheduleFinish or task.TaskTime.ScheduleDuration):
|
||||
task_time = task.TaskTime
|
||||
item.start = (
|
||||
canonicalise_time(ifcopenshell.util.date.ifc2datetime(task_time.ScheduleStart))
|
||||
|
||||
@@ -156,6 +156,39 @@ optional message text.
|
||||
Similar to commit messages, tag messages should be 50 characters or less,
|
||||
though there is no practical limit.
|
||||
|
||||
Remote operations
|
||||
-----------------
|
||||
|
||||
Git is a *distributed revision control system*, your local repository can be a
|
||||
version of a remote repository and vice-versa. This is conceptually similar to
|
||||
local branching except this remote repository could belong to someone else or
|
||||
could be hosted by an online Git-forge service.
|
||||
|
||||
Your repository can have multiple remote repositories registered, each
|
||||
can have potentially multiple branches.
|
||||
|
||||
BlenderBIM allows you to make a local *clone* of a remote repository. You will
|
||||
need to provide a URL *origin* to fetch, and an empty local folder to become
|
||||
the local repository.
|
||||
|
||||
The *Fetch* operator retrieves new data from the remote repository. This isn't
|
||||
automatically merged, each branch fetched from the remote repository appears as
|
||||
a branch that can be browsed, switched-to or merged just like a local branch.
|
||||
These remote branches have prefixed names, eg. `origin/main`.
|
||||
|
||||
Once you have committed changes to your local repository, the *Push* operator
|
||||
tries to update the remote branch using changes from the selected local branch.
|
||||
|
||||
.. Warning::
|
||||
|
||||
Remote repositories can be accessed in multiple ways; ssh, ftp or https
|
||||
protocols, for example, can require authentication. This authentication may
|
||||
expect you to generate and upload ssh keys, store API tokens, save
|
||||
username/password pairs, or use some other form of credential.
|
||||
BlenderBIM can't configure these credentials for you, follow the
|
||||
configuration instructions provided by your online service before trying
|
||||
actions that require authentication.
|
||||
|
||||
Using other Git tools
|
||||
---------------------
|
||||
|
||||
|
||||
@@ -74,6 +74,21 @@ Scenario: Remove drawing - via object deletion
|
||||
When I press "bim.override_object_delete"
|
||||
Then the collection "IfcGroup/PLAN_VIEW" does not exist
|
||||
|
||||
Scenario: Remove drawing - deleting active drawing
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the variable "wall1" is "IfcStore.get_file().by_type('IfcWall')[-1].id()"
|
||||
And I press "bim.add_drawing"
|
||||
And the variable "drawing" is "IfcStore.get_file().by_type('IfcAnnotation')[0].id()"
|
||||
And I set "scene.DocProperties.active_drawing_index" to "0"
|
||||
And I press "bim.activate_drawing(drawing={drawing})"
|
||||
And the object "IfcAnnotation/PLAN_VIEW" is selected
|
||||
When I press "bim.override_object_delete"
|
||||
Then the collection "IfcGroup/PLAN_VIEW" does not exist
|
||||
|
||||
Scenario: Reproducing freeze on generating drawing - Issue 3169
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
|
||||
@@ -345,6 +345,8 @@ class TestDuplicateDrawing:
|
||||
|
||||
class TestRemoveDrawing:
|
||||
def test_run(self, ifc, drawing):
|
||||
drawing.is_active_drawing("drawing").should_be_called().will_return(True)
|
||||
drawing.run_drawing_activate_model().should_be_called()
|
||||
drawing.get_drawing_collection("drawing").should_be_called().will_return("collection")
|
||||
drawing.get_drawing_group("drawing").should_be_called().will_return("group")
|
||||
drawing.get_group_elements("group").should_be_called().will_return("elements")
|
||||
|
||||
@@ -26,18 +26,7 @@ class TestCreateProject:
|
||||
ifc.get().should_be_called().will_return("ifc")
|
||||
subject.create_project(ifc, project, schema="IFC4", template=None)
|
||||
|
||||
def test_create_an_ifc4_project(self, ifc, project):
|
||||
ifc.get().should_be_called().will_return(None)
|
||||
ifc.run("project.create_file", version="IFC4").should_be_called().will_return("ifc")
|
||||
ifc.set("ifc").should_be_called()
|
||||
|
||||
project.create_empty("My Project").should_be_called().will_return("project")
|
||||
project.create_empty("My Site").should_be_called().will_return("site")
|
||||
project.create_empty("My Building").should_be_called().will_return("building")
|
||||
project.create_empty("My Storey").should_be_called().will_return("storey")
|
||||
project.run_root_assign_class(obj="project", ifc_class="IfcProject").should_be_called()
|
||||
project.run_unit_assign_scene_units().should_be_called()
|
||||
|
||||
def check_contexts(self, project):
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="", target_view="", parent=0
|
||||
).should_be_called().will_return("model")
|
||||
@@ -59,6 +48,9 @@ class TestCreateProject:
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Annotation", target_view="MODEL_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Annotation", target_view="PLAN_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Profile", target_view="ELEVATION_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
@@ -75,6 +67,20 @@ class TestCreateProject:
|
||||
context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent="plan"
|
||||
).should_be_called()
|
||||
|
||||
def test_create_an_ifc4_project(self, ifc, project):
|
||||
ifc.get().should_be_called().will_return(None)
|
||||
ifc.run("project.create_file", version="IFC4").should_be_called().will_return("ifc")
|
||||
ifc.set("ifc").should_be_called()
|
||||
|
||||
project.create_empty("My Project").should_be_called().will_return("project")
|
||||
project.create_empty("My Site").should_be_called().will_return("site")
|
||||
project.create_empty("My Building").should_be_called().will_return("building")
|
||||
project.create_empty("My Storey").should_be_called().will_return("storey")
|
||||
project.run_root_assign_class(obj="project", ifc_class="IfcProject").should_be_called()
|
||||
project.run_unit_assign_scene_units().should_be_called()
|
||||
|
||||
self.check_contexts(project)
|
||||
|
||||
project.run_root_assign_class(obj="site", ifc_class="IfcSite", context="body").should_be_called()
|
||||
project.run_root_assign_class(obj="building", ifc_class="IfcBuilding", context="body").should_be_called()
|
||||
project.run_root_assign_class(obj="storey", ifc_class="IfcBuildingStorey", context="body").should_be_called()
|
||||
@@ -104,42 +110,7 @@ class TestCreateProject:
|
||||
project.run_root_assign_class(obj="project", ifc_class="IfcProject").should_be_called()
|
||||
project.run_unit_assign_scene_units().should_be_called()
|
||||
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="", target_view="", parent=0
|
||||
).should_be_called().will_return("model")
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent="model"
|
||||
).should_be_called().will_return("body")
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Axis", target_view="GRAPH_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Annotation", target_view="SECTION_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Annotation", target_view="ELEVATION_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Annotation", target_view="MODEL_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Profile", target_view="ELEVATION_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Plan", context_identifier="", target_view="", parent=0
|
||||
).should_be_called().will_return("plan")
|
||||
project.run_context_add_context(
|
||||
context_type="Plan", context_identifier="Axis", target_view="GRAPH_VIEW", parent="plan"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Plan", context_identifier="Body", target_view="PLAN_VIEW", parent="plan"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent="plan"
|
||||
).should_be_called()
|
||||
self.check_contexts(project)
|
||||
|
||||
project.run_root_assign_class(obj="site", ifc_class="IfcSite", context="body").should_be_called()
|
||||
project.run_root_assign_class(obj="building", ifc_class="IfcBuilding", context="body").should_be_called()
|
||||
@@ -179,42 +150,7 @@ class TestCreateProject:
|
||||
project.run_root_assign_class(obj="project", ifc_class="IfcProject").should_be_called()
|
||||
project.run_unit_assign_scene_units().should_be_called()
|
||||
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="", target_view="", parent=0
|
||||
).should_be_called().will_return("model")
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent="model"
|
||||
).should_be_called().will_return("body")
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Axis", target_view="GRAPH_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Annotation", target_view="SECTION_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Annotation", target_view="ELEVATION_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Annotation", target_view="MODEL_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Model", context_identifier="Profile", target_view="ELEVATION_VIEW", parent="model"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Plan", context_identifier="", target_view="", parent=0
|
||||
).should_be_called().will_return("plan")
|
||||
project.run_context_add_context(
|
||||
context_type="Plan", context_identifier="Axis", target_view="GRAPH_VIEW", parent="plan"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Plan", context_identifier="Body", target_view="PLAN_VIEW", parent="plan"
|
||||
).should_be_called()
|
||||
project.run_context_add_context(
|
||||
context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent="plan"
|
||||
).should_be_called()
|
||||
self.check_contexts(project)
|
||||
|
||||
project.run_root_assign_class(obj="site", ifc_class="IfcSite", context="body").should_be_called()
|
||||
project.run_root_assign_class(obj="building", ifc_class="IfcBuilding", context="body").should_be_called()
|
||||
|
||||
@@ -753,3 +753,30 @@ class TestUpdateTextValue(NewFile):
|
||||
# test first literal value is unchanged
|
||||
assert props.literals[0].attributes["Literal"].string_value == "Literal"
|
||||
assert ifc.by_type("IfcTextLiteralWithExtent")[0].Literal == "Literal"
|
||||
|
||||
|
||||
class TestDrawingStyles(NewFile):
|
||||
def setup_project_with_drawing(self):
|
||||
bpy.ops.bim.create_project()
|
||||
bpy.ops.bim.load_drawings()
|
||||
bpy.ops.bim.add_drawing()
|
||||
ifc = tool.Ifc.get()
|
||||
drawing = ifc.by_type("IfcAnnotation")[0]
|
||||
bpy.ops.bim.activate_drawing(drawing=drawing.id())
|
||||
self.drawing_styles = bpy.context.scene.DocProperties.drawing_styles
|
||||
|
||||
def test_drawing_styles_not_loaded_if_underlay_is_inactive(self):
|
||||
self.setup_project_with_drawing()
|
||||
assert len(self.drawing_styles) == 0
|
||||
|
||||
def test_drawing_styles_loaded_on_underlay_enabled(self):
|
||||
self.setup_project_with_drawing()
|
||||
bpy.context.scene.camera.data.BIMCameraProperties.has_underlay = True
|
||||
assert len(self.drawing_styles) == 3
|
||||
|
||||
def test_drawing_styles_reload(self):
|
||||
self.setup_project_with_drawing()
|
||||
bpy.ops.bim.reload_drawing_styles()
|
||||
assert len(self.drawing_styles) == 3
|
||||
|
||||
|
||||
@@ -195,12 +195,13 @@ class IfcDataGetter:
|
||||
|
||||
|
||||
class Ifc5Dwriter:
|
||||
def __init__(self, file=None, output=None):
|
||||
def __init__(self, file=None, output=None, cost_schedule=None):
|
||||
self.output = output
|
||||
if isinstance(file, str):
|
||||
self.file = ifcopenshell.open(file)
|
||||
else:
|
||||
self.file = file
|
||||
self.cost_schedule = cost_schedule
|
||||
self.cost_schedules = []
|
||||
self.sheet_data = {}
|
||||
self.column_indexes = []
|
||||
@@ -220,6 +221,10 @@ class Ifc5Dwriter:
|
||||
self.used_names = []
|
||||
for i in range(26):
|
||||
self.column_indexes.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % 26])
|
||||
if self.cost_schedule:
|
||||
for cost_schedule in self.cost_schedules:
|
||||
if cost_schedule.id() != self.cost_schedule.id():
|
||||
self.cost_schedules.remove(cost_schedule)
|
||||
for cost_schedule in self.cost_schedules:
|
||||
sheet_id = cost_schedule.id()
|
||||
self.sheet_data[sheet_id] = {}
|
||||
|
||||
@@ -45,7 +45,7 @@ except:
|
||||
class IfcAttributeSetter:
|
||||
@staticmethod
|
||||
def set_element_key(ifc_file, element, key, value):
|
||||
if key == "type" and element.is_a() != value:
|
||||
if key == "class" and element.is_a() != value:
|
||||
return ifcopenshell.util.schema.reassign_class(ifc_file, element, value)
|
||||
if hasattr(element, key):
|
||||
setattr(element, key, value)
|
||||
|
||||
@@ -20,6 +20,7 @@ related information in ``shape.geometry``:
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.shape
|
||||
|
||||
ifc_file = ifcopenshell.open('model.ifc')
|
||||
element = ifc_file.by_type('IfcWall')[0]
|
||||
@@ -55,19 +56,25 @@ related information in ``shape.geometry``:
|
||||
# Objects are never scaled, so the scale factor of the matrix is always 1.
|
||||
matrix = shape.transformation.matrix.data
|
||||
|
||||
# Indices of vertices per triangle face e.g. [f1v1, f1v2, f1v3, f2v1, f2v2, f2v3, ...]
|
||||
faces = shape.geometry.faces
|
||||
|
||||
# Indices of vertices per edge e.g. [e1v1, e1v2, e2v1, e2v2, ...]
|
||||
edges = shape.geometry.edges
|
||||
# For convenience, you might want the matrix as a nested numpy array, so you can do matrix math.
|
||||
matrix = ifcopenshell.util.shape.get_shape_matrix(shape)
|
||||
|
||||
# X Y Z of vertices in flattened list e.g. [v1x, v1y, v1z, v2x, v2y, v2z, ...]
|
||||
verts = shape.geometry.verts
|
||||
|
||||
# Indices of vertices per edge e.g. [e1v1, e1v2, e2v1, e2v2, ...]
|
||||
edges = shape.geometry.edges
|
||||
|
||||
# Indices of vertices per triangle face e.g. [f1v1, f1v2, f1v3, f2v1, f2v2, f2v3, ...]
|
||||
faces = shape.geometry.faces
|
||||
|
||||
# Since the lists are flattened, you may prefer to group them like so depending on your geometry kernel
|
||||
grouped_verts = [[verts[i], verts[i + 1], verts[i + 2]] for i in range(0, len(verts), 3)]
|
||||
grouped_edges = [[edges[i], edges[i + 1]] for i in range(0, len(edges), 2)]
|
||||
grouped_faces = [[faces[i], faces[i + 1], faces[i + 2]] for i in range(0, len(faces), 3)]
|
||||
# A nested numpy array e.g. [[v1x, v1y, v1z], [v2x, v2y, v2z], ...]
|
||||
grouped_verts = ifcopenshell.util.shape.get_vertices(shape.geometry)
|
||||
# A nested numpy array e.g. [[e1v1, e1v2], [e2v1, e2v2], ...]
|
||||
grouped_edges = ifcopenshell.util.shape.get_edges(shape.geometry)
|
||||
# A nested numpy array e.g. [[f1v1, f1v2, f1v3], [f2v1, f2v2, f2v3], ...]
|
||||
grouped_faces = ifcopenshell.util.shape.get_faces(shape.geometry)
|
||||
|
||||
# A list of styles that are relevant to this shape
|
||||
styles = shape.geometry.materials
|
||||
|
||||
@@ -96,7 +96,7 @@ class Usecase:
|
||||
break
|
||||
|
||||
if decomposes and decomposes == is_decomposed_by:
|
||||
return
|
||||
return decomposes
|
||||
|
||||
container = ifcopenshell.util.element.get_container(self.settings["product"], should_get_direct=True)
|
||||
if container:
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, cost_item=None):
|
||||
"""Copies all cost items and related relationships
|
||||
|
||||
The following relationships are also duplicated:
|
||||
|
||||
* The copy will have the same attributes and property sets as the original cost item
|
||||
* The copy will be assigned to the parent cost schedule
|
||||
* The copy will have duplicated nested cost items
|
||||
|
||||
:param cost_item: The cost item to be duplicated
|
||||
:type cost_item: ifcopenshell.entity_instance.entity_instance
|
||||
:return: The duplicated cost item or the list of duplicated cost items if the latter has children
|
||||
:rtype: ifcopenshell.entity_instance.entity_instance or list of ifcopenshell.entity_instance.entity_instance
|
||||
|
||||
Example:
|
||||
.. code:: python
|
||||
|
||||
# We have a cost item
|
||||
cost_item = CostItem(name="Design new feature", deadline="2023-03-01")
|
||||
|
||||
# And now we have two
|
||||
duplicated_cost_item = project.duplicate_cost_item(cost_item)
|
||||
|
||||
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {"cost_item": cost_item}
|
||||
|
||||
def execute(self):
|
||||
self.new_cost_items = []
|
||||
self.duplicate_cost_item(self.settings["cost_item"])
|
||||
|
||||
def duplicate_cost_item(self, cost_item):
|
||||
new_cost_item = ifcopenshell.util.element.copy_deep(self.file, cost_item)
|
||||
self.new_cost_items.append(new_cost_item)
|
||||
self.copy_indirect_attributes(cost_item, new_cost_item)
|
||||
return new_cost_item
|
||||
|
||||
def copy_indirect_attributes(self, from_element, to_element):
|
||||
for inverse in self.file.get_inverse(from_element):
|
||||
if inverse.is_a("IfcRelDefinesByProperties"):
|
||||
inverse = ifcopenshell.util.element.copy(self.file, inverse)
|
||||
inverse.RelatedObjects = [to_element]
|
||||
pset = ifcopenshell.util.element.copy_deep(
|
||||
self.file, inverse.RelatingPropertyDefinition
|
||||
)
|
||||
inverse.RelatingPropertyDefinition = pset
|
||||
elif inverse.is_a("IfcRelNests") and inverse.RelatingObject == from_element:
|
||||
nested_cost_items = [e for e in inverse.RelatedObjects]
|
||||
if nested_cost_items:
|
||||
new_cost_items = []
|
||||
for cost_item in nested_cost_items:
|
||||
new_cost_item = self.duplicate_cost_item(cost_item)
|
||||
new_cost_items.append(new_cost_item)
|
||||
inverse = ifcopenshell.util.element.copy(self.file, inverse)
|
||||
inverse.RelatingObject = to_element
|
||||
inverse.RelatedObjects = new_cost_items
|
||||
for cost_item in new_cost_items:
|
||||
ifcopenshell.api.run(
|
||||
"nest.unassign_object", self.file, related_object=cost_item
|
||||
)
|
||||
rel = ifcopenshell.api.run(
|
||||
"nest.assign_object",
|
||||
self.file,
|
||||
related_object=cost_item,
|
||||
relating_object=to_element,
|
||||
)
|
||||
# elif inverse.is_a("IfcRelAssignsToProduct"):
|
||||
# continue
|
||||
# to_element.IsDefinedBy = inverse.RelatingOrder
|
||||
# elif inverse.is_a("IfcRelAssignsToProcess"):
|
||||
# to_element.IsDefinedBy = inverse.RelatingProcess
|
||||
# elif inverse.is_a("IfcRelAssignsToResource"):
|
||||
# continue
|
||||
# elif inverse.is_a("IfcRelAssignsToControl"):
|
||||
# continue
|
||||
# elif inverse.is_a("IfcRelDefinesByObject"):
|
||||
# continue
|
||||
else:
|
||||
for i, value in enumerate(inverse):
|
||||
if value == from_element:
|
||||
new_inverse = ifcopenshell.util.element.copy(self.file, inverse)
|
||||
new_inverse[i] = to_element
|
||||
elif isinstance(value, (tuple, list)) and from_element in value:
|
||||
new_value = list(value)
|
||||
new_value.append(to_element)
|
||||
inverse[i] = new_value
|
||||
@@ -0,0 +1,29 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, material=None, attributes=None):
|
||||
"""Edits the attributes of an IfcMaterial"""
|
||||
|
||||
self.file = file
|
||||
self.settings = {"material": material, "attributes": attributes or {}}
|
||||
|
||||
def execute(self):
|
||||
for name, value in self.settings["attributes"].items():
|
||||
setattr(self.settings["material"], name, value)
|
||||
@@ -64,7 +64,7 @@ class Usecase:
|
||||
become IfcLabel, float values will become IfcReal, booleans will
|
||||
become IfcBoolean, and integers will become IfcInteger. If more
|
||||
control is desired, you may explicitly specify IFC data objects
|
||||
directly.
|
||||
directly. Note that provided `properties` might be mutated in the process.
|
||||
:type properties: dict
|
||||
:param pset_template: If a property set template is provided, this will
|
||||
be used to determine data types. If no user-defined template is
|
||||
|
||||
@@ -76,6 +76,13 @@ class Usecase:
|
||||
)
|
||||
elif not inverse.RelatedObjects:
|
||||
self.file.remove(inverse)
|
||||
elif self.settings["task"] in inverse.RelatedObjects:
|
||||
related_objects = list(inverse.RelatedObjects)
|
||||
related_objects.remove(self.settings["task"])
|
||||
if not related_objects:
|
||||
self.file.remove(inverse)
|
||||
else:
|
||||
inverse.RelatedObjects = related_objects
|
||||
elif inverse.is_a("IfcRelAssignsToControl"):
|
||||
if (
|
||||
inverse.RelatingControl == self.settings["task"]
|
||||
|
||||
@@ -186,7 +186,6 @@ def string_to_duration(duration_string):
|
||||
|
||||
|
||||
def parse_duration(value):
|
||||
print("parsing duration", value)
|
||||
if not value:
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
|
||||
@@ -68,7 +68,7 @@ def reassign_class(ifc_file, element, new_class):
|
||||
try:
|
||||
declaration = schema.declaration_by_name(new_class)
|
||||
except:
|
||||
print(f"Class of {element} could not be changed to {new_class} as the class does not exist")
|
||||
raise Exception(f"Class of {element} could not be changed to {new_class} as the class does not exist")
|
||||
|
||||
info = element.get_info()
|
||||
|
||||
|
||||
@@ -66,6 +66,11 @@ def get_z(geometry):
|
||||
return max(z_values) - min(z_values)
|
||||
|
||||
|
||||
def get_shape_matrix(shape):
|
||||
m = shape.transformation.matrix.data
|
||||
return 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]))
|
||||
|
||||
|
||||
def get_bbox_centroid(geometry):
|
||||
x_values = [geometry.verts[i] for i in range(0, len(geometry.verts), 3)]
|
||||
y_values = [geometry.verts[i + 1] for i in range(0, len(geometry.verts), 3)]
|
||||
@@ -89,9 +94,7 @@ def get_element_bbox_centroid(element, geometry):
|
||||
|
||||
def get_shape_bbox_centroid(shape, geometry):
|
||||
centroid = get_bbox_centroid(geometry)
|
||||
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]))
|
||||
return (mat @ np.array([*centroid, 1.0]))[0:3]
|
||||
return (get_shape_matrix(shape) @ np.array([*centroid, 1.0]))[0:3]
|
||||
|
||||
|
||||
def get_vertices(geometry):
|
||||
@@ -110,14 +113,15 @@ def get_faces(geometry):
|
||||
|
||||
|
||||
def get_shape_vertices(shape, geometry):
|
||||
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]))
|
||||
verts = get_vertices(geometry)
|
||||
mat = get_shape_matrix(shape)
|
||||
return np.array([mat @ np.array([verts[i], verts[i + 1], verts[i + 2]]) for i in range(0, len(verts), 3)])
|
||||
|
||||
|
||||
def get_element_vertices(element, geometry):
|
||||
verts = get_vertices(geometry)
|
||||
if not element.ObjectPlacement or not element.ObjectPlacement.is_a("IfcLocalPlacement"):
|
||||
return get_shape_vertices(geometry)
|
||||
return verts
|
||||
mat = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
return np.array([mat @ np.array([verts[i], verts[i + 1], verts[i + 2]]) for i in range(0, len(verts), 3)])
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ class Facet:
|
||||
return results
|
||||
|
||||
def parse(self, xml):
|
||||
setattr(self, "minOccurs", 1)
|
||||
setattr(self, "maxOccurs", 1)
|
||||
for name, value in xml.items():
|
||||
name = name.replace("@", "")
|
||||
if isinstance(value, dict) and "simpleValue" in value.keys():
|
||||
@@ -893,7 +895,7 @@ class ClassificationResult(Result):
|
||||
return "The entity has no classification"
|
||||
elif self.reason["type"] == "VALUE":
|
||||
return f"The references \"{str(self.reason['actual'])}\" do not match the requirements"
|
||||
elif self.reason["type"] == "system":
|
||||
elif self.reason["type"] == "SYSTEM":
|
||||
return f"The systems \"{str(self.reason['actual'])}\" do not match the requirements"
|
||||
elif self.reason["type"] == "PROHIBITED":
|
||||
return f"The classification should not have met the requirement"
|
||||
|
||||
Reference in New Issue
Block a user