@@ -442,6 +442,8 @@ def generate_underlay(self, context: bpy.types.Context) -> Union[str, None]:
442442 context .scene .render .filepath = str (Path (svg_path ).with_suffix (".png" ))
443443 assert (drawing_style := self .cprops .get_active_drawing_style ())
444444
445+ tool .Blender .sync_render_visibility ()
446+
445447 if drawing_style .render_type == "DEFAULT" :
446448 bpy .ops .render .render (write_still = True )
447449 else :
@@ -625,7 +627,7 @@ def generate_bisect_linework(self, context: bpy.types.Context, root) -> None:
625627 path .attrib ["d" ] = d
626628 group .append (g )
627629
628- def generate_wall_layers (self , context : bpy .types .Context , root ) -> None :
630+ def generate_material_layers (self , context : bpy .types .Context , root ) -> None :
629631 for el in root .findall (".//{http://www.w3.org/2000/svg}g[@{http://www.ifcopenshell.org/ns}guid]" ):
630632 if "projection" in el .get ("class" , "" ).split ():
631633 continue
@@ -794,8 +796,9 @@ def generate_freestyle_linework(self, context: bpy.types.Context) -> str | None:
794796 edge_bm .to_mesh (edge_mesh )
795797 edge_bm .free ()
796798
797- actual_path = svg_path [ 0 : - 4 ] + "0001.svg"
799+ freestyle_svg_exporter = tool . Blender . get_addon ( "freestyle_svg_exporter" )
798800 context .scene .render .filepath = svg_path [0 :- 4 ]
801+ actual_path = freestyle_svg_exporter .create_path (bpy .context .scene )
799802 bpy .ops .render .render (write_still = False )
800803
801804 os .replace (actual_path , svg_path )
@@ -839,7 +842,8 @@ def generate_freestyle_linework(self, context: bpy.types.Context) -> str | None:
839842
840843 if tool .Drawing .is_camera_orthographic ():
841844 self .generate_bisect_linework (context , root )
842- self .generate_wall_layers (context , root )
845+ if self .cprops .generate_material_layers :
846+ self .generate_material_layers (context , root )
843847 self .merge_linework_and_add_metadata (root )
844848 self .move_elements_to_top (root )
845849
@@ -937,12 +941,14 @@ def generate_linework(self, context: bpy.types.Context) -> Union[str, None]:
937941 if self .cprops .cut_mode == "BISECT" :
938942 self .remove_cut_linework (root )
939943 self .generate_bisect_linework (context , root )
940- self .generate_wall_layers (context , root )
944+ if self .cprops .generate_material_layers :
945+ self .generate_material_layers (context , root )
941946 self .merge_linework_and_add_metadata (root )
942947 self .move_elements_to_top (root )
943948 elif self .cprops .cut_mode == "OPENCASCADE" :
944949 self .move_projection_to_bottom (root )
945- self .generate_wall_layers (context , root )
950+ if self .cprops .generate_material_layers :
951+ self .generate_material_layers (context , root )
946952 self .merge_linework_and_add_metadata (root )
947953 self .move_elements_to_top (root )
948954
@@ -1348,7 +1354,7 @@ def merge_linework_and_add_metadata(self, root):
13481354 join_criteria = join_criteria .split ("," )
13491355 else :
13501356 # Drawing convention states that same objects classes with the same material are merged when cut.
1351- join_criteria = ["class" , "material.Name" , "/Pset_.*Common/.Status" , "EPset_Status.Status" , "Material.Name " ]
1357+ join_criteria = ["class" , "material.Name" , "/Pset_.*Common/.Status" , "EPset_Status.Status" , "EPset_Status.UserDefinedStatus " ]
13521358
13531359 group = root .find ("{http://www.w3.org/2000/svg}g" )
13541360 joined_paths = {}
@@ -1477,11 +1483,10 @@ def merge_linework_and_add_metadata(self, root):
14771483 joined_paths .setdefault (hash_keys , []).append (el )
14781484
14791485 for key , els in joined_paths .items ():
1480- polygons = []
1481- classes = set ()
1486+ queue = []
14821487
14831488 for el in els :
1484- classes . update (el .attrib ["class" ].split ())
1489+ classes = set (el .attrib ["class" ].split ())
14851490 classes .add (el .attrib ["{http://www.ifcopenshell.org/ns}guid" ])
14861491 is_closed_polygon = False
14871492 for path in el .findall ("{http://www.w3.org/2000/svg}path" ):
@@ -1496,31 +1501,30 @@ def merge_linework_and_add_metadata(self, root):
14961501 coords .append (coords [0 ])
14971502 if len (coords ) > 2 and coords [0 ] == coords [- 1 ]:
14981503 is_closed_polygon = True
1499- polygons .append (shapely .Polygon (coords ))
1504+ queue .append (( shapely .Polygon (coords ), classes ))
15001505 if is_closed_polygon :
15011506 el .getparent ().remove (el )
15021507
1503- try :
1504- merged_polygons = shapely . ops . unary_union ( polygons )
1505- except :
1506- print ( "Warning. Portions of the merge failed. Please report a bug!" , polygons )
1507- merged_polygons = polygons
1508-
1509- if type ( merged_polygons ) == shapely . MultiPolygon :
1510- merged_polygons = merged_polygons . geoms
1511- elif type (merged_polygons ) == shapely .Polygon :
1512- merged_polygons = [ merged_polygons ]
1513- else :
1514- merged_polygons = []
1508+ while queue :
1509+ polygon , polygon_classes = queue . pop ( )
1510+ for polygon2 , polygon2_classes in queue [:] :
1511+ try :
1512+ merged_polygon = shapely . union ( polygon , polygon2 )
1513+ except :
1514+ print ( "Warning. Portions of the merge failed. Please report a bug!" , polygon , polygon2 )
1515+ continue
1516+ if type (merged_polygon ) == shapely .Polygon :
1517+ polygon = merged_polygon
1518+ polygon_classes . update ( polygon2_classes )
1519+ queue . remove (( polygon2 , polygon2_classes ))
15151520
1516- for polygon in merged_polygons :
15171521 g = etree .Element ("g" )
15181522 path = etree .SubElement (g , "path" )
15191523 d = "M" + " L" .join (["," .join ([str (o ) for o in co ]) for co in polygon .exterior .coords [0 :- 1 ]]) + " Z"
15201524 for interior in polygon .interiors :
15211525 d += " M" + " L" .join (["," .join ([str (o ) for o in co ]) for co in interior .coords [0 :- 1 ]]) + " Z"
15221526 path .attrib ["d" ] = d
1523- g .set ("class" , " " .join (list (classes )))
1527+ g .set ("class" , " " .join (list (polygon_classes )))
15241528 group .append (g )
15251529
15261530 def drawing_to_model_co (self , x : float , y : float ) -> Vector :
@@ -1862,13 +1866,15 @@ class AddDrawingToSheet(bpy.types.Operator, tool.Ifc.Operator):
18621866 @classmethod
18631867 def poll (cls , context ):
18641868 props = tool .Drawing .get_document_props ()
1865- # Won't be visible in UI anyway.
1866- prefs = tool .Blender .get_addon_preferences ()
1867- if not props .sheets or not prefs .data_dir :
1868- return False
18691869 if not tool .Drawing .get_active_drawing_item ():
18701870 cls .poll_message_set ("No drawing selected." )
18711871 return False
1872+ if not props .sheets :
1873+ cls .poll_message_set ("No sheets available." )
1874+ return False
1875+ if not tool .Blender .get_user_data_dir ():
1876+ cls .poll_message_set ("BIM data directory not set." )
1877+ return False
18721878 return True
18731879
18741880 def _execute (self , context ):
@@ -2222,6 +2228,7 @@ def refine_elements(
22222228 )
22232229
22242230 tool .Blender .reset_object_visibility ()
2231+ tool .Drawing .hide_all_drawing_collections ()
22252232 tool .Blender .update_viewport ()
22262233 bonsai .bim .handler .refresh_ui_data ()
22272234
@@ -2318,9 +2325,8 @@ def _execute(self, context) -> set["rna_enums.OperatorReturnItems"]:
23182325
23192326 dprops .active_drawing_id = self .drawing
23202327 dprops .drawing_styles .clear ()
2321- if ifcopenshell .util .element .get_pset (drawing , "EPset_Drawing" , "HasUnderlay" ):
2322- bpy .ops .bim .reload_drawing_styles ()
2323- bpy .ops .bim .activate_drawing_style ()
2328+ bpy .ops .bim .reload_drawing_styles ()
2329+ bpy .ops .bim .activate_drawing_style ()
23242330
23252331 if tool .Drawing .is_camera_orthographic ():
23262332 core .sync_references (tool .Ifc , tool .Collector , tool .Drawing , drawing = tool .Ifc .get ().by_id (self .drawing ))
@@ -2333,10 +2339,21 @@ def _execute(self, context) -> set["rna_enums.OperatorReturnItems"]:
23332339 camera = context .scene .camera
23342340 assert camera
23352341 camera_props = tool .Drawing .get_camera_props (camera )
2342+ # Check if this is a reflected ceiling camera and preserve its scale
2343+ camera_element = tool .Ifc .get_entity (camera )
2344+ is_reflected = False
2345+ if camera_element :
2346+ is_reflected = ifcopenshell .util .element .get_pset (camera_element , "EPset_Drawing" , "TargetView" ) == "REFLECTED_PLAN_VIEW"
2347+ if is_reflected and camera .scale != (- 1 , - 1 , - 1 ):
2348+ camera .scale = (- 1 , - 1 , - 1 )
2349+ camera .rotation_euler = (0.0 , 0.0 , radians (180 ))
2350+
23362351 if camera_props .update_representation (camera .matrix_world ):
23372352 bpy .ops .bim .update_representation (obj = camera .name , ifc_representation_class = "" )
2338- # See 6452 and 6478.
2339- # bpy.ops.bim.refresh_clipping_planes("INVOKE_DEFAULT")
2353+ # Restore the scale after update if needed
2354+ if is_reflected :
2355+ camera .scale = (- 1 , - 1 , - 1 )
2356+ camera .rotation_euler = (0.0 , 0.0 , radians (180 ))
23402357
23412358 return {"FINISHED" }
23422359
@@ -2812,8 +2829,13 @@ def poll(cls, context):
28122829 if not props .schedules :
28132830 cls .poll_message_set ("No schedule selected." )
28142831 return False
2815- prefs = tool .Blender .get_addon_preferences ()
2816- return props .schedules and props .sheets and prefs .data_dir
2832+ if not props .sheets :
2833+ cls .poll_message_set ("No sheets available." )
2834+ return False
2835+ if not tool .Blender .get_user_data_dir ():
2836+ cls .poll_message_set ("BIM data directory not set." )
2837+ return False
2838+ return True
28172839
28182840 def _execute (self , context ):
28192841 props = tool .Drawing .get_document_props ()
@@ -2880,8 +2902,13 @@ def poll(cls, context):
28802902 if not props .references :
28812903 cls .poll_message_set ("No reference selected." )
28822904 return False
2883- bim_props = tool .Blender .get_bim_props ()
2884- return props .references and props .sheets and bim_props .data_dir
2905+ if not props .sheets :
2906+ cls .poll_message_set ("No sheets available." )
2907+ return False
2908+ if not tool .Blender .get_user_data_dir ():
2909+ cls .poll_message_set ("BIM data directory not set." )
2910+ return False
2911+ return True
28852912
28862913 def _execute (self , context ):
28872914 props = tool .Drawing .get_document_props ()
0 commit comments