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 findObjects(classtype): return [obj for obj in FreeCAD.ActiveDocument.Objects if hasattr(obj, "Proxy") and hasattr(obj.Proxy, "Type") and obj.Proxy.Type == classtype] 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