Placement: motor unificado _calculate_placement para aligned/non_aligned, misma lógica compartida
This commit is contained in:
+69
-113
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user