Punto de restauración.
This commit is contained in:
@@ -78,11 +78,12 @@ class _PVPlantPlacementTaskPanel:
|
||||
self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "PVPlantPlacement.ui"))
|
||||
self.form.setWindowIcon(QtGui.QIcon(os.path.join(PVPlantResources.DirIcons, "way.svg")))
|
||||
|
||||
self.form.buttonPVArea.clicked.connect(self.addPVArea)
|
||||
#self.form.buttonAddFrame.clicked.connect(self.addFrames)
|
||||
#self.form.buttonRemoveFrame.clicked.connect(self.removeFrame)
|
||||
|
||||
self.addFrames()
|
||||
self.maxWidth = max([frame.Width.Value for frame in self.site.Frames])
|
||||
|
||||
self.form.buttonPVArea.clicked.connect(self.addPVArea)
|
||||
self.form.editGapCols.valueChanged.connect(self.update_inner_spacing)
|
||||
self.update_inner_spacing()
|
||||
|
||||
def addPVArea(self):
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
@@ -95,6 +96,10 @@ class _PVPlantPlacementTaskPanel:
|
||||
list_item = QListWidgetItem(frame_setup.Name, self.form.listFrameSetups)
|
||||
list_item.setCheckState(QtCore.Qt.Checked)
|
||||
|
||||
def update_inner_spacing(self):
|
||||
self.form.editInnerSpacing.setText(
|
||||
("{} m".format((self.form.editGapCols.value() - self.maxWidth / 1000))))
|
||||
|
||||
def createFrameFromPoints(self, dataframe):
|
||||
from Mechanical.Frame import PVPlantFrame
|
||||
try:
|
||||
@@ -104,22 +109,37 @@ class _PVPlantPlacementTaskPanel:
|
||||
MechanicalGroup.Label = "Frames"
|
||||
FreeCAD.ActiveDocument.MechanicalGroup.addObject(MechanicalGroup)
|
||||
|
||||
if self.form.cbSubfolders.checked:
|
||||
group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", self.PVArea.Label)
|
||||
group.Label = self.PVArea.Label
|
||||
MechanicalGroup.addObject(group)
|
||||
MechanicalGroup = group
|
||||
|
||||
placements = dataframe["placement"].tolist()
|
||||
types = dataframe["type"].tolist()
|
||||
frames = []
|
||||
for idx in range(len(placements)):
|
||||
newrack = PVPlantFrame.makeTracker(setup=types[idx])
|
||||
newrack.Label = "Tracker"
|
||||
newrack.Visibility = False
|
||||
newrack.Placement = placements[idx]
|
||||
MechanicalGroup.addObject(newrack)
|
||||
frames.append(newrack)
|
||||
if self.form.cbSubfolders.isChecked:
|
||||
label = "Frames-" + self.PVArea.Label
|
||||
if label in [obj.Label for obj in FreeCAD.ActiveDocument.Frames.Group]:
|
||||
MechanicalGroup = FreeCAD.ActiveDocument.getObject(label)[0]
|
||||
else:
|
||||
group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", label)
|
||||
group.Label = label
|
||||
MechanicalGroup.addObject(group)
|
||||
MechanicalGroup = group
|
||||
try:
|
||||
placements = dataframe["placement"].tolist()
|
||||
types = dataframe["type"].tolist()
|
||||
frames = []
|
||||
for idx in range(len(placements)):
|
||||
newrack = PVPlantFrame.makeTracker(setup=types[idx])
|
||||
newrack.Label = "Tracker"
|
||||
newrack.Visibility = False
|
||||
newrack.Placement = placements[idx]
|
||||
MechanicalGroup.addObject(newrack)
|
||||
frames.append(newrack)
|
||||
except:
|
||||
placements = dataframe[0]
|
||||
frames = []
|
||||
for idx in placements:
|
||||
print(idx)
|
||||
newrack = PVPlantFrame.makeTracker(setup=idx[0])
|
||||
newrack.Label = "Tracker"
|
||||
newrack.Visibility = False
|
||||
newrack.Placement = idx[1]
|
||||
MechanicalGroup.addObject(newrack)
|
||||
frames.append(newrack)
|
||||
|
||||
if self.PVArea.Name.startswith("FrameArea"):
|
||||
self.PVArea.Frames = frames
|
||||
@@ -179,7 +199,7 @@ class _PVPlantPlacementTaskPanel:
|
||||
return np.arange(startx, self.Area.BoundBox.XMax, self.gap_col, dtype=np.int64), \
|
||||
np.arange(starty, self.Area.BoundBox.YMin, -self.gap_row, dtype=np.int64)
|
||||
|
||||
def adjustToTerrain(self, coordinates):
|
||||
def adjustToTerrain_old(self, coordinates):
|
||||
mode = 1
|
||||
terrain = self.Terrain.Mesh
|
||||
|
||||
@@ -276,6 +296,106 @@ class _PVPlantPlacementTaskPanel:
|
||||
placeRegion(df)
|
||||
return df
|
||||
|
||||
def _setup_terrain_interpolator(self):
|
||||
"""Prepara interpolador del terreno para ajuste rápido"""
|
||||
import numpy as np
|
||||
from scipy.interpolate import LinearNDInterpolator
|
||||
|
||||
mesh = self.Terrain.Mesh
|
||||
points = np.array([p.Vector for p in mesh.Points])
|
||||
bbox = self.Area.BoundBox
|
||||
|
||||
# Filtrar puntos dentro del área de trabajo
|
||||
in_bbox = [
|
||||
p for p in points
|
||||
if bbox.XMin <= p[0] <= bbox.XMax and
|
||||
bbox.YMin <= p[1] <= bbox.YMax
|
||||
]
|
||||
|
||||
if not in_bbox:
|
||||
return None
|
||||
|
||||
coords = np.array(in_bbox)
|
||||
return LinearNDInterpolator(coords[:, :2], coords[:, 2])
|
||||
|
||||
def adjustToTerrain(self, coordinates):
|
||||
from scipy.ndimage import label as sclabel
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from scipy import stats
|
||||
import MeshPart
|
||||
|
||||
# Crear matriz binaria
|
||||
arr = np.array([[1 if obj != 0 else 0 for obj in col] for col in coordinates])
|
||||
labeled_array, num_features = sclabel(arr)
|
||||
|
||||
# Construir DataFrame optimizado
|
||||
data = []
|
||||
terrain_interp = self._setup_terrain_interpolator()
|
||||
|
||||
for label in range(1, num_features + 1):
|
||||
cols, rows = np.where(labeled_array == label)
|
||||
for idx, (col, row) in enumerate(zip(cols, rows)):
|
||||
frame_type, placement = coordinates[col][row]
|
||||
data.append({
|
||||
'ID': len(data) + 1,
|
||||
'region': label,
|
||||
'type': frame_type,
|
||||
'column': col,
|
||||
'row': row,
|
||||
'placement': placement
|
||||
})
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
|
||||
# Ajustar al terreno
|
||||
for idx, row in df.iterrows():
|
||||
pl = row['placement']
|
||||
yl = row['type'].Length.Value / 2
|
||||
|
||||
# Calcular puntos extremos
|
||||
top_point = FreeCAD.Vector(pl.x, pl.y + yl, 0)
|
||||
bot_point = FreeCAD.Vector(pl.x, pl.y - yl, 0)
|
||||
|
||||
# Usar interpolador si está disponible
|
||||
if terrain_interp:
|
||||
yy = np.linspace(bot_point.y, top_point.y, 10)
|
||||
xx = np.full(10, pl.x)
|
||||
zz = terrain_interp(xx, yy)
|
||||
|
||||
if not np.isnan(zz).all():
|
||||
slope, intercept, *_ = stats.linregress(yy, zz)
|
||||
z_top = slope * top_point.y + intercept
|
||||
z_bot = slope * bot_point.y + intercept
|
||||
else:
|
||||
z_top = z_bot = 0
|
||||
else:
|
||||
# Fallback a proyección directa
|
||||
line = Part.LineSegment(bot_point, top_point).toShape()
|
||||
projected = MeshPart.projectShapeOnMesh(line, self.Terrain.Mesh, FreeCAD.Vector(0, 0, 1))[0]
|
||||
if len(projected) >= 2:
|
||||
yy = [p.y for p in projected]
|
||||
zz = [p.z for p in projected]
|
||||
slope, intercept, *_ = stats.linregress(yy, zz)
|
||||
z_top = slope * top_point.y + intercept
|
||||
z_bot = slope * bot_point.y + intercept
|
||||
else:
|
||||
z_top = z_bot = 0
|
||||
|
||||
# Actualizar placement
|
||||
new_top = FreeCAD.Vector(top_point.x, top_point.y, z_top)
|
||||
new_bot = FreeCAD.Vector(bot_point.x, bot_point.y, z_bot)
|
||||
|
||||
new_pl = FreeCAD.Placement()
|
||||
new_pl.Base = (new_top + new_bot) / 2
|
||||
new_pl.Rotation = FreeCAD.Rotation(
|
||||
FreeCAD.Vector(-1, 0, 0),
|
||||
new_top - new_bot
|
||||
)
|
||||
df.at[idx, 'placement'] = new_pl
|
||||
|
||||
return df
|
||||
|
||||
def isInside(self, frame, point):
|
||||
if self.Area.isInside(point, 10, True):
|
||||
frame.Placement.Base = point
|
||||
@@ -349,86 +469,68 @@ class _PVPlantPlacementTaskPanel:
|
||||
if countcols == self.form.editColCount.value():
|
||||
offsetcols += valcols
|
||||
countcols = 0
|
||||
print("/n/n")
|
||||
print(cols)
|
||||
|
||||
return self.adjustToTerrain(cols)
|
||||
|
||||
def calculateNonAlignedArray(self):
|
||||
gap_col = FreeCAD.Units.Quantity(self.form.editGapCols.text()).Value
|
||||
gap_row = FreeCAD.Units.Quantity(self.form.editGapRows.text()).Value + max(self.Rack.Shape.BoundBox.XLength,
|
||||
self.Rack.Shape.BoundBox.YLength)
|
||||
offset_x = FreeCAD.Units.Quantity(self.form.editOffsetHorizontal.text()).Value
|
||||
offset_y = FreeCAD.Units.Quantity(self.form.editOffsetVertical.text()).Value
|
||||
pointsx, pointsy = self.getAligments()
|
||||
|
||||
Area = self.calculateWorkingArea()
|
||||
footprints = []
|
||||
for frame in self.FrameSetups:
|
||||
xx = frame.Length.Value
|
||||
yy = frame.Width.Value
|
||||
xx_med = xx / 2
|
||||
yy_med = yy / 2
|
||||
rec = Part.makePolygon([FreeCAD.Vector(-xx_med, -yy_med, 0),
|
||||
FreeCAD.Vector(xx_med, -yy_med, 0),
|
||||
FreeCAD.Vector(xx_med, yy_med, 0),
|
||||
FreeCAD.Vector(-xx_med, yy_med, 0),
|
||||
FreeCAD.Vector(-xx_med, -yy_med, 0)])
|
||||
rec.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), FreeCAD.Vector(0, 1, 0))
|
||||
footprints.append([frame, rec])
|
||||
ref = footprints.pop(0)
|
||||
xx = ref[0].Length.Value
|
||||
yy = ref[0].Width.Value
|
||||
xx_med = xx / 2
|
||||
yy_med = yy / 2
|
||||
|
||||
rec = Part.makePlane(self.Rack.Shape.BoundBox.YLength, self.Rack.Shape.BoundBox.XLength)
|
||||
|
||||
# TODO: revisar todo esto: -----------------------------------------------------------------
|
||||
sel = FreeCADGui.Selection.getSelectionEx()[0]
|
||||
refh = None
|
||||
refv = None
|
||||
|
||||
if len(sel.SubObjects) == 0:
|
||||
refh = refv = Area.Edges[0]
|
||||
|
||||
if len(sel.SubObjects) == 1:
|
||||
refh = refv = sel.SubObjects[0]
|
||||
|
||||
if len(sel.SubObjects) == 2:
|
||||
if sel.SubObjects[0].BoundBox.XLength > sel.SubObjects[1].BoundBox.XLength:
|
||||
refh = sel.SubObjects[0]
|
||||
else:
|
||||
refh = sel.SubObjects[1]
|
||||
|
||||
if sel.SubObjects[0].BoundBox.YLength > sel.SubObjects[1].BoundBox.YLength:
|
||||
refv = sel.SubObjects[0]
|
||||
else:
|
||||
refv = sel.SubObjects[1]
|
||||
|
||||
steps = int((refv.BoundBox.XMax - Area.BoundBox.XMin + offset_x) / gap_col)
|
||||
startx = refv.BoundBox.XMax + offset_x - gap_col * steps
|
||||
# todo end ----------------------------------------------------------------------------------
|
||||
|
||||
start = FreeCAD.Vector(startx, 0.0, 0.0)
|
||||
pointsx = np.arange(start.x, Area.BoundBox.XMax, gap_col)
|
||||
|
||||
if self.form.groupCorridor.isChecked():
|
||||
if (self.form.editColCount.value() > 0):
|
||||
xlen = len(pointsx)
|
||||
count = self.form.editColCount.value()
|
||||
val = FreeCAD.Units.Quantity(self.form.editColGap.text()).Value - (
|
||||
gap_col - min(self.Rack.Shape.BoundBox.XLength, self.Rack.Shape.BoundBox.YLength))
|
||||
while count <= xlen:
|
||||
for i, point in enumerate(pointsx):
|
||||
if i >= count:
|
||||
pointsx[i] += val
|
||||
count += self.form.editColCount.value()
|
||||
# variables for corridors:
|
||||
countcols = 0
|
||||
countrows = 0
|
||||
offsetcols = 0 # ??
|
||||
offsetrows = 0 # ??
|
||||
valcols = FreeCAD.Units.Quantity(self.form.editColGap.text()).Value - (self.gap_col - yy)
|
||||
|
||||
pl = []
|
||||
for point in pointsx:
|
||||
p1 = FreeCAD.Vector(point, Area.BoundBox.YMax, 0.0)
|
||||
p2 = FreeCAD.Vector(point, Area.BoundBox.YMin, 0.0)
|
||||
p1 = FreeCAD.Vector(point, self.Area.BoundBox.YMax, 0.0)
|
||||
p2 = FreeCAD.Vector(point, self.Area.BoundBox.YMin, 0.0)
|
||||
line = Part.makePolygon([p1, p2])
|
||||
|
||||
inter = Area.section([line])
|
||||
inter = self.Area.section([line])
|
||||
pts = [ver.Point for ver in inter.Vertexes] # todo: sort points
|
||||
for i in range(0, len(pts), 2):
|
||||
line = Part.LineSegment(pts[i], pts[i + 1])
|
||||
if line.length() >= rec.BoundBox.YLength:
|
||||
y1 = pts[i].y - rec.BoundBox.YLength
|
||||
cp = rec.copy()
|
||||
cp.Placement.Base = FreeCAD.Vector(pts[i].x - rec.BoundBox.XLength / 2, y1, 0.0)
|
||||
inter = cp.cut([Area])
|
||||
y1 = min([ver.Point.y for ver in inter.Vertexes])
|
||||
pointsy = np.arange(y1, pts[i + 1].y, -gap_row)
|
||||
for point in pointsy:
|
||||
cp = rec.copy()
|
||||
cp.Placement.Base = FreeCAD.Vector(pts[i].x - rec.BoundBox.XLength / 2, point, 0.0)
|
||||
cut = cp.cut([Area], 0)
|
||||
if len(cut.Vertexes) == 0:
|
||||
Part.show(cp)
|
||||
pl.append(point)
|
||||
if line.length() >= ref[1].BoundBox.YLength:
|
||||
y1 = pts[i].y - ref[1].BoundBox.YLength / 2
|
||||
cp = ref[1].copy()
|
||||
cp.Placement.Base = FreeCAD.Vector(pts[i].x, y1, 0.0)
|
||||
Part.show(cp)
|
||||
inter = cp.cut([self.Area])
|
||||
pts1 = [ver.Point for ver in inter.Vertexes]
|
||||
if len(pts1) == 0:
|
||||
continue
|
||||
y1 = min(pts1, key=lambda p: p.y).y
|
||||
pointsy = np.arange(y1, pts[i + 1].y, -self.gap_row)
|
||||
continue
|
||||
for pointy in pointsy:
|
||||
cp = ref[1].copy()
|
||||
cp.Placement.Base = FreeCAD.Vector(pts[i].x + ref[1].BoundBox.XLength / 2, pointy, 0.0)
|
||||
cut = cp.cut([self.Area], 0)
|
||||
#print(y1, " - ", pointy, " - ", len(cut.Vertexes))
|
||||
#if len(cut.Vertexes) == 0:
|
||||
Part.show(cp)
|
||||
pl.append([ref[0], pointy])
|
||||
return pl
|
||||
|
||||
def accept(self):
|
||||
@@ -446,21 +548,6 @@ class _PVPlantPlacementTaskPanel:
|
||||
if (item := self.form.listFrameSetups.item(i)).checkState() == QtCore.Qt.Checked
|
||||
]
|
||||
|
||||
"""seen_lengths = set()
|
||||
tmpframes = []
|
||||
for frame in sorted(items, key=lambda rack: rack.Length, reverse=True):
|
||||
if frame.Length not in seen_lengths:
|
||||
seen_lengths.add(frame.Length)
|
||||
tmpframes.append(frame)
|
||||
'''found = False
|
||||
for tmp in tmpframes:
|
||||
if tmp.Length == frame.Length:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
tmpframes.append(frame)'''
|
||||
self.FrameSetups = tmpframes.copy()"""
|
||||
|
||||
unique_frames = {frame.Length.Value: frame for frame in items}
|
||||
self.FrameSetups = sorted(list(unique_frames.values()), key=lambda rack: rack.Length, reverse=True)
|
||||
|
||||
@@ -479,8 +566,14 @@ class _PVPlantPlacementTaskPanel:
|
||||
dataframe = self.calculateNonAlignedArray()
|
||||
# 3. Adjust to terrain:
|
||||
self.createFrameFromPoints(dataframe)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
|
||||
import Electrical.group as egroup
|
||||
import importlib
|
||||
importlib.reload(egroup)
|
||||
egroup.groupTrackersToTransformers(5000000, self.gap_row + self.FrameSetups[0].Length.Value)
|
||||
|
||||
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.RecomputesFrozen = False
|
||||
params.SetBool("AutoSaveEnabled", auto_save_enabled)
|
||||
|
||||
@@ -489,6 +582,8 @@ class _PVPlantPlacementTaskPanel:
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# function AdjustToTerrain
|
||||
# Take a group of objects and adjust it to the slope and altitude of the terrain mesh. It detects the terrain mesh
|
||||
|
||||
Reference in New Issue
Block a user