import FreeCAD import FreeCADGui import Part import _utils import sys from PySide2.QtWidgets import QApplication from PySide2.QtGui import QColor from pivy import quarter, coin, graphics, utils class ConnectionMarker(graphics.Marker): def __init__(self, points): super(ConnectionMarker, self).__init__(points, True) class MarkerOnEdge(graphics.Marker): def __init__(self, tuples): points = list() for t in tuples: points.append(t[0].valueAt(t[1])) super(MarkerOnEdge, self).__init__(points, True) self.edge_params = tuples def __repr__(self): pts = list() for p in self.points: pts.append(p.getValue()) return("MarkerOnEdge(%s)"%pts) def drag(self, mouse_coords, fact=1.): if self.enabled: pts = self.points for i, p in enumerate(pts): p[0] = mouse_coords[0] * fact + self._tmp_points[i][0] p[1] = mouse_coords[1] * fact + self._tmp_points[i][1] p[2] = mouse_coords[2] * fact + self._tmp_points[i][2] v = Part.Vertex(p[0],p[1],p[2]) proj = v.distToShape(self.edge_params[i][0])[1][0][1] # FreeCAD.Console.PrintMessage("%s -> %s\n"%(p.getValue(),proj)) p[0] = proj.x p[1] = proj.y p[2] = proj.z self.edge_params[i][1] = self.edge_params[i][0].Curve.parameter(proj) self.points = pts for foo in self.on_drag: foo() # not yet implemented #class MarkerOnShape(graphics.Marker): #def __init__(self, points, shape=None): #super(MarkerOnShape, self).__init__(points, True) #if isinstance(shape,Part.Shape): #self.support = shape #else: #ci = Part.Geom2d.Circle2d() #ci.Radius = 1e50 #self.support = ci.toShape(shape) #def __repr__(self): ##pts = list() ##for p in self.points: ##pts.append(p.getValue()) #return("MarkerOnShape(%s)"%self.support) #def drag(self, mouse_coords, fact=1.): #if self.enabled: #pts = self.points #for i, p in enumerate(pts): #p[0] = mouse_coords[0] * fact + self._tmp_points[i][0] #p[1] = mouse_coords[1] * fact + self._tmp_points[i][1] #p[2] = mouse_coords[2] * fact + self._tmp_points[i][2] #v = Part.Vertex(p[0],p[1],p[2]) #proj = v.distToShape(self.support)[1][0][1] ## FreeCAD.Console.PrintMessage("%s -> %s\n"%(p.getValue(),proj)) #p[0] = proj.x #p[1] = proj.y #p[2] = proj.z ##self.edge_params[i][1] = self.edge_params[i][0].Curve.parameter(proj) #self.points = pts #for foo in self.on_drag: #foo() class ConnectionPolygon(graphics.Polygon): std_col = "green" def __init__(self, markers): super(ConnectionPolygon, self).__init__( sum([m.points for m in markers], []), True) self.markers = markers for m in self.markers: m.on_drag.append(self.updatePolygon) def updatePolygon(self): self.points = sum([m.points for m in self.markers], []) @property def drag_objects(self): return self.markers def check_dependency(self): if any([m._delete for m in self.markers]): self.delete() class ConnectionLine(graphics.Line): def __init__(self, markers): super(ConnectionLine, self).__init__( sum([m.points for m in markers], []), True) self.markers = markers for m in self.markers: m.on_drag.append(self.updateLine) def updateLine(self): self.points = sum([m.points for m in self.markers], []) @property def drag_objects(self): return self.markers def check_dependency(self): if any([m._delete for m in self.markers]): self.delete() class InterpolationPolygon(object): def __init__(self, points=[], fp = None): self.points = points self.curve = Part.BSplineCurve() self.fp = fp self.root_inserted = False #self.support = None # Not yet implemented if len(points) > 0: if isinstance(points[0],FreeCAD.Vector): self.points = [ConnectionMarker([p]) for p in points] elif isinstance(points[0],(tuple,list)): self.points = [MarkerOnEdge([p]) for p in points] else: FreeCAD.Console.PrintError("InterpolationPolygon : bad input") # Setup coin objects if not FreeCAD.ActiveDocument: appdoc = FreeCAD.newDocument("New") self.guidoc = FreeCADGui.ActiveDocument self.view = self.guidoc.ActiveView self.rm = self.view.getViewer().getSoRenderManager() self.sg = self.view.getSceneGraph() self.setup_InteractionSeparator() self.update_curve() def setup_InteractionSeparator(self): if self.root_inserted: self.sg.removeChild(self.root) self.root = graphics.InteractionSeparator(self.rm) self.root.setName("InteractionSeparator") self.root.pick_radius = 40 self.root.on_drag.append(self.update_curve) # Keyboard callback #self.events = coin.SoEventCallback() self._controlCB = self.root.events.addEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self.controlCB) # populate root node #self.root.addChild(self.events) self.root += self.points self.build_lines() self.root += self.lines self.root.register() self.sg.addChild(self.root) self.root_inserted = True def update_curve(self): pts = list() for p in self.points: pts += p.points FreeCAD.Console.PrintMessage("pts :\n%s\n"%str(pts)) if len(pts) > 1: self.curve.interpolate(pts) #FreeCAD.Console.PrintMessage("update_curve\n") if self.fp: self.fp.Shape = self.curve.toShape() def build_lines(self): self.lines = list() for i in range(len(self.points)-1): line = ConnectionLine([self.points[i], self.points[i+1]]) line.set_color("blue") self.lines.append(line) def controlCB(self, attr, event_callback): event = event_callback.getEvent() if event.getState() == event.UP: FreeCAD.Console.PrintMessage("Key pressed : %s\n"%chr(event.getKey())) if event.getKey() == ord("s"): self.subdivide() elif event.getKey() == ord("q"): self.quit() def subdivide(self): # get selected lines and subdivide them pts = list() for o in self.lines: FreeCAD.Console.PrintMessage("object %s\n"%str(o)) if isinstance(o,ConnectionLine): pts.append(o.markers[0]) if o in self.root.selected_objects: idx = self.lines.index(o) FreeCAD.Console.PrintMessage("Subdividing line #%d\n"%idx) # m1,m2 = o.markers # i1 = self.points.index(m1) # i2 = self.points.index(m2) p1 = o.markers[0].points[0] p2 = o.markers[1].points[0] #mp = (p1+p2)/2.0 #pts.append(ConnectionMarker([mp])) par1 = self.curve.parameter(FreeCAD.Vector(p1)) par2 = self.curve.parameter(FreeCAD.Vector(p2)) midpar = (par1+par2)/2.0 pts.append(ConnectionMarker([self.curve.value(midpar)])) pts.append(self.points[-1]) self.points = pts self.setup_InteractionSeparator() self.update_curve() return(True) def quit(self): #self.sg.removeChild(self.root) #self.root_inserted = False self.root.events.removeEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self._controlCB) self.root.unregister() self.sg.removeChild(self.root) self.root_inserted = False def get_guide_params(): sel = Gui.Selection.getSelectionEx() pts = list() for so in sel: pts.extend(so.PickedPoints) edges = list() for so in sel: for sen in so.SubElementNames: n = eval(sen.lstrip("Edge")) e = so.Object.Shape.Edges[n-1] edges.append(e) inter = list() for pt in pts: sol = None min = 1e50 for e in edges: d,points,info = e.distToShape(Part.Vertex(pt)) if d < min: min = d sol = [e,e.Curve.parameter(points[0][0])] inter.append(sol) return(inter) def main(): obj = FreeCAD.ActiveDocument.addObject("Part::Spline","profile") tups = get_guide_params() ip = InterpolationPolygon(tups, obj) FreeCAD.ActiveDocument.recompute() if __name__ == '__main__': main()