Files
PVPlant/Utils/PVPlantUtils.py
2025-03-28 19:40:11 +06:00

321 lines
9.1 KiB
Python

import math
import FreeCAD
import Part
if FreeCAD.GuiUp:
from PySide import QtCore
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
def VectorToPoint(vector):
from shapely.geometry import Point
if True:
return Point(vector.x, vector.y)
else:
return Point(vector.x, vector.y, vector.z)
# buscar el camino más corto:
from collections import defaultdict
class Graph():
def __init__(self):
"""
self.edges is a dict of all possible next nodes
e.g. {'X': ['A', 'B', 'C', 'E'], ...}
self.weights has all the weights between two nodes,
with the two nodes as a tuple as the key
e.g. {('X', 'A'): 7, ('X', 'B'): 2, ...}
"""
self.edges = defaultdict(list)
self.weights = {}
def add_edge(self, from_node, to_node, weight):
# Note: assumes edges are bi-directional
self.edges[from_node].append(to_node)
self.edges[to_node].append(from_node)
self.weights[(from_node, to_node)] = weight
self.weights[(to_node, from_node)] = weight
def dijsktra(graph, initial, end):
# shortest paths is a dict of nodes
# whose value is a tuple of (previous node, weight)
shortest_paths = {initial: (None, 0)}
current_node = initial
visited = set()
while current_node != end:
visited.add(current_node)
destinations = graph.edges[current_node]
weight_to_current_node = shortest_paths[current_node][1]
for next_node in destinations:
weight = graph.weights[(current_node, next_node)] + weight_to_current_node
if next_node not in shortest_paths:
shortest_paths[next_node] = (current_node, weight)
else:
current_shortest_weight = shortest_paths[next_node][1]
if current_shortest_weight > weight:
shortest_paths[next_node] = (current_node, weight)
next_destinations = {node: shortest_paths[node] for node in shortest_paths if node not in visited}
if not next_destinations:
return "Route Not Possible"
# next node is the destination with the lowest weight
current_node = min(next_destinations, key=lambda k: next_destinations[k][1])
# Work back through destinations in shortest path
path = []
while current_node is not None:
path.append(current_node)
next_node = shortest_paths[current_node][0]
current_node = next_node
# Reverse path
path = path[::-1]
return path
'''
Flatten a wire to 2 axis: X - Y
'''
def FlattenWire(wire):
pts = []
xx = 0
for i, ver in enumerate(wire.Vertexes):
if i == 0:
pts.append(FreeCAD.Vector(xx, ver.Point.z, 0))
else:
xx += (ver.Point - wire.Vertexes[i - 1].Point).Length
pts.append(FreeCAD.Vector(xx, ver.Point.z, 0))
return Part.makePolygon(pts)
def flattenWire(wire):
pts = []
for i, ver in enumerate(wire.Vertexes):
point = FreeCAD.Vector(ver.Point)
point.z = 0
pts.append(point)
return Part.makePolygon(pts)
def simplifyWire(wire):
if not wire.isDerivedFrom("Part::Part2DObject"):
wire = flattenWire(wire)
pts = [ver.Point for ver in wire.Vertexes]
for i in range(len(pts)-2):
'''
'''
'''for i in range(len(wire.Edges) - 1):
ed1 = wire.Edges[i]
ed2 = wire.Edges[i + 1]
vec1 = ed1.Vertexes[1].Point.sub(ed1.Vertexes[0].Point)
vec2 = ed2.Vertexes[1].Point.sub(ed2.Vertexes[0].Point)
angle = vec1.getAngle(vec2)
if angle == 0:'''
def getPointsFromVertexes(Vertexes):
return [ver.Point for ver in Vertexes]
def makeProfileFromTerrain(path):
import PVPlantSite
terrain = PVPlantSite.get().Terrain
if terrain:
return terrain.Shape.makeParallelProjection(path.Shape.Wires[0], FreeCAD.Vector(0, 0, 1))
def angleToPercent(deg):
''' ángulo en porcentaje = tan(ángulo en grados) * 100% '''
return math.tan(math.radians(deg)) * 100
def PercentToAngle(percent):
''' ángulo en grados = arctan(ángulo en porcentaje / 100%) '''
return math.degrees(math.atan(percent / 100))
def getEWAngle(frame1, frame2):
vec = frame2.Placement.Base.sub(frame1.Placement.Base)
rad = vec.getAngle(FreeCAD.Vector(1, 0, 0))
deg = math.degrees(rad)
if deg > 90:
deg = 180 - deg
return deg
def simplifyWire(wire):
import math
closed = False
points = [ver.Point for ver in wire.Vertexes]
if points[0] == points[-1]:
points.pop(-1)
closed = True
if len(points) < 3:
return wire
'''cnt = 0
while cnt < (len(points) - 2):
p1 = cnt + 1
if p1 == len(points)-2:
p1 = 0
elif p1 == len(points)-1:
p1 = 1
p2 = p1 + 1
vec1 = points[p1].sub(points[cnt])
vec2 = points[p2].sub(points[p1])
angle = vec2.getAngle(vec1)
# Edges have same direction
if (round(angle, 2) == 0) or (round(angle - math.pi, 2) == 0):
points.pop(cnt + 1)
else:
cnt += 1'''
i = 0
while i < len(points) - 2:
p1 = points[(i + 1) % len(points)]
p2 = points[(i + 2) % len(points)]
vec1 = p1 - points[i]
vec2 = p2 - p1
angle = vec2.getAngle(vec1)
# Agregar tolerancia para el ángulo
if round(angle, 2) == 0 or round(angle - math.pi, 2) == 0:
points.pop(i + 1)
else:
i += 1
#if closed:
points.append(FreeCAD.Vector(points[0]))
pol = Part.makePolygon(points)
return pol
def getProjected(shape, direction=FreeCAD.Vector(0, 0, 1)): # Based on Draft / shape2dview
""" returns projected edges from a shape and a direction """
import Part, TechDraw
edges = []
groups = TechDraw.projectEx(shape, direction)
for g in groups[0:5]:
if g:
if len(g.Edges) > 0:
edges.extend(g.Edges)
if len(edges) > 1:
ow = TechDraw.findOuterWire(edges)
else:
ow = Part.Wire(edges[0])
return ow
def findObjects(classtype):
objects = FreeCAD.ActiveDocument.Objects
objlist = list()
for object in objects:
if hasattr(object, "Proxy"):
if object.Proxy.Type == classtype:
objlist.append(object)
return objlist
def getClosePoints(sh1, angle):
'''
sh1= reference
tag angle = height / offset
- dato conocido: angle
- si conocemos offset (= 1 mm) => height = offset * tag angle
'''
import Draft, math, Part
import MeshPart as mp
pts = []
for ver in sh1.Vertexes:
point = FreeCAD.Vector(ver.Point)
point.z = 0
pts.append([point, ver])
minsh1 = min(pts, key=lambda x: x[1].Z)[1]
projected = getProjected(max(sh1.Wires, key=lambda x: x.Length))
sh2 = projected.makeOffset2D(1, 0, False, False, True)
height = math.tan(math.radians(angle))
sh2.Placement.Base.z = - height
sh2pts = []
for edge in sh2.Edges:
count = 0
if issubclass(type(edge.Curve), Part.Circle):
v1 = edge.Vertexes[0].Point.sub(edge.Curve.Center)
v2 = edge.Curve.Center.sub(edge.Vertexes[0].Point)
count = int(v1.getAngle(v2) / math.radians(5))
if count > 0:
sh2pts.extend(edge.discretize(Number=count))
else:
sh2pts.append(edge.Vertexes[0].Point)
def getline(real, pro):
land = FreeCAD.ActiveDocument.Mesh002.Mesh
vec = FreeCAD.Vector(real.Point)
vec.z = 0
vec = vec.sub(pro)
vec.normalize()
vec.Length = 10000
vec = real.Point.sub(vec)
line = Part.LineSegment(real.Point, vec)
plane = Part.Plane(minsh1, FreeCAD.Vector(0, 0, 1))
tmp = mp.projectPointsOnMesh([real.Point,], land, vec)
if len(tmp) > 0:
return tmp[0]
#Draft.makeLine(real.Point, vec)
#return vec
lines = []
if len(sh1.Vertexes) >= len(sh2pts):
for ver in sh1.Vertexes:
point = FreeCAD.Vector(ver.Point)
point.z = 0
mn = min(sh2.Vertexes, key=lambda x: x.Point.sub(point).Length)
getpoints(ver, mn)
else:
from datetime import datetime
starttime = datetime.now()
for point in sh2pts:
mn = min(pts, key=lambda x: x[0].sub(point).Length)
lines.append(getline(mn[1], point))
total_time = datetime.now() - starttime
print(" -- Tiempo tardado en ajustar al terreno:", total_time)
ret = [ver.Point for ver in sh1.Vertexes]
ret.extend(lines)
Draft.makeWire(lines, closed=True)
import Mesh
from MeshTools.Triangulation import Triangulate
m = Triangulate(ret, MaxlengthLE=4000)
Mesh.show(m)
import FreeCAD as App
def offset_wire(wire, distance):
# Crear un tubo alrededor de la wire original
tube = Part.makeTube(wire, distance)
# Offsetear el tubo
offset_wire = tube.makeOffsetShape(distance)
return offset_wire