diff --git a/PVPlantPlacement.py b/PVPlantPlacement.py index dc770b0..3203ec4 100644 --- a/PVPlantPlacement.py +++ b/PVPlantPlacement.py @@ -156,7 +156,7 @@ class _PVPlantPlacementTaskPanel_old: newrack.Placement = placements[idx] group.addObject(newrack) frames.append(newrack) - except (KeyError, TypeError, AttributeError): + except: placements = dataframe[0] frames = [] for idx in placements: @@ -696,7 +696,7 @@ class _PVPlantPlacementTaskPanel_new1: newrack.Placement = placements[idx] group.addObject(newrack) frames.append(newrack) - except (KeyError, TypeError, AttributeError): + except: placements = dataframe[0] frames = [] for idx in placements: @@ -1310,7 +1310,7 @@ class _PVPlantPlacementTaskPanel: self._terrain_interpolator = LinearNDInterpolator( filtered_points[:, :2], filtered_points[:, 2] ) - except Exception: + except: self._terrain_interpolator = None return self._terrain_interpolator @@ -1357,7 +1357,7 @@ class _PVPlantPlacementTaskPanel: z_bot = slope * bot_point.y + intercept else: z_top = z_bot = 0 - except Exception: + except: z_top = z_bot = 0 else: # Fallback to direct projection (slower) @@ -1373,7 +1373,7 @@ class _PVPlantPlacementTaskPanel: z_bot = slope * bot_point.y + intercept else: z_top = z_bot = 0 - except Exception: + except: z_top = z_bot = 0 new_top = FreeCAD.Vector(top_point.x, top_point.y, z_top) @@ -1436,7 +1436,7 @@ class _PVPlantPlacementTaskPanel: try: cut = frame_footprint.cut([self.Area]) return len(cut.Vertexes) == 0 - except Part.OCCError: + except: return False def getAligments(self): @@ -1469,142 +1469,98 @@ class _PVPlantPlacementTaskPanel: return x_range, y_range def calculateAlignedArray(self): - """Optimized aligned array calculation""" - pointsx, pointsy = self.getAligments() - if len(pointsx) == 0 or len(pointsy) == 0: - return pd.DataFrame() - - # Precompute footprints once - footprints = [] - for frame in self.FrameSetups: - footprint = self._get_frame_footprint(frame) - footprints.append((frame, footprint)) - - ref_frame, ref_footprint = footprints[0] - ref_length = ref_frame.Length.Value - ref_width = ref_frame.Width.Value - - # Corridor variables - countcols = 0 - valcols = FreeCAD.Units.Quantity(self.form.editColGap.text()).Value - (self.gap_col - ref_width) - corridor_enabled = self.form.groupCorridor.isChecked() - - cols = [] - for x in pointsx: - col = [] - corridor_offset = 0 - - for y in pointsy: - point = FreeCAD.Vector(x + ref_width / 2 + corridor_offset, y - ref_length / 2, 0.0) - found = False - - # Check reference frame first (most common case) - if self.isInside(ref_frame, point): - col.append([ref_frame, point]) - found = True - else: - # Check alternative frames - for frame, footprint in footprints[1:]: - length_diff = (ref_frame.Length.Value - frame.Length.Value) / 2 - for offset in [length_diff, -length_diff]: - test_point = FreeCAD.Vector(point.x, point.y + offset, 0.0) - if self.isInside(frame, test_point): - col.append([frame, test_point]) - found = True - break - if found: - break - - if not found: - col.append(0) - - # Handle corridors - if corridor_enabled and col: - countcols += 1 - if countcols >= self.form.editColCount.value(): - corridor_offset += valcols - countcols = 0 - - cols.append(col) - - return self.adjustToTerrain(cols) + """ + Coloca frames en grid alineado (filas y columnas). + Llama al motor unificado _calculate_placement. + """ + return self._calculate_placement(mode='aligned') def calculateNonAlignedArray(self): - """Optimized non-aligned array calculation""" + """ + Coloca frames adaptados al contorno del área (solo columnas). + Llama al motor unificado _calculate_placement. + """ + return self._calculate_placement(mode='non_aligned') + + def _calculate_placement(self, mode='non_aligned'): + """ + Motor de posicionamiento unificado para aligned y non_aligned. + + aligned: grid Y fijo + isInside (rápido en áreas rectangulares) + non_aligned: intersección área-línea (preciso en bordes irregulares) + """ pointsx, pointsy = self.getAligments() if len(pointsx) == 0: FreeCAD.Console.PrintWarning("No X alignments found.\n") return pd.DataFrame() - # Precompute footprints + # Pre-calcular footprints una sola vez footprints = [] for frame in self.FrameSetups: footprint = self._get_frame_footprint(frame) footprints.append((frame, footprint)) + min_h = min(ftp[0].Width.Value for ftp in footprints) corridor_enabled = self.form.groupCorridor.isChecked() corridor_count = 0 corridor_offset = 0 + ref_width = footprints[0][0].Width.Value + corridor_val = FreeCAD.Units.Quantity( + self.form.editColGap.text()).Value - (self.gap_col - ref_width) + area_ymax = self.Area.BoundBox.YMax + area_ymin = self.Area.BoundBox.YMin cols = [] for x in pointsx: col = [] - current_x = x + corridor_offset + cx = x + corridor_offset - # Create vertical line for intersection - p1 = FreeCAD.Vector(current_x, self.Area.BoundBox.YMax, 0.0) - p2 = FreeCAD.Vector(current_x, self.Area.BoundBox.YMin, 0.0) - line = Part.LineSegment(p1, p2).toShape() + if mode == 'aligned' and len(pointsy) > 0: + ref_frame = footprints[0][0] + for y in pointsy: + tp = FreeCAD.Vector(cx, y - ref_frame.Length.Value / 2, 0.0) + found = False + if self.isInside(ref_frame, tp): + col.append([ref_frame, tp]) + found = True + else: + for fi, (fr, _) in enumerate(footprints[1:], 1): + ld = (ref_frame.Length.Value - fr.Length.Value) / 2 + for yoff in (ld, -ld): + tp2 = FreeCAD.Vector(tp.x, tp.y + yoff, 0.0) + if self.isInside(fr, tp2): + col.append([fr, tp2]) + found = True + break + if found: + break + if not found: + col.append(0) + else: + line = Part.LineSegment( + FreeCAD.Vector(cx, area_ymax, 0.0), + FreeCAD.Vector(cx, area_ymin, 0.0) + ).toShape() + try: + inter = self.Area.section(line) + pts = sorted([v.Point for v in inter.Vertexes], + key=lambda p: p.y, reverse=True) + for i in range(0, len(pts) - 1, 1 + (len(pts) > 2)): + top, bot = pts[i], pts[i + 1] + if top.y - bot.y > min_h: + self._place_frames_in_segment(col, footprints, cx, top, bot) + except Exception as e: + FreeCAD.Console.PrintWarning(f"Segment error: {e}\n") - # Get intersections with area - try: - inter = self.Area.section(line) - pts = sorted([v.Point for v in inter.Vertexes], key=lambda p: p.y, reverse=True) - - for i in range(0, len(pts) - 1, 2): - top, bottom = pts[i], pts[i + 1] - available_height = top.y - bottom.y - - if available_height > footprints[-1][0].Width.Value: - # Use optimized placement algorithm - self._place_frames_in_segment(col, footprints, current_x, top, bottom) - - except Exception as e: - FreeCAD.Console.PrintWarning(f"Error in segment processing: {e}\n") - - # Handle corridor offset if corridor_enabled and col: corridor_count += 1 if corridor_count >= self.form.editColCount.value(): - corridor_offset += 12000 # 12m corridor + corridor_offset += corridor_val corridor_count = 0 - cols.append(col) return self.adjustToTerrain(cols) - def _place_frames_in_segment(self, col, footprints, x, top, bottom): - """Optimized frame placement within a segment""" - current_y = top.y - frame_heights = [ftp[0].Width.Value for ftp in footprints] - min_frame_height = min(frame_heights) - - while current_y - bottom.y > min_frame_height: - placed = False - - for frame, footprint in footprints: - test_y = current_y - frame.Width.Value / 2 - test_point = FreeCAD.Vector(x, test_y, 0.0) - - if self.isInside(frame, test_point): - col.append([frame, test_point]) - current_y -= frame.Width.Value - placed = True - break - - if not placed: - break - def accept(self): from datetime import datetime starttime = datetime.now()