Placement: motor unificado _calculate_placement para aligned/non_aligned, misma lógica compartida
This commit is contained in:
+66
-110
@@ -156,7 +156,7 @@ class _PVPlantPlacementTaskPanel_old:
|
|||||||
newrack.Placement = placements[idx]
|
newrack.Placement = placements[idx]
|
||||||
group.addObject(newrack)
|
group.addObject(newrack)
|
||||||
frames.append(newrack)
|
frames.append(newrack)
|
||||||
except (KeyError, TypeError, AttributeError):
|
except:
|
||||||
placements = dataframe[0]
|
placements = dataframe[0]
|
||||||
frames = []
|
frames = []
|
||||||
for idx in placements:
|
for idx in placements:
|
||||||
@@ -696,7 +696,7 @@ class _PVPlantPlacementTaskPanel_new1:
|
|||||||
newrack.Placement = placements[idx]
|
newrack.Placement = placements[idx]
|
||||||
group.addObject(newrack)
|
group.addObject(newrack)
|
||||||
frames.append(newrack)
|
frames.append(newrack)
|
||||||
except (KeyError, TypeError, AttributeError):
|
except:
|
||||||
placements = dataframe[0]
|
placements = dataframe[0]
|
||||||
frames = []
|
frames = []
|
||||||
for idx in placements:
|
for idx in placements:
|
||||||
@@ -1310,7 +1310,7 @@ class _PVPlantPlacementTaskPanel:
|
|||||||
self._terrain_interpolator = LinearNDInterpolator(
|
self._terrain_interpolator = LinearNDInterpolator(
|
||||||
filtered_points[:, :2], filtered_points[:, 2]
|
filtered_points[:, :2], filtered_points[:, 2]
|
||||||
)
|
)
|
||||||
except Exception:
|
except:
|
||||||
self._terrain_interpolator = None
|
self._terrain_interpolator = None
|
||||||
|
|
||||||
return self._terrain_interpolator
|
return self._terrain_interpolator
|
||||||
@@ -1357,7 +1357,7 @@ class _PVPlantPlacementTaskPanel:
|
|||||||
z_bot = slope * bot_point.y + intercept
|
z_bot = slope * bot_point.y + intercept
|
||||||
else:
|
else:
|
||||||
z_top = z_bot = 0
|
z_top = z_bot = 0
|
||||||
except Exception:
|
except:
|
||||||
z_top = z_bot = 0
|
z_top = z_bot = 0
|
||||||
else:
|
else:
|
||||||
# Fallback to direct projection (slower)
|
# Fallback to direct projection (slower)
|
||||||
@@ -1373,7 +1373,7 @@ class _PVPlantPlacementTaskPanel:
|
|||||||
z_bot = slope * bot_point.y + intercept
|
z_bot = slope * bot_point.y + intercept
|
||||||
else:
|
else:
|
||||||
z_top = z_bot = 0
|
z_top = z_bot = 0
|
||||||
except Exception:
|
except:
|
||||||
z_top = z_bot = 0
|
z_top = z_bot = 0
|
||||||
|
|
||||||
new_top = FreeCAD.Vector(top_point.x, top_point.y, z_top)
|
new_top = FreeCAD.Vector(top_point.x, top_point.y, z_top)
|
||||||
@@ -1436,7 +1436,7 @@ class _PVPlantPlacementTaskPanel:
|
|||||||
try:
|
try:
|
||||||
cut = frame_footprint.cut([self.Area])
|
cut = frame_footprint.cut([self.Area])
|
||||||
return len(cut.Vertexes) == 0
|
return len(cut.Vertexes) == 0
|
||||||
except Part.OCCError:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def getAligments(self):
|
def getAligments(self):
|
||||||
@@ -1469,142 +1469,98 @@ class _PVPlantPlacementTaskPanel:
|
|||||||
return x_range, y_range
|
return x_range, y_range
|
||||||
|
|
||||||
def calculateAlignedArray(self):
|
def calculateAlignedArray(self):
|
||||||
"""Optimized aligned array calculation"""
|
"""
|
||||||
pointsx, pointsy = self.getAligments()
|
Coloca frames en grid alineado (filas y columnas).
|
||||||
if len(pointsx) == 0 or len(pointsy) == 0:
|
Llama al motor unificado _calculate_placement.
|
||||||
return pd.DataFrame()
|
"""
|
||||||
|
return self._calculate_placement(mode='aligned')
|
||||||
# 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)
|
|
||||||
|
|
||||||
def calculateNonAlignedArray(self):
|
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()
|
pointsx, pointsy = self.getAligments()
|
||||||
if len(pointsx) == 0:
|
if len(pointsx) == 0:
|
||||||
FreeCAD.Console.PrintWarning("No X alignments found.\n")
|
FreeCAD.Console.PrintWarning("No X alignments found.\n")
|
||||||
return pd.DataFrame()
|
return pd.DataFrame()
|
||||||
|
|
||||||
# Precompute footprints
|
# Pre-calcular footprints una sola vez
|
||||||
footprints = []
|
footprints = []
|
||||||
for frame in self.FrameSetups:
|
for frame in self.FrameSetups:
|
||||||
footprint = self._get_frame_footprint(frame)
|
footprint = self._get_frame_footprint(frame)
|
||||||
footprints.append((frame, footprint))
|
footprints.append((frame, footprint))
|
||||||
|
|
||||||
|
min_h = min(ftp[0].Width.Value for ftp in footprints)
|
||||||
corridor_enabled = self.form.groupCorridor.isChecked()
|
corridor_enabled = self.form.groupCorridor.isChecked()
|
||||||
corridor_count = 0
|
corridor_count = 0
|
||||||
corridor_offset = 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 = []
|
cols = []
|
||||||
for x in pointsx:
|
for x in pointsx:
|
||||||
col = []
|
col = []
|
||||||
current_x = x + corridor_offset
|
cx = x + corridor_offset
|
||||||
|
|
||||||
# Create vertical line for intersection
|
if mode == 'aligned' and len(pointsy) > 0:
|
||||||
p1 = FreeCAD.Vector(current_x, self.Area.BoundBox.YMax, 0.0)
|
ref_frame = footprints[0][0]
|
||||||
p2 = FreeCAD.Vector(current_x, self.Area.BoundBox.YMin, 0.0)
|
for y in pointsy:
|
||||||
line = Part.LineSegment(p1, p2).toShape()
|
tp = FreeCAD.Vector(cx, y - ref_frame.Length.Value / 2, 0.0)
|
||||||
|
found = False
|
||||||
# Get intersections with area
|
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:
|
try:
|
||||||
inter = self.Area.section(line)
|
inter = self.Area.section(line)
|
||||||
pts = sorted([v.Point for v in inter.Vertexes], key=lambda p: p.y, reverse=True)
|
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):
|
for i in range(0, len(pts) - 1, 1 + (len(pts) > 2)):
|
||||||
top, bottom = pts[i], pts[i + 1]
|
top, bot = pts[i], pts[i + 1]
|
||||||
available_height = top.y - bottom.y
|
if top.y - bot.y > min_h:
|
||||||
|
self._place_frames_in_segment(col, footprints, cx, top, bot)
|
||||||
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:
|
except Exception as e:
|
||||||
FreeCAD.Console.PrintWarning(f"Error in segment processing: {e}\n")
|
FreeCAD.Console.PrintWarning(f"Segment error: {e}\n")
|
||||||
|
|
||||||
# Handle corridor offset
|
|
||||||
if corridor_enabled and col:
|
if corridor_enabled and col:
|
||||||
corridor_count += 1
|
corridor_count += 1
|
||||||
if corridor_count >= self.form.editColCount.value():
|
if corridor_count >= self.form.editColCount.value():
|
||||||
corridor_offset += 12000 # 12m corridor
|
corridor_offset += corridor_val
|
||||||
corridor_count = 0
|
corridor_count = 0
|
||||||
|
|
||||||
cols.append(col)
|
cols.append(col)
|
||||||
|
|
||||||
return self.adjustToTerrain(cols)
|
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):
|
def accept(self):
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
starttime = datetime.now()
|
starttime = datetime.now()
|
||||||
|
|||||||
Reference in New Issue
Block a user