updates
This commit is contained in:
@@ -102,7 +102,7 @@ class _PVPlantPlacementTaskPanel:
|
||||
|
||||
def createFrameFromPoints(self, dataframe):
|
||||
from Mechanical.Frame import PVPlantFrame
|
||||
try:
|
||||
'''try:
|
||||
MechanicalGroup = FreeCAD.ActiveDocument.Frames
|
||||
except:
|
||||
MechanicalGroup = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Frames')
|
||||
@@ -110,14 +110,41 @@ class _PVPlantPlacementTaskPanel:
|
||||
FreeCAD.ActiveDocument.MechanicalGroup.addObject(MechanicalGroup)
|
||||
|
||||
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]
|
||||
name = "Frames-" + self.PVArea.Label
|
||||
if name in [obj.Name for obj in FreeCAD.ActiveDocument.Frames.Group]:
|
||||
MechanicalGroup = FreeCAD.ActiveDocument.getObject(name)[0]
|
||||
else:
|
||||
group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", label)
|
||||
group.Label = label
|
||||
group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", name)
|
||||
group.Label = name
|
||||
MechanicalGroup.addObject(group)
|
||||
MechanicalGroup = group
|
||||
MechanicalGroup = group'''
|
||||
|
||||
doc = FreeCAD.ActiveDocument
|
||||
|
||||
# 1. Obtener o crear el grupo principal 'Frames'
|
||||
main_group_name = "Frames"
|
||||
main_group = doc.getObject(main_group_name)
|
||||
if not main_group:
|
||||
main_group = doc.addObject("App::DocumentObjectGroup", main_group_name)
|
||||
main_group.Label = main_group_name
|
||||
# Asumiendo que existe un grupo 'MechanicalGroup'
|
||||
if hasattr(doc, 'MechanicalGroup'):
|
||||
doc.MechanicalGroup.addObject(main_group)
|
||||
|
||||
# 2. Manejar subgrupo si es necesario
|
||||
group = main_group # Grupo donde se añadirán los marcos
|
||||
if self.form.cbSubfolders.isChecked(): # ¡Corregido: falta de paréntesis!
|
||||
subgroup_name = f"Frames-{self.PVArea.Label}"
|
||||
|
||||
# Buscar subgrupo existente
|
||||
subgroup = next((obj for obj in main_group.Group if obj.Name == subgroup_name), None)
|
||||
|
||||
if not subgroup:
|
||||
subgroup = doc.addObject("App::DocumentObjectGroup", subgroup_name)
|
||||
subgroup.Label = subgroup_name
|
||||
main_group.addObject(subgroup)
|
||||
group = subgroup
|
||||
|
||||
try:
|
||||
placements = dataframe["placement"].tolist()
|
||||
types = dataframe["type"].tolist()
|
||||
@@ -127,7 +154,7 @@ class _PVPlantPlacementTaskPanel:
|
||||
newrack.Label = "Tracker"
|
||||
newrack.Visibility = False
|
||||
newrack.Placement = placements[idx]
|
||||
MechanicalGroup.addObject(newrack)
|
||||
group.addObject(newrack)
|
||||
frames.append(newrack)
|
||||
except:
|
||||
placements = dataframe[0]
|
||||
@@ -138,7 +165,7 @@ class _PVPlantPlacementTaskPanel:
|
||||
newrack.Label = "Tracker"
|
||||
newrack.Visibility = False
|
||||
newrack.Placement = idx[1]
|
||||
MechanicalGroup.addObject(newrack)
|
||||
groupq.addObject(newrack)
|
||||
frames.append(newrack)
|
||||
|
||||
if self.PVArea.Name.startswith("FrameArea"):
|
||||
@@ -160,7 +187,7 @@ class _PVPlantPlacementTaskPanel:
|
||||
if exclusion_areas:
|
||||
prohibited_faces = []
|
||||
for obj in exclusion_areas:
|
||||
face = self.getProjected(obj.Base.Shape)
|
||||
face = self.getProjected(obj.Shape.SubShapes[1])
|
||||
if face.isValid():
|
||||
prohibited_faces.append(face)
|
||||
self.Area = self.Area.cut(prohibited_faces)
|
||||
@@ -474,64 +501,70 @@ class _PVPlantPlacementTaskPanel:
|
||||
|
||||
def calculateNonAlignedArray(self):
|
||||
pointsx, pointsy = self.getAligments()
|
||||
if len(pointsx) == 0:
|
||||
FreeCAD.Console.PrintWarning("No se encontraron alineaciones X.\n")
|
||||
return []
|
||||
|
||||
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)])
|
||||
l = frame.Length.Value
|
||||
w = frame.Width.Value
|
||||
l_med = l / 2
|
||||
w_med = w / 2
|
||||
rec = Part.makePolygon([FreeCAD.Vector(-l_med, -w_med, 0),
|
||||
FreeCAD.Vector( l_med, -w_med, 0),
|
||||
FreeCAD.Vector( l_med, w_med, 0),
|
||||
FreeCAD.Vector(-l_med, w_med, 0),
|
||||
FreeCAD.Vector(-l_med, -w_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
|
||||
|
||||
# variables for corridors:
|
||||
countcols = 0
|
||||
countrows = 0
|
||||
offsetcols = 0 # ??
|
||||
offsetrows = 0 # ??
|
||||
valcols = FreeCAD.Units.Quantity(self.form.editColGap.text()).Value - (self.gap_col - yy)
|
||||
corridor = self.form.groupCorridor.isChecked()
|
||||
corridor_offset = 0
|
||||
count = 0
|
||||
|
||||
pl = []
|
||||
for point in pointsx:
|
||||
p1 = FreeCAD.Vector(point, self.Area.BoundBox.YMax, 0.0)
|
||||
p2 = FreeCAD.Vector(point, self.Area.BoundBox.YMin, 0.0)
|
||||
cols = []
|
||||
for x in pointsx:
|
||||
col=[]
|
||||
x += corridor_offset
|
||||
p1 = FreeCAD.Vector(x, self.Area.BoundBox.YMax, 0.0)
|
||||
p2 = FreeCAD.Vector(x, self.Area.BoundBox.YMin, 0.0)
|
||||
line = Part.makePolygon([p1, p2])
|
||||
|
||||
inter = self.Area.section([line])
|
||||
pts = [ver.Point for ver in inter.Vertexes] # todo: sort points
|
||||
pts = [ver.Point for ver in inter.Vertexes]
|
||||
pts = sorted(pts, key=lambda p: p.y, reverse=True)
|
||||
for i in range(0, len(pts), 2):
|
||||
line = Part.LineSegment(pts[i], pts[i + 1])
|
||||
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)
|
||||
top = pts[i]
|
||||
bootom = pts[i + 1]
|
||||
if top.distanceToPoint(bootom) > footprints[-1][1].BoundBox.YLength:
|
||||
y1 = top.y - (footprints[-1][1].BoundBox.YLength / 2)
|
||||
cp = footprints[-1][1].copy()
|
||||
cp.Placement.Base = FreeCAD.Vector(x + footprints[-1][1].BoundBox.XLength / 2, y1, 0.0)
|
||||
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
|
||||
vtx = [ver.Point for ver in inter.Vertexes]
|
||||
mod = top.y
|
||||
if len(vtx) != 0:
|
||||
mod = min(vtx, key=lambda p: p.y).y
|
||||
#y1 = cp.Placement.Base.y - mod
|
||||
|
||||
tmp = optimized_cut(mod - bootom.y, [ftp[1].BoundBox.YLength for ftp in footprints], 500, 'greedy')
|
||||
for opt in tmp[0]:
|
||||
mod -= (footprints[opt][1].BoundBox.YLength / 2)
|
||||
pl = FreeCAD.Vector(x + footprints[opt][1].BoundBox.XLength / 2, mod, 0.0)
|
||||
cp = footprints[opt][1].copy()
|
||||
if self.isInside(cp, pl):
|
||||
col.append([footprints[opt][0], pl])
|
||||
mod -= ((footprints[opt][1].BoundBox.YLength / 2) + 500)
|
||||
Part.show(cp)
|
||||
|
||||
if corridor and len(col) > 0:
|
||||
count += 1
|
||||
if count == self.form.editColCount.value():
|
||||
corridor_offset += 12000
|
||||
count = 0
|
||||
|
||||
cols.append(cols)
|
||||
return self.adjustToTerrain(cols)
|
||||
|
||||
def accept(self):
|
||||
from datetime import datetime
|
||||
@@ -583,6 +616,115 @@ class _PVPlantPlacementTaskPanel:
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
def optimized_cut(L_total, piezas, margen=0, metodo='auto'):
|
||||
"""
|
||||
Encuentra la combinación óptima de piezas para minimizar el desperdicio,
|
||||
considerando un margen entre piezas.
|
||||
|
||||
Args:
|
||||
L_total (int): Longitud total del material.
|
||||
piezas (list): Lista de longitudes de los patrones de corte.
|
||||
margen (int): Espacio perdido entre piezas consecutivas.
|
||||
metodo (str): 'dp' para programación dinámica, 'greedy' para voraz, 'auto' para selección automática.
|
||||
|
||||
Returns:
|
||||
tuple: (piezas_seleccionadas, desperdicio)
|
||||
"""
|
||||
# Filtrar piezas inválidas
|
||||
piezas = [p for p in piezas if 0 < p <= L_total]
|
||||
if not piezas:
|
||||
return [], L_total
|
||||
|
||||
# Transformar longitudes y longitud total con margen
|
||||
longitudes_aumentadas = [p + margen for p in piezas]
|
||||
L_total_aumentado = L_total + margen
|
||||
|
||||
# Selección automática de método
|
||||
if metodo == 'auto':
|
||||
if L_total_aumentado <= 10000 and len(piezas) <= 100:
|
||||
metodo = 'dp'
|
||||
else:
|
||||
metodo = 'greedy'
|
||||
|
||||
if metodo == 'dp':
|
||||
n = len(piezas)
|
||||
dp = [0] * (L_total_aumentado + 1)
|
||||
parent = [-1] * (L_total_aumentado + 1) # Almacena índices de piezas usadas
|
||||
|
||||
# Llenar la tabla dp y parent
|
||||
for j in range(1, L_total_aumentado + 1):
|
||||
for i in range(n):
|
||||
p_aum = longitudes_aumentadas[i]
|
||||
if p_aum <= j:
|
||||
if dp[j] < dp[j - p_aum] + p_aum:
|
||||
dp[j] = dp[j - p_aum] + p_aum
|
||||
parent[j] = i # Guardar índice de la pieza
|
||||
|
||||
# Reconstruir solución desde el final
|
||||
current = L_total_aumentado
|
||||
seleccion_indices = []
|
||||
while current > 0 and parent[current] != -1:
|
||||
i = parent[current]
|
||||
seleccion_indices.append(i)
|
||||
current -= longitudes_aumentadas[i]
|
||||
|
||||
# Calcular desperdicio real
|
||||
k = len(seleccion_indices)
|
||||
if k == 0:
|
||||
desperdicio = L_total
|
||||
else:
|
||||
suma_original = sum(piezas[i] for i in seleccion_indices)
|
||||
desperdicio = L_total - suma_original - margen * (k - 1)
|
||||
|
||||
return seleccion_indices, desperdicio
|
||||
|
||||
elif metodo == 'greedy':
|
||||
# Crear lista con índices y longitudes aumentadas
|
||||
lista_con_indices = [(longitudes_aumentadas[i], i) for i in range(len(piezas))]
|
||||
lista_con_indices.sort(key=lambda x: x[0], reverse=True) # Ordenar descendente
|
||||
|
||||
seleccion_indices = []
|
||||
restante = L_total_aumentado
|
||||
|
||||
# Seleccionar piezas vorazmente
|
||||
for p_aum, i in lista_con_indices:
|
||||
while restante >= p_aum:
|
||||
seleccion_indices.append(i)
|
||||
restante -= p_aum
|
||||
|
||||
# Calcular desperdicio real
|
||||
k = len(seleccion_indices)
|
||||
if k == 0:
|
||||
desperdicio = L_total
|
||||
else:
|
||||
suma_original = sum(piezas[i] for i in seleccion_indices)
|
||||
desperdicio = L_total - suma_original - margen * (k - 1)
|
||||
|
||||
return seleccion_indices, desperdicio
|
||||
|
||||
|
||||
# Ejemplo de uso
|
||||
'''if __name__ == "__main__":
|
||||
L_total = 100
|
||||
piezas = [25, 35, 40, 20, 15, 30, 50]
|
||||
margen = 5
|
||||
|
||||
print("Solución óptima con margen (programación dinámica):")
|
||||
seleccion, desperd = corte_optimizado(L_total, piezas, margen, 'dp')
|
||||
print(f"Piezas usadas: {seleccion}")
|
||||
print(f"Margen entre piezas: {margen} cm")
|
||||
print(f"Material útil: {sum(seleccion)} cm")
|
||||
print(f"Espacio usado por márgenes: {(len(seleccion) - 1) * margen} cm")
|
||||
print(f"Desperdicio total: {desperd} cm")
|
||||
|
||||
print("\nSolución aproximada con margen (algoritmo voraz):")
|
||||
seleccion_g, desperd_g = corte_optimizado(L_total, piezas, margen, 'greedy')
|
||||
print(f"Piezas usadas: {seleccion_g}")
|
||||
print(f"Margen entre piezas: {margen} cm")
|
||||
print(f"Material útil: {sum(seleccion_g)} cm")
|
||||
print(f"Espacio usado por márgenes: {(len(seleccion_g) - 1) * margen} cm")
|
||||
print(f"Desperdicio total: {desperd_g} cm")'''
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# function AdjustToTerrain
|
||||
|
||||
Reference in New Issue
Block a user