321 lines
9.1 KiB
Python
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 |