Trajectory.py
1 # -*- coding: utf-8 -*-
2 
3 # ***************************************************************************
4 # * *
5 # * Animate workbench - FreeCAD Workbench for lightweight animation *
6 # * Copyright (c) 2019 Jiří Valášek jirka362@gmail.com *
7 # * *
8 # * This file is part of the Animate workbench. *
9 # * *
10 # * This program is free software; you can redistribute it and/or modify *
11 # * it under the terms of the GNU Lesser General Public License (LGPL) *
12 # * as published by the Free Software Foundation; either version 2 of *
13 # * the License, or (at your option) any later version. *
14 # * for detail see the LICENCE text file. *
15 # * *
16 # * Animate workbench is distributed in the hope that it will be useful, *
17 # * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 # * GNU Lesser General Public License for more details. *
20 # * *
21 # * You should have received a copy of the GNU Library General Public *
22 # * License along with Animate workbench; if not, write to the Free *
23 # * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
24 # * MA 02111-1307 USA *
25 # * *
26 # ***************************************************************************/
27 
28 
35 
36 import FreeCAD
37 import FreeCADGui
38 
39 from PySide2.QtWidgets import QDialogButtonBox
40 from PySide2.QtWidgets import QMessageBox
41 from PySide2.QtCore import QObject
42 from bisect import bisect
43 from pivy import coin
44 from os import path
45 
46 
47 PATH_TO_ICONS = path.join(FreeCAD.getHomePath(), "Mod", "Animate", "Resources",
48  "Icons")
49 
50 
51 PATH_TO_UI = path.join(FreeCAD.getHomePath(), "Mod", "Animate", "Resources",
52  "UIs")
53 
54 
62 
63 class TrajectoryPanel(QObject):
64 
65 
67 
68 
70 
71 
73 
74 
86 
87  def __init__(self, trajectories, forms):
88  super(TrajectoryPanel, self).__init__()
89  self.trajectories = trajectories
90 
91  # Disable editing of Trajectory properties and store previous
92  # times from trajectories
93  self.previous_times = []
94  for trajectory in trajectories:
95  self.previous_times.append(trajectory.Time)
96  for prop in trajectory.PropertiesList:
97  trajectory.setEditorMode(prop, 1)
98  # Leave some properties hidden
99  trajectory.setEditorMode("Placement", 2)
100  trajectory.setEditorMode("ValidTrajectory", 2)
101 
102  # Add QDialogs to be displayed in freeCAD
103  self.form = forms
104 
105  # Add callbacks to sliders on all forms and muve sliders to a position
106  # corresponding with time values
107  for i in range(len(forms)):
108  forms[i].sld_time.valueChanged.connect(
109  lambda value, form=forms[i],
110  trajectory=trajectories[i]:
111  self.sliderChanged(value, form, trajectory))
112  val = (100 * (trajectories[i].Time
113  - trajectories[i].Timestamps[0])) / \
114  (trajectory.Timestamps[-1]
115  - trajectory.Timestamps[0])
116  forms[i].sld_time.setValue(val)
117 
118 
129 
130  def sliderChanged(self, value, form, trajectory):
131  # Compute a time from the slider position and timestamp range
132  t = value * (trajectory.Timestamps[-1]
133  - trajectory.Timestamps[0]) / 100 \
134  + trajectory.Timestamps[0]
135 
136  # Update the time in a trajectory and
137  # recompute the document to show changes
138  trajectory.Time = t
139  form.lbl_time.setText("Time: " + ("%5.3f" % t))
140  FreeCAD.ActiveDocument.recompute()
141  FreeCADGui.updateGui()
142 
143 
148 
149  def close(self):
150  # Allow editing of Trajecotry properties again
151  for trajectory in self.trajectories:
152  for prop in trajectory.PropertiesList:
153  trajectory.setEditorMode(prop, 0)
154  trajectory.ViewObject.Proxy.panel = None
155 
156  # Keep some properties read-only state if they were in it before
157  trajectory.setEditorMode("ObjectPlacement", 1)
158  trajectory.setEditorMode("ParentFramePlacement", 1)
159 
160  # Keep some properties hidden state if they were hidden before
161  trajectory.setEditorMode("Placement", 2)
162  trajectory.setEditorMode("ValidTrajectory", 2)
163  FreeCADGui.Control.closeDialog()
164 
165 
170 
171  def reject(self):
172  # Return Trajectory times to previous values
173  for i in range(len(self.trajectories)):
174  self.trajectories[i].Time = self.previous_times[i]
175 
176  # Close the panel and recompute the document to show changes
177  self.close()
178  FreeCAD.ActiveDocument.recompute()
179  FreeCADGui.updateGui()
180 
181 
186 
187  def accept(self):
188  # Close the panel
189  self.close()
190 
191 
196 
197  def getStandardButtons(self, *args):
198  return QDialogButtonBox.Ok | QDialogButtonBox.Cancel
199 
200 
205 
207  return False
208 
209 
214 
216  return True
217 
218 
223 
225  return True
226 
227 
228 
243 
245 
246 
248 
249 
258 
259  def __init__(self, fp):
260  # Add (and preset) properties
261  self.setProperties(fp)
262  fp.Proxy = self
263 
264 
275 
276  def onChanged(self, fp, prop):
277  # Check that a trajectory has valid format
278  if self.is_trajectory_property(prop):
279  traj_valid = self.is_ValidTrajectory(
280  fp.Timestamps, fp.TranslationX, fp.TranslationY,
281  fp.TranslationZ, fp.RotationPointX, fp.RotationPointY,
282  fp.RotationPointZ, fp.RotationAxisX, fp.RotationAxisY,
283  fp.RotationAxisZ, fp.RotationAngle)
284 # traj_valid = self.is_ValidTrajectory(trajectory=traj)
285  if traj_valid != fp.ValidTrajectory:
286  fp.ValidTrajectory = traj_valid
287 
288  elif prop == "Placement":
289  # Propagate the Placement updates down the chain
290  if hasattr(fp, "Group") and len(fp.Group) != 0:
291  for child in fp.Group:
292  child.ParentFramePlacement = fp.Placement
293  child.purgeTouched()
294 
295  # Display animated objects in a pose specified by the trajectory
296  # and current time
297  if hasattr(fp, "AnimatedObjects") and len(fp.AnimatedObjects) != 0:
298  for o in fp.AnimatedObjects:
299  o.Placement = fp.Placement
300  o.purgeTouched()
301 
302  elif prop == "ParentFramePlacement":
303  # If parent frame changed, recompute placement
304  fp.Placement = fp.ParentFramePlacement.multiply(
305  fp.ObjectPlacement)
306 
307 
315 
316  def execute(self, fp):
317  # Check that current trajectory has valid format
318  if not fp.ValidTrajectory:
319  FreeCAD.Console.PrintWarning(fp.Name + ".execute(): Trajectory " +
320  "is not in a valid format.\n")
321  return
322 
323  # Update placement according to current time and trajectory
324  indices, weights = self.find_timestamp_indices_and_weights(fp)
325 
326  self.pose["position"] = (weights[0]*fp.TranslationX[indices[0]] +
327  weights[1]*fp.TranslationX[indices[1]],
328  weights[0]*fp.TranslationY[indices[0]] +
329  weights[1]*fp.TranslationY[indices[1]],
330  weights[0]*fp.TranslationZ[indices[0]] +
331  weights[1]*fp.TranslationZ[indices[1]])
332  self.pose["rot_axis"] = (weights[0]*fp.RotationAxisX[indices[0]]
333  + weights[1]*fp.RotationAxisX[indices[1]],
334  weights[0]*fp.RotationAxisY[indices[0]]
335  + weights[1]*fp.RotationAxisY[indices[1]],
336  weights[0]*fp.RotationAxisZ[indices[0]]
337  + weights[1]*fp.RotationAxisZ[indices[1]])
338  self.pose["rot_point"] = (weights[0]*fp.RotationPointX[indices[0]]
339  + weights[1]*fp.RotationPointX[indices[1]],
340  weights[0]*fp.RotationPointY[indices[0]]
341  + weights[1]*fp.RotationPointY[indices[1]],
342  weights[0]*fp.RotationPointZ[indices[0]]
343  + weights[1]*fp.RotationPointZ[indices[1]])
344  self.pose["rot_angle"] = (weights[0]*fp.RotationAngle[indices[0]]
345  + weights[1]*fp.RotationAngle[indices[1]])
346 
347  fp.ObjectPlacement = FreeCAD.Placement(
348  FreeCAD.Vector(self.pose["position"][0],
349  self.pose["position"][1],
350  self.pose["position"][2]),
351  FreeCAD.Rotation(FreeCAD.Vector(self.pose["rot_axis"][0],
352  self.pose["rot_axis"][1],
353  self.pose["rot_axis"][2]),
354  self.pose["rot_angle"]),
355  FreeCAD.Vector(self.pose["rot_point"][0],
356  self.pose["rot_point"][1],
357  self.pose["rot_point"][2],))
358  fp.Placement = fp.ParentFramePlacement.multiply(
359  fp.ObjectPlacement)
360 
361 
370 
371  def onDocumentRestored(self, fp):
372  fp.ViewObject.Proxy.setProperties(fp.ViewObject)
373  self.setProperties(fp)
374 
375  # supporting methods-------------------------------------------------------
376 
384 
385  def setProperties(self, fp):
386  self.pose = {"position": (0, 0, 0),
387  "rot_axis": (0, 0, 0),
388  "rot_point": (0, 0, 0),
389  "rot_angle": None}
390 
391  # Add (and preset) properties
392  # Animation properties
393  if not hasattr(fp, "ValidTrajectory"):
394  fp.addProperty("App::PropertyBool", "ValidTrajectory", "General",
395  "This property records if trajectory was changed."
396  ).ValidTrajectory = False
397  if not hasattr(fp, "AnimatedObjects"):
398  fp.addProperty("App::PropertyLinkListGlobal", "AnimatedObjects",
399  "General", "Objects that will be animated.")
400  if not hasattr(fp, "Interpolate"):
401  fp.addProperty("App::PropertyBool", "Interpolate", "General",
402  "Interpolate trajectory between timestamps."
403  ).Interpolate = True
404  if not hasattr(fp, "AllowServer"):
405  fp.addProperty("App::PropertyBool", "AllowServer", "General",
406  "Should this object allow a Server object to "
407  + "change it.").AllowServer = True
408  if not hasattr(fp, "AllowControl"):
409  fp.addProperty("App::PropertyBool", "AllowControl", "General",
410  "Should this object allow a Control object "
411  + " to change it."
412  ).AllowControl = True
413  if not hasattr(fp, "Time"):
414  fp.addProperty("App::PropertyFloat", "Time", "General",
415  "Animation time in seconds.").Time = 0
416  if not hasattr(fp, "ParentFramePlacement"):
417  fp.addProperty("App::PropertyPlacement", "ParentFramePlacement",
418  "General", "Current placement of a Parent Frame.")
419  if not hasattr(fp, "ObjectPlacement"):
420  fp.addProperty("App::PropertyPlacement", "ObjectPlacement",
421  "General",
422  "Current Object placement in a Parent Frame.")
423 
424  # Trajectory properties
425  if not hasattr(fp, "Timestamps"):
426  fp.addProperty("App::PropertyFloatList", "Timestamps",
427  "Trajectory", "Timestamps at which we define\n" +
428  "translation and rotation.")
429  if not hasattr(fp, "TranslationX"):
430  fp.addProperty("App::PropertyFloatList", "TranslationX",
431  "Trajectory",
432  "Object translation along global X direction.")
433  if not hasattr(fp, "TranslationY"):
434  fp.addProperty("App::PropertyFloatList", "TranslationY",
435  "Trajectory",
436  "Object translation along global Y direction.")
437  if not hasattr(fp, "TranslationZ"):
438  fp.addProperty("App::PropertyFloatList", "TranslationZ",
439  "Trajectory",
440  "Object translation along global Z direction.")
441 
442  if not hasattr(fp, "RotationPointX"):
443  fp.addProperty("App::PropertyFloatList", "RotationPointX",
444  "Trajectory",
445  "Object rotation point X coordinate.")
446  if not hasattr(fp, "RotationPointY"):
447  fp.addProperty("App::PropertyFloatList", "RotationPointY",
448  "Trajectory",
449  "Object rotation point Y coordinate.")
450  if not hasattr(fp, "RotationPointZ"):
451  fp.addProperty("App::PropertyFloatList", "RotationPointZ",
452  "Trajectory",
453  "Object rotation point Z coordinate.")
454 
455  if not hasattr(fp, "RotationAxisX"):
456  fp.addProperty("App::PropertyFloatList", "RotationAxisX",
457  "Trajectory", "Object rotation axis component X.")
458  if not hasattr(fp, "RotationAxisY"):
459  fp.addProperty("App::PropertyFloatList", "RotationAxisY",
460  "Trajectory", "Object rotation axis component Y.")
461  if not hasattr(fp, "RotationAxisZ"):
462  fp.addProperty("App::PropertyFloatList", "RotationAxisZ",
463  "Trajectory", "Object rotation axis component Z.")
464  if not hasattr(fp, "RotationAngle"):
465  fp.addProperty("App::PropertyFloatList", "RotationAngle",
466  "Trajectory",
467  "Rotation angle in degrees.")
468 
469  # Frame properties
470  if not hasattr(fp, "ShowFrame"):
471  fp.addProperty("App::PropertyBool", "ShowFrame", "Frame",
472  "Show a frame for current pose."
473  ).ShowFrame = True
474  if not hasattr(fp, "FrameTransparency"):
475  fp.addProperty("App::PropertyPercent", "FrameTransparency",
476  "Frame", "Transparency of the frame in percents."
477  ).FrameTransparency = 0
478  if not hasattr(fp, "ShowFrameArrowheads"):
479  fp.addProperty("App::PropertyBool", "ShowFrameArrowheads", "Frame",
480  "Show arrowheads for frame axis arrow's."
481  ).ShowFrameArrowheads = True
482  if not hasattr(fp, "FrameArrowheadLength"):
483  fp.addProperty("App::PropertyFloatConstraint",
484  "FrameArrowheadLength", "Frame",
485  "Frame axis arrow's arrowhead length.\n"
486  + "Range is < 1.0 | 1e6 >."
487  ).FrameArrowheadLength = (10, 1.0, 1e6, 1)
488  else:
489  fp.FrameArrowheadLength = (fp.FrameArrowheadLength, 1.0, 1e6, 1)
490  if not hasattr(fp, "FrameArrowheadRadius"):
491  fp.addProperty("App::PropertyFloatConstraint",
492  "FrameArrowheadRadius", "Frame",
493  "Frame axis arrow's arrowhead bottom radius.\n"
494  + "Range is < 0.5 | 1e6 >."
495  ).FrameArrowheadRadius = (5, 0.5, 1e6, 0.5)
496  else:
497  fp.FrameArrowheadRadius = (fp.FrameArrowheadRadius, 0.5, 1e6, 0.5)
498  if not hasattr(fp, "ShaftLength"):
499  fp.addProperty("App::PropertyFloatConstraint", "ShaftLength",
500  "Frame", "Frame axis arrow's shaft length.\n"
501  + "Range is < 1.0 | 1e6 >."
502  ).ShaftLength = (20, 1.0, 1e6, 1)
503  else:
504  fp.ShaftLength = (fp.ShaftLength, 1.0, 1e6, 1)
505  if not hasattr(fp, "ShaftWidth"):
506  fp.addProperty("App::PropertyFloatConstraint", "ShaftWidth",
507  "Frame", "Frame axis arrow's shaft width.\n"
508  + "Range is < 1.0 | 64 >."
509  ).ShaftWidth = (4, 1.0, 64, 1)
510  else:
511  fp.ShaftWidth = (fp.ShaftWidth, 1.0, 64, 1)
512  if not hasattr(fp, "ShowFrameLabels"):
513  fp.addProperty("App::PropertyBool", "ShowFrameLabels",
514  "Frame", "Show label for frame axes."
515  ).ShowFrameLabels = True
516 
517  # Rotation axis properties
518  if not hasattr(fp, "ShowRotationAxis"):
519  fp.addProperty("App::PropertyBool", "ShowRotationAxis",
520  "RotationAxis",
521  "Show currently used rotation axis."
522  ).ShowRotationAxis = True
523  if not hasattr(fp, "AxisLength"):
524  fp.addProperty("App::PropertyFloatConstraint", "AxisLength",
525  "RotationAxis", "The rotation axis length.\n"
526  + "Range is < 1.0 | 1e6 >."
527  ).AxisLength = (20, 1.0, 1e6, 1)
528  else:
529  fp.AxisLength = (fp.AxisLength, 1.0, 1e6, 1)
530  if not hasattr(fp, "AxisWidth"):
531  fp.addProperty("App::PropertyFloatConstraint", "AxisWidth",
532  "RotationAxis", "The rotation axis width.\n"
533  + "Range is < 1.0 | 64 >."
534  ).AxisWidth = (4, 1.0, 64, 1)
535  else:
536  fp.AxisWidth = (fp.AxisWidth, 1.0, 64, 1)
537  if not hasattr(fp, "AxisColor"):
538  fp.addProperty("App::PropertyColor", "AxisColor",
539  "RotationAxis", "The rotation axis width."
540  ).AxisColor = (1.000, 0.667, 0.000)
541  if not hasattr(fp, "AxisTransparency"):
542  fp.addProperty("App::PropertyPercent", "AxisTransparency",
543  "RotationAxis",
544  "Transparency of the rotation axis in percents."
545  ).AxisTransparency = 0
546  if not hasattr(fp, "ShowAxisArrowhead"):
547  fp.addProperty("App::PropertyBool", "ShowAxisArrowhead",
548  "RotationAxis", "Show arrowhead for axis arrow."
549  ).ShowAxisArrowhead = True
550  if not hasattr(fp, "AxisArrowheadLength"):
551  fp.addProperty("App::PropertyFloatConstraint",
552  "AxisArrowheadLength", "RotationAxis",
553  "Frame axis arrow's arrowhead length.\n"
554  + "Range is < 1.0 | 1e6 >."
555  ).AxisArrowheadLength = (10, 1.0, 1e6, 1)
556  else:
557  fp.AxisArrowheadLength = (fp.AxisArrowheadLength, 1.0, 1e6, 1)
558  if not hasattr(fp, "AxisArrowheadRadius"):
559  fp.addProperty("App::PropertyFloatConstraint",
560  "AxisArrowheadRadius", "RotationAxis",
561  "Frame axis arrow's arrowhead bottom radius.\n"
562  + "Range is < 0.5 | 1e6 >."
563  ).AxisArrowheadRadius = (5, 0.5, 1e6, 0.5)
564  else:
565  fp.AxisArrowheadRadius = (fp.AxisArrowheadRadius, 0.5, 1e6, 0.5)
566  if not hasattr(fp, "ShowAxisLabel"):
567  fp.addProperty("App::PropertyBool", "ShowAxisLabel",
568  "RotationAxis", "Show label for rotation axis."
569  ).ShowAxisLabel = True
570 
571  # Label properties
572  if not hasattr(fp, "FontSize"):
573  fp.addProperty("App::PropertyIntegerConstraint", "FontSize",
574  "Labels", "Label font size.\n"
575  + "Range is < 1 | 100 >."
576  ).FontSize = (10, 1, 100, 1)
577  else:
578  fp.FontSize = (fp.FontSize, 1, 100, 1)
579  if not hasattr(fp, "DistanceToAxis"):
580  fp.addProperty("App::PropertyFloatConstraint", "DistanceToAxis",
581  "Labels", "Distance from label to its axis.\n"
582  + "Range is < 0.5 | 1e6 >."
583  ).DistanceToAxis = (5, 0.5, 1e6, 0.5)
584  else:
585  fp.DistanceToAxis = (fp.DistanceToAxis, 0.5, 1e6, 0.5)
586  if not hasattr(fp, "Subscription"):
587  fp.addProperty("App::PropertyString", "Subscription", "Labels",
588  "Subscription added to an axis name."
589  ).Subscription = ""
590  if not hasattr(fp, "Superscription"):
591  fp.addProperty("App::PropertyString", "Superscription", "Labels",
592  "Superscription added to an axis name."
593  ).Superscription = ""
594  if not hasattr(fp, "FontFamily"):
595  fp.addProperty("App::PropertyEnumeration", "FontFamily",
596  "Labels", "Label font family."
597  ).FontFamily = ["SERIF", "SANS", "TYPEWRITER"]
598  if not hasattr(fp, "FontStyle"):
599  fp.addProperty("App::PropertyEnumeration", "FontStyle",
600  "Labels", "Label font style."
601  ).FontStyle = ["NONE", "BOLD", "ITALIC",
602  "BOLD ITALIC"]
603 
604  # Placement properties
605  if not hasattr(fp, "Placement"):
606  fp.addProperty("App::PropertyPlacement", "Placement", "Base",
607  "Current placement for animated objects in "
608  + "world frame.")
609 
610  # Make some properties read-only
611  fp.setEditorMode("ObjectPlacement", 1)
612  fp.setEditorMode("ParentFramePlacement", 1)
613 
614  # Hide some properties
615  fp.setEditorMode("Placement", 2)
616  fp.setEditorMode("ValidTrajectory", 2)
617 
618  import AnimateDocumentObserver
620 
621 
630 
631  def change_trajectory(self, fp, traj):
632  # Check that trajectory has a correct format and load it
633  if self.is_ValidTrajectory(trajectory=traj):
634  fp.RotationAngle = traj["RotationAngle"]
635  fp.RotationAxisX = traj["RotationAxisX"]
636  fp.RotationAxisY = traj["RotationAxisY"]
637  fp.RotationAxisZ = traj["RotationAxisZ"]
638  fp.RotationPointX = traj["RotationPointX"]
639  fp.RotationPointY = traj["RotationPointY"]
640  fp.RotationPointZ = traj["RotationPointZ"]
641  fp.TranslationX = traj["TranslationX"]
642  fp.TranslationY = traj["TranslationY"]
643  fp.TranslationZ = traj["TranslationZ"]
644  fp.Timestamps = traj["Timestamps"]
645  else:
646  FreeCAD.Console.PrintError("Invalid trajectory!")
647 
648 
660 
661  def is_trajectory_property(self, prop):
662  return prop in ["Timestamps", "TranslationX", "TranslationY",
663  "TranslationZ", "RotationPointX", "RotationPointY",
664  "RotationPointZ", "RotationAxisX", "RotationAxisY",
665  "RotationAxisZ", "RotationAngle"]
666 
667 
692 
693  def is_ValidTrajectory(self, timestamps=[], translation_x=[],
694  translation_y=[], translation_z=[],
695  rotation_point_x=[], rotation_point_y=[],
696  rotation_point_z=[], rotation_axis_x=[],
697  rotation_axis_y=[], rotation_axis_z=[],
698  rotation_angle=[], trajectory=None):
699  # Check all keys are included and record lengths of their lists
700  if trajectory is not None and isinstance(trajectory, dict):
701  for key in ["Timestamps", "TranslationX", "TranslationY",
702  "TranslationZ", "RotationPointX", "RotationPointY",
703  "RotationPointZ", "RotationAxisX", "RotationAxisY",
704  "RotationAxisZ", "RotationAngle"]:
705  if key not in trajectory.keys():
706  FreeCAD.Console.PrintWarning("Trajectory misses key " +
707  key + ".\n")
708  return False
709  timestamps = trajectory["Timestamps"]
710  translation_x = trajectory["TranslationX"]
711  translation_y = trajectory["TranslationY"]
712  translation_z = trajectory["TranslationZ"]
713  rotation_point_x = trajectory["RotationPointX"]
714  rotation_point_y = trajectory["RotationPointY"]
715  rotation_point_z = trajectory["RotationPointZ"]
716  rotation_axis_x = trajectory["RotationAxisX"]
717  rotation_axis_y = trajectory["RotationAxisY"]
718  rotation_axis_z = trajectory["RotationAxisZ"]
719  rotation_angle = trajectory["RotationAngle"]
720 
721  # Check that all lists have the same length
722  if len(timestamps) == 0 or \
723  (len(timestamps) != 0 and
724  (len(timestamps) != len(timestamps) or
725  len(timestamps) != len(translation_x) or
726  len(timestamps) != len(translation_y) or
727  len(timestamps) != len(translation_z) or
728  len(timestamps) != len(rotation_point_x) or
729  len(timestamps) != len(rotation_point_y) or
730  len(timestamps) != len(rotation_point_z) or
731  len(timestamps) != len(rotation_axis_x) or
732  len(timestamps) != len(rotation_axis_y) or
733  len(timestamps) != len(rotation_axis_z) or
734  len(timestamps) != len(rotation_angle))):
735  FreeCAD.Console.PrintWarning("Trajectory has lists with "
736  + "inconsistent or zero "
737  + "lengths.\n")
738  return False
739 
740  # Check timestamps correspond to list of increasing values
741  if any([timestamps[i] >= timestamps[i+1]
742  for i in range(len(timestamps)-1)]):
743  FreeCAD.Console.PrintWarning("Trajectory 'Timestamps' is not "
744  + "list of increasing values.\n")
745  return False
746 
747  if any([sum([rotation_axis_x[i]**2,
748  rotation_axis_y[i]**2,
749  rotation_axis_z[i]**2]) != 1
750  for i in range(len(rotation_axis_x))]):
751  FreeCAD.Console.PrintWarning("Trajectory 'Rotation Axis' "
752  + "elements don't have norm 1.\n")
753  return False
754 
755  return True
756 
757 
776 
778  # Retrieve indices corresponding to current time
779  # If the time is before the first Timestamp use the first Timestamp
780  if fp.Time <= fp.Timestamps[0]:
781  indices = [0, 0]
782  weights = [1, 0]
783 
784  # If the time is after the last Timpestamp use the last Timestamp
785  elif fp.Time >= fp.Timestamps[-1]:
786  indices = [-1, -1]
787  weights = [1, 0]
788 
789  # If time is in the range of Timesteps
790  else:
791  # Find the index of the closest higher value
792  indices = [bisect(fp.Timestamps, fp.Time)]
793  # Add the previous index
794  indices.insert(0, indices[0]-1)
795  weights = [fp.Timestamps[indices[1]] - fp.Time,
796  fp.Time - fp.Timestamps[indices[0]]]
797  if not fp.Interpolate:
798  if weights[0] > weights[1]:
799  weights = [1, 0]
800  else:
801  weights = [0, 1]
802  else:
803  weights = [weights[0]/sum(weights), weights[1]/sum(weights)]
804 
805  return indices, weights
806 
807 
808 
823 
825 
826 
828 
829 
831 
832 
834 
835 
837 
838 
840 
841 
843 
844 
846 
847 
849 
850 
852 
853 
855 
856 
858 
859 
861 
862 
864 
865 
867 
868 
870 
871 
873 
874 
876 
877 
879 
880 
882 
883 
885 
886 
888 
889 
891 
892 
894 
895 
897 
898 
900 
901 
903 
904  panel = None
905  fp = None
906 
907  # standard methods---------------------------------------------------------
908 
917 
918  def __init__(self, vp):
919  self.setProperties(vp)
920  vp.Proxy = self
921 
922 
931 
932  def attach(self, vp):
933  # prepare transformation to keep pose corresponding to placement
934  self.tf_object2world = coin.SoTransform()
935 
936  labels = self.makeLabels()
937  self.font = coin.SoFontStyle()
938 
939  axis = self.makeRotationAxis(labels[3])
940  axis.insertChild(self.tf_object2world, 0)
941  axis.insertChild(self.font, 1)
942  self.rot_axis = coin.SoSwitch()
943  self.rot_axis.addChild(axis)
944 
945  frame = self.makeFrame(labels[:3])
946  frame.insertChild(self.tf_object2world, 0)
947  frame.insertChild(self.font, 1)
948  self.frame = coin.SoSwitch()
949  self.frame.addChild(frame)
950 
951  self.visualisations = coin.SoSwitch()
952  self.visualisations.addChild(self.rot_axis)
953  self.visualisations.addChild(self.frame)
954  self.visualisations.whichChild.setValue(coin.SO_SWITCH_ALL)
955  vp.RootNode.addChild(self.visualisations)
956 
957  vp.Object.Proxy.setProperties(vp.Object)
958  self.setProperties(vp)
959  self.fp = vp.Object
960 
961 
971 
972  def updateData(self, fp, prop):
973  # Placement changes
974  if prop == "Placement" and hasattr(fp, "Placement"):
975  trans = fp.Placement.Base
976  rot = fp.Placement.Rotation
977  self.tf_object2world.translation.setValue((trans.x, trans.y,
978  trans.z))
979  self.tf_object2world.rotation.setValue(rot.Q)
980  if len(fp.Proxy.pose["rot_point"]) == 3 and \
981  len(fp.Proxy.pose["rot_axis"]) == 3:
982  self.tf_y2axis.rotation.setValue(
983  coin.SbRotation(coin.SbVec3f(0, 1, 0),
984  coin.SbVec3f(fp.Proxy.pose["rot_axis"][0],
985  fp.Proxy.pose["rot_axis"][1],
986  fp.Proxy.pose["rot_axis"][2])
987  ))
988  self.tf_y2axis.translation.setValue(
989  (fp.Proxy.pose["rot_point"][0],
990  fp.Proxy.pose["rot_point"][1],
991  fp.Proxy.pose["rot_point"][2]))
992 
993  # Frame changes
994  elif prop == "ShowFrame" and hasattr(fp, "ShowFrame"):
995  if fp.ShowFrame:
996  self.frame.whichChild.setValue(coin.SO_SWITCH_ALL)
997  else:
998  self.frame.whichChild.setValue(coin.SO_SWITCH_NONE)
999 
1000  elif prop == "FrameTransparency" and hasattr(fp, "FrameTransparency"):
1001  self.frame_color_x.orderedRGBA.\
1002  setValue(0xff0000ff - (0xff*fp.FrameTransparency)//100)
1003  self.frame_color_y.orderedRGBA.\
1004  setValue(0x00ff00ff - (0xff*fp.FrameTransparency)//100)
1005  self.frame_color_z.orderedRGBA.\
1006  setValue(0x0000ffff - (0xff*fp.FrameTransparency)//100)
1007 
1008  elif prop == "ShaftLength" and hasattr(fp, "ShaftLength"):
1009  self.frame_shaft.vertexProperty.getValue().vertex.\
1010  set1Value(1, 0, fp.ShaftLength, 0)
1011  if hasattr(fp, "FrameArrowheadLength"):
1012  self.frame_arrowhead_translation.translation.setValue(
1013  0, fp.ShaftLength + fp.FrameArrowheadLength/2, 0)
1014  if not fp.ShowFrameArrowheads and hasattr(fp, "DistanceToAxis"):
1015  self.label_translations[0].translation.setValue(
1016  0, fp.ShaftLength + fp.DistanceToAxis, 0)
1017 
1018  elif prop == "FrameArrowheadLength" and \
1019  hasattr(fp, "FrameArrowheadLength"):
1020  self.frame_arrowhead_cone.height.setValue(fp.FrameArrowheadLength)
1021  if hasattr(fp, "ShaftLength"):
1022  self.frame_arrowhead_translation.translation.setValue(
1023  0, fp.ShaftLength + fp.FrameArrowheadLength/2, 0)
1024  if fp.ShowFrameArrowheads and hasattr(fp, "DistanceToAxis"):
1025  self.label_translations[0].translation.setValue(
1026  0, fp.FrameArrowheadLength/2 + fp.DistanceToAxis, 0)
1027 
1028  elif prop == "ShaftWidth" and hasattr(fp, "ShaftWidth"):
1029  self.frame_drawstyle.lineWidth.setValue(fp.ShaftWidth)
1030 
1031  elif prop == "FrameArrowheadRadius" and \
1032  hasattr(fp, "FrameArrowheadRadius"):
1033  self.frame_arrowhead_cone.bottomRadius.setValue(
1034  fp.FrameArrowheadRadius)
1035 
1036  elif prop == "ShowFrameArrowheads" and \
1037  hasattr(fp, "ShowFrameArrowheads"):
1038  if fp.ShowFrameArrowheads:
1039  self.frame_arrowhead.whichChild.setValue(coin.SO_SWITCH_ALL)
1040  if hasattr(fp, "FrameArrowheadLength") and \
1041  hasattr(fp, "DistanceToAxis"):
1042  self.label_translations[0].translation.setValue(
1043  0, fp.FrameArrowheadLength/2 + fp.DistanceToAxis, 0)
1044  else:
1045  self.frame_arrowhead.whichChild.setValue(coin.SO_SWITCH_NONE)
1046  if hasattr(fp, "ShaftLength") and \
1047  hasattr(fp, "DistanceToAxis"):
1048  self.label_translations[0].translation.setValue(
1049  0, fp.ShaftLength + fp.DistanceToAxis, 0)
1050 
1051  elif prop == "ShowFrameLabels" and hasattr(fp, "ShowFrameLabels"):
1052  for label in self.labels[:3]:
1053  if fp.ShowFrameLabels:
1054  label.whichChild.setValue(coin.SO_SWITCH_ALL)
1055  else:
1056  label.whichChild.setValue(coin.SO_SWITCH_NONE)
1057 
1058  # Axis changes
1059  elif prop == "ShowRotationAxis" and hasattr(fp, "ShowRotationAxis"):
1060  if fp.ShowRotationAxis:
1061  self.rot_axis.whichChild.setValue(coin.SO_SWITCH_ALL)
1062  else:
1063  self.rot_axis.whichChild.setValue(coin.SO_SWITCH_NONE)
1064 
1065  elif prop == "AxisTransparency" and \
1066  (hasattr(fp, "AxisColor") and hasattr(fp, "AxisTransparency")):
1067  self.rot_axis_color.orderedRGBA.setValue(
1068  (round(0xff*fp.AxisColor[0]) << 24)
1069  + (round(0xff*fp.AxisColor[1]) << 16)
1070  + (round(0xff*fp.AxisColor[2]) << 8)
1071  + 0xff*(100 - fp.AxisTransparency)//100)
1072 
1073  elif prop == "AxisColor" and \
1074  (hasattr(fp, "AxisColor") and hasattr(fp, "AxisTransparency")):
1075  self.rot_axis_color.orderedRGBA.setValue(
1076  (round(0xff*fp.AxisColor[0]) << 24)
1077  + (round(0xff*fp.AxisColor[1]) << 16)
1078  + (round(0xff*fp.AxisColor[2]) << 8)
1079  + 0xff*(100 - fp.AxisTransparency)//100)
1080  self.axis_label_color.orderedRGBA.setValue(
1081  (self.rot_axis_color.orderedRGBA.getValues()[0] & 0xFFFFFF00)
1082  + 0xFF)
1083 
1084  elif prop == "AxisWidth" and hasattr(fp, "AxisWidth"):
1085  self.rot_axis_drawstyle.lineWidth.setValue(fp.AxisWidth)
1086 
1087  elif prop == "AxisLength" and hasattr(fp, "AxisLength"):
1088  self.rot_axis_shaft.vertexProperty.getValue().vertex.\
1089  set1Value(1, 0, fp.AxisLength, 0)
1090  if hasattr(fp, "AxisArrowheadLength"):
1091  self.rot_axis_arrowhead_translation.translation.setValue(
1092  0, fp.AxisLength + fp.AxisArrowheadLength/2, 0)
1093  if not fp.ShowAxisArrowhead and hasattr(fp, "DistanceToAxis"):
1094  self.label_translations[1].translation.setValue(
1095  0, fp.AxisLength + fp.DistanceToAxis, 0)
1096 
1097  elif prop == "AxisArrowheadLength" and \
1098  hasattr(fp, "AxisArrowheadLength"):
1099  self.rot_axis_arrowhead_cone.height.setValue(
1100  fp.AxisArrowheadLength)
1101  if hasattr(fp, "AxisLength"):
1102  self.rot_axis_arrowhead_translation.translation.setValue(
1103  0, fp.AxisLength + fp.AxisArrowheadLength/2, 0)
1104  if fp.ShowAxisArrowhead and hasattr(fp, "DistanceToAxis"):
1105  self.label_translations[1].translation.setValue(
1106  0, fp.AxisArrowheadLength/2 + fp.DistanceToAxis, 0)
1107 
1108  elif prop == "AxisArrowheadRadius" and \
1109  hasattr(fp, "AxisArrowheadRadius"):
1110  self.rot_axis_arrowhead_cone.bottomRadius.setValue(
1111  fp.AxisArrowheadRadius)
1112 
1113  elif prop == "ShowAxisArrowhead" and hasattr(fp, "ShowAxisArrowhead"):
1114  if fp.ShowAxisArrowhead:
1115  self.rot_axis_arrowhead.whichChild.setValue(
1116  coin.SO_SWITCH_ALL)
1117  if hasattr(fp, "AxisArrowheadLength") and \
1118  hasattr(fp, "DistanceToAxis"):
1119  self.label_translations[1].translation.setValue(
1120  0, fp.AxisArrowheadLength/2 + fp.DistanceToAxis, 0)
1121  else:
1122  self.rot_axis_arrowhead.whichChild.setValue(
1123  coin.SO_SWITCH_NONE)
1124  if hasattr(fp, "AxisLength") and hasattr(fp, "DistanceToAxis"):
1125  self.label_translations[1].translation.setValue(
1126  0, fp.AxisLength + fp.DistanceToAxis, 0)
1127 
1128  elif prop == "ShowAxisLabel" and hasattr(fp, "ShowAxisLabel"):
1129  if fp.ShowAxisLabel:
1130  self.labels[-1].whichChild.setValue(coin.SO_SWITCH_ALL)
1131  else:
1132  self.labels[-1].whichChild.setValue(coin.SO_SWITCH_NONE)
1133 
1134  # Changes to the labels
1135  elif prop == "Subscription" and hasattr(fp, "Subscription"):
1136  for l in self.label_texts:
1137  l.string.setValues(2, 1, [fp.Subscription])
1138 
1139  elif prop == "Superscription" and hasattr(fp, "Superscription"):
1140  for l in self.label_texts:
1141  l.string.setValues(0, 1, [fp.Superscription])
1142 
1143  elif prop == "FontFamily" and hasattr(fp, "FontFamily"):
1144  if fp.FontFamily == "SERIF":
1145  self.font.family.setValue(self.font.SERIF)
1146  if fp.FontFamily == "SANS":
1147  self.font.family.setValue(self.font.SANS)
1148  if fp.FontFamily == "TYPEWRITER":
1149  self.font.family.setValue(self.font.TYPEWRITER)
1150 
1151  elif prop == "FontStyle" and hasattr(fp, "FontStyle"):
1152  if fp.FontStyle == "NONE":
1153  self.font.style.setValue(self.font.NONE)
1154  if fp.FontStyle == "BOLD":
1155  self.font.style.setValue(self.font.BOLD)
1156  if fp.FontStyle == "ITALIC":
1157  self.font.style.setValue(self.font.ITALIC)
1158  if fp.FontStyle == "BOLD ITALIC":
1159  self.font.style.setValue(self.font.BOLD | self.font.ITALIC)
1160 
1161  elif prop == "FontSize" and hasattr(fp, "FontSize"):
1162  self.font.size.setValue(fp.FontSize)
1163 
1164  elif prop == "DistanceToAxis" and hasattr(fp, "DistanceToAxis") and \
1165  hasattr(fp, "ShowFrameArrowheads") and \
1166  hasattr(fp, "ShowAxisArrowhead"):
1167  if fp.ShowFrameArrowheads and hasattr(fp, "FrameArrowheadLength"):
1168  self.label_translations[0].translation.setValue(
1169  0, fp.FrameArrowheadLength/2 + fp.DistanceToAxis, 0)
1170  elif hasattr(fp, "ShaftLength"):
1171  self.label_translations[0].translation.setValue(
1172  0, fp.ShaftLength + fp.DistanceToAxis, 0)
1173  if fp.ShowAxisArrowhead and hasattr(fp, "AxisArrowheadLength"):
1174  self.label_translations[1].translation.setValue(
1175  0, fp.AxisArrowheadLength/2 + fp.DistanceToAxis, 0)
1176  elif hasattr(fp, "AxisLength"):
1177  self.label_translations[1].translation.setValue(
1178  0, fp.AxisLength + fp.DistanceToAxis, 0)
1179 
1180 
1189 
1190  def onChanged(self, vp, prop):
1191  if prop == "Visibility":
1192  if vp.Visibility:
1193  self.visualisations.whichChild.setValue(coin.SO_SWITCH_ALL)
1194  else:
1195  self.visualisations.whichChild.setValue(coin.SO_SWITCH_NONE)
1196 
1197 
1205 
1206  def claimChildren(self):
1207  if hasattr(self, "fp") and self.fp:
1208  return self.fp.Group
1209  return []
1210 
1211 
1219 
1220  def canDropObject(self, obj):
1221  if hasattr(obj, "Proxy") and \
1222  isinstance(obj.Proxy, self.fp.Proxy.__class__):
1223  return True
1224  return False
1225 
1226 
1233 
1234  def getIcon(self):
1235  return path.join(PATH_TO_ICONS, "Trajectory.png")
1236 
1237 
1249 
1250  def __getstate__(self):
1251  return None
1252 
1253 
1259 
1260  def __setstate__(self, state):
1261  pass
1262 
1263 
1270 
1271  def setProperties(self, vp):
1272  # hide unnecessary view properties
1273  vp.setEditorMode("DisplayMode", 2)
1274 
1275 
1286 
1287  def doubleClicked(self, vp):
1288  # Switch to the Task View if a Trajectory panel is already opened
1289  if self.panel:
1290  FreeCADGui.Control.showTaskView()
1291 
1292  # Try to open new Trajectory panel
1293  else:
1294  # Check there is a valid trajectory
1295  if not vp.Object.ValidTrajectory:
1296  QMessageBox.warning(
1297  None,
1298  'Error while opening trajectory panel',
1299  "Valid trajectory is necessary to open "
1300  + "a trajectory panel.")
1301  return True
1302 
1303  # Load the QDialog from a file and name it after this object
1304  new_form = [FreeCADGui.PySideUic.loadUi(path.join(PATH_TO_UI,
1305  "AnimationTrajectory.ui"))]
1306  new_form[0].setWindowTitle(vp.Object.Label)
1307 
1308  # Create a control panel and try to show it
1309  self.panel = TrajectoryPanel([vp.Object], new_form)
1310  try:
1311  FreeCADGui.Control.showDialog(self.panel)
1312  except RuntimeError as e:
1313  # Reset the panel
1314  self.panel = None
1315 
1316  # Find all Trajectory feature python objects with
1317  # a reference to a Trajectory panel
1318  trajectories = []
1319  for obj in FreeCAD.ActiveDocument.Objects:
1320  if hasattr(obj, "Proxy") and \
1321  obj.Proxy.__class__.__name__ == "TrajectoryProxy":
1322  if obj.ViewObject.Proxy.panel is not None:
1323  trajectories.append(obj)
1324 
1325  if len(trajectories) > 0:
1326  # Close opened Trajecotry panel
1327  trajectories[0].ViewObject.Proxy.panel.reject()
1328 
1329  # Load the QDialog form for each Trajectory which
1330  # had a reference to the panel
1331  forms = []
1332  for trajectory in trajectories:
1333  form = FreeCADGui.PySideUic.loadUi(
1334  path.join(PATH_TO_UI,
1335  "AnimationTrajectory.ui"))
1336  form.setWindowTitle(trajectory.Label)
1337  forms.append(form)
1338 
1339  # Load one more QDialog for this Trajectory
1340  forms.append(new_form[0])
1341 
1342  # Add this Trajectory to the list of trajectories
1343  trajectories.append(vp.Object)
1344 
1345  # Add a reference to the new panel to view providers
1346  # of all trajectories
1347  self.panel = TrajectoryPanel(trajectories, forms)
1348  for trajectory in trajectories:
1349  trajectory.ViewObject.Proxy.panel = self.panel
1350  FreeCADGui.Control.showDialog(self.panel)
1351  return True
1352 
1353  # Diffeerent Task panel is opened, inform the user
1354  else:
1355  QMessageBox.warning(
1356  None,
1357  'Error while opening trajectory panel',
1358  "A different panel is already active.\n"
1359  + "Close it before opening this one.")
1360  FreeCADGui.Control.showTaskView()
1361  return True
1362 
1363 
1373 
1374  def setupContextMenu(self, vp, menu):
1375  menu.clear()
1376  action = menu.addAction("Select Time")
1377  action.triggered.connect(lambda f=self.doubleClicked,
1378  arg=vp: f(arg))
1379 
1380 
1388 
1389  def makeLabels(self):
1390  label_strings = ["X", "Y", "Z", "O"]
1391  colors = [0xFF0000FF, 0x00FF00FF, 0x0000FFFF]
1392  self.label_texts = []
1394  # frame translation
1395  self.label_translations.append(coin.SoTranslation())
1396  # axis translation
1397  self.label_translations.append(coin.SoTranslation())
1398  self.labels = []
1399  for i in range(4):
1400  label_group = coin.SoSeparator()
1401  if i < 3:
1402  label_group.addChild(self.label_translations[0])
1403  frame_axis_color = coin.SoPackedColor()
1404  frame_axis_color.orderedRGBA.setValue(colors[i])
1405  label_group.addChild(frame_axis_color)
1406  else:
1407  label_group.addChild(self.label_translations[1])
1408  self.axis_label_color = coin.SoPackedColor()
1409  label_group.addChild(self.axis_label_color)
1410  self.label_texts.append(coin.SoText2())
1411  self.label_texts[i].string.setValues(
1412  0, 3, ["", label_strings[i], ""])
1413  self.label_texts[i].justification.setValue(
1414  self.label_texts[i].CENTER)
1415  self.label_texts[i].spacing.setValue(0.45)
1416  label_group.addChild(self.label_texts[i])
1417  self.labels.append(coin.SoSwitch())
1418  self.labels[i].addChild(label_group)
1419  return self.labels
1420 
1421 
1431 
1432  def makeFrame(self, frame_labels):
1433  # make a generic shaft from 0 in Y direction
1434  shaft_vertices = coin.SoVertexProperty()
1435  shaft_vertices.vertex.setNum(2)
1436  shaft_vertices.vertex.set1Value(0, 0, 0, 0)
1437  self.frame_shaft = coin.SoLineSet()
1438  self.frame_shaft.vertexProperty.setValue(shaft_vertices)
1439  self.frame_shaft.numVertices.setNum(1)
1440  self.frame_shaft.numVertices.setValue(2)
1441 
1442  # make a generic conic arrowhead oriented in Y axis direction and
1443  # move it at the end of the shaft
1444  self.frame_arrowhead_translation = coin.SoTranslation()
1445  self.frame_arrowhead_cone = coin.SoCone()
1446  self.frame_arrowhead = coin.SoSwitch()
1447  self.frame_arrowhead.addChild(self.frame_arrowhead_translation)
1448  self.frame_arrowhead.addChild(self.frame_arrowhead_cone)
1449 
1450  # make rotations to rotate prepared shaft and arrowhead for Y axis
1451  # direction also to X and Z
1452  rot_y2x = coin.SoRotation()
1453  rot_y2x.rotation.setValue(coin.SbRotation(coin.SbVec3f(0, 1, 0),
1454  coin.SbVec3f(1, 0, 0)))
1455  rot_y2z = coin.SoRotation()
1456  rot_y2z.rotation.setValue(coin.SbRotation(coin.SbVec3f(0, 1, 0),
1457  coin.SbVec3f(0, 0, 1)))
1458 
1459  # prepare colors for X,Y,Z which will correspond to R,G,B as customary
1460  self.frame_color_x = coin.SoPackedColor()
1461  self.frame_color_y = coin.SoPackedColor()
1462  self.frame_color_z = coin.SoPackedColor()
1463 
1464  # make complete colored and rotated arrows
1465  x_arrow = coin.SoSeparator()
1466  x_arrow.addChild(rot_y2x)
1467  x_arrow.addChild(self.frame_color_x)
1468  x_arrow.addChild(self.frame_shaft)
1469  x_arrow.addChild(self.frame_arrowhead)
1470  x_arrow.addChild(frame_labels[0])
1471  y_arrow = coin.SoSeparator()
1472  y_arrow.addChild(self.frame_color_y)
1473  y_arrow.addChild(self.frame_shaft)
1474  y_arrow.addChild(self.frame_arrowhead)
1475  y_arrow.addChild(frame_labels[1])
1476  z_arrow = coin.SoSeparator()
1477  z_arrow.addChild(rot_y2z)
1478  z_arrow.addChild(self.frame_color_z)
1479  z_arrow.addChild(self.frame_shaft)
1480  z_arrow.addChild(self.frame_arrowhead)
1481  z_arrow.addChild(frame_labels[2])
1482 
1483  # prepare draw style to control shaft width
1484  self.frame_drawstyle = coin.SoDrawStyle()
1485 
1486  # make complete frame and it to shaded display mode
1487  separated_frame = coin.SoSeparator()
1488  separated_frame.addChild(self.frame_drawstyle)
1489  separated_frame.addChild(x_arrow)
1490  separated_frame.addChild(y_arrow)
1491  separated_frame.addChild(z_arrow)
1492 
1493  return separated_frame
1494 
1495 
1504 
1505  def makeRotationAxis(self, axis_label):
1506  # make a generic shaft from 0 in Y direction
1507  shaft_vertices = coin.SoVertexProperty()
1508  shaft_vertices.vertex.setNum(2)
1509  shaft_vertices.vertex.set1Value(0, 0, 0, 0)
1510  self.rot_axis_shaft = coin.SoLineSet()
1511  self.rot_axis_shaft.vertexProperty.setValue(shaft_vertices)
1512  self.rot_axis_shaft.numVertices.setNum(1)
1513  self.rot_axis_shaft.numVertices.setValue(2)
1514 
1515  # make a generic conic arrowhead oriented in Y axis direction and
1516  # move it at the end of the shaft
1517  self.rot_axis_arrowhead_translation = coin.SoTranslation()
1518  self.rot_axis_arrowhead_cone = coin.SoCone()
1519  self.rot_axis_arrowhead = coin.SoSwitch()
1521  self.rot_axis_arrowhead.addChild(self.rot_axis_arrowhead_cone)
1522 
1523  # make rotations to rotate prepared shaft and arrowhead for Y axis
1524  # direction also to X and Z
1525  self.tf_y2axis = coin.SoTransform()
1526 
1527  # prepare colors for X,Y,Z which will correspond to R,G,B as customary
1528  self.rot_axis_color = coin.SoPackedColor()
1529 
1530  # prepare draw style to control shaft width
1531  self.rot_axis_drawstyle = coin.SoDrawStyle()
1532 
1533  # make complete frame and it to shaded display mode
1534  separated_axis = coin.SoSeparator()
1535  separated_axis.addChild(self.tf_y2axis)
1536  separated_axis.addChild(self.rot_axis_drawstyle)
1537  separated_axis.addChild(self.rot_axis_color)
1538  separated_axis.addChild(self.rot_axis_shaft)
1539  separated_axis.addChild(self.rot_axis_arrowhead)
1540  separated_axis.addChild(axis_label)
1541 
1542  return separated_axis
1543 
1544 
1545 
1551 
1552 class TrajectoryCommand(object):
1553 
1554 
1561 
1562  def GetResources(self):
1563  return {'Pixmap': path.join(PATH_TO_ICONS, "TrajectoryCmd.png"),
1564  'MenuText': "Trajectory",
1565  'ToolTip': "Create Trajectory instance."}
1566 
1567 
1574 
1575  def Activated(self):
1576  doc = FreeCAD.ActiveDocument
1577  a = doc.addObject("App::DocumentObjectGroupPython", "Trajectory")
1578  TrajectoryProxy(a)
1579  if FreeCAD.GuiUp:
1580  ViewProviderTrajectoryProxy(a.ViewObject)
1581  doc.recompute()
1582  return
1583 
1584 
1593 
1594  def IsActive(self):
1595  if FreeCAD.ActiveDocument is None:
1596  return False
1597  else:
1598  return True
1599 
1600 
1601 if FreeCAD.GuiUp:
1602  # Add command to FreeCAD Gui when importing this module in InitGui
1603  FreeCADGui.addCommand('TrajectoryCommand', TrajectoryCommand())
panel
A TrajectoryPanel if one is active or None.
Definition: Trajectory.py:904
def isAllowedAlterView(self)
Method to tell FreeCAD if dialog is allowed to alter a view.
Definition: Trajectory.py:215
def __setstate__(self, state)
Necessary method to avoid errors when trying to restore unserializable objects.
Definition: Trajectory.py:1260
axis_label_color
A SoPackedColor coloring a rotational axis(RA) label.
Definition: Trajectory.py:1408
previous_times
A list of trajectory times before opening a panel.
Definition: Trajectory.py:93
def Activated(self)
Method used as a callback when the toolbar button or the menu item is clicked.
Definition: Trajectory.py:1575
def isAllowedAlterDocument(self)
Method to tell FreeCAD if dialog is allowed to alter a document.
Definition: Trajectory.py:224
Class specifying Animate workbench's Trajectory button/command.
Definition: Trajectory.py:1552
frame_shaft
A SoLineSet shaft for frame axes.
Definition: Trajectory.py:1437
tf_y2axis
A SoTransform transformation from Y axis to a rotation axis.
Definition: Trajectory.py:1525
def onChanged(self, fp, prop)
Method called after DocumentObjectGroupPython Trajectory was changed.
Definition: Trajectory.py:276
def addObserver()
Adds an AnimateDocumentObserver between FreeCAD's document observers safely.
rot_axis_arrowhead_cone
A SoCone arrowhead cone for a rotation axis.
Definition: Trajectory.py:1518
def setProperties(self, vp)
Method to hide unused properties.
Definition: Trajectory.py:1271
def __getstate__(self)
Necessary method to avoid errors when trying to save unserializable objects.
Definition: Trajectory.py:1250
def isAllowedAlterSelection(self)
Method to tell FreeCAD if dialog is allowed to alter a selection.
Definition: Trajectory.py:206
rot_axis_color
A SoPackedColor coloring a rotational axis.
Definition: Trajectory.py:1528
rot_axis
A SoSwitch with a rotation axis in form of an arrow.
Definition: Trajectory.py:942
frame_color_z
A SoPackedColor blue color for an Z axis.
Definition: Trajectory.py:1462
def __init__(self, vp)
Initialization method for ViewProviderTrajectoryProxy.
Definition: Trajectory.py:918
def doubleClicked(self, vp)
Method called by FreeCAD when Trajectory is double-clicked in the Tree View.
Definition: Trajectory.py:1287
def getIcon(self)
Method called by FreeCAD to supply an icon for the Tree View.
Definition: Trajectory.py:1234
tf_object2world
A SoTransform transformation from object to world frame.
Definition: Trajectory.py:934
frame_arrowhead_translation
A SoTranslation moving frame arrowheads.
Definition: Trajectory.py:1444
Proxy class for a DocumentObjectGroupPython Trajectory instance.
Definition: Trajectory.py:244
frame_color_y
A SoPackedColor green color for an Y axis.
Definition: Trajectory.py:1461
frame_arrowhead_cone
A SoCone arrowhead cone for frame axes.
Definition: Trajectory.py:1445
def onChanged(self, vp, prop)
Method called after Trajectory.ViewObject was changed.
Definition: Trajectory.py:1190
def updateData(self, fp, prop)
Method called after DocumentObjectGroupPython Trajectory was changed.
Definition: Trajectory.py:972
label_translations
A list of SoTranslations moving labels.
Definition: Trajectory.py:1393
def IsActive(self)
Method to specify when the toolbar button and the menu item are enabled.
Definition: Trajectory.py:1594
label_texts
A list of SoText2s labels denoting all axes and an origin.
Definition: Trajectory.py:1392
frame_color_x
A SoPackedColor red color for an X axis.
Definition: Trajectory.py:1460
def makeFrame(self, frame_labels)
Method which makes a Coin3D frame to show a current pose in a trajectory.
Definition: Trajectory.py:1432
def GetResources(self)
Method used by FreeCAD to retrieve resources to use for this command.
Definition: Trajectory.py:1562
rot_axis_arrowhead
A SoSwitch translated cone for a rotation axis.
Definition: Trajectory.py:1519
font
A SoFontStyle font for axes labels.
Definition: Trajectory.py:937
def __init__(self, fp)
Initialization method for TrajectoryProxy.
Definition: Trajectory.py:259
def change_trajectory(self, fp, traj)
Method used to change a Trajectory's trajectory.
Definition: Trajectory.py:631
def sliderChanged(self, value, form, trajectory)
Feedback method called when any slider position is changed.
Definition: Trajectory.py:130
visualisations
A SoSwitch with all visualisations (frame & rotation axis).
Definition: Trajectory.py:951
rot_axis_arrowhead_translation
A SoTranslation moving a RA arrowhead.
Definition: Trajectory.py:1517
def setProperties(self, fp)
Method to set properties during initialization or document restoration.
Definition: Trajectory.py:385
Class providing funcionality to a Trajectory panel inside the TaskView.
Definition: Trajectory.py:63
def is_ValidTrajectory(self, timestamps=[], translation_x=[], translation_y=[], translation_z=[], rotation_point_x=[], rotation_point_y=[], rotation_point_z=[], rotation_axis_x=[], rotation_axis_y=[], rotation_axis_z=[], rotation_angle=[], trajectory=None)
Method to check if a trajectory is valid.
Definition: Trajectory.py:693
def getStandardButtons(self, *args)
Method to set just one button (close) to close the dialog.
Definition: Trajectory.py:197
def accept(self)
Feedback method called when 'OK' button was pressed to close the panel.
Definition: Trajectory.py:187
def reject(self)
Feedback method called when 'Cancel' button was pressed to close the panel.
Definition: Trajectory.py:171
rot_axis_drawstyle
A SoDrawStyle controlling RA shaft line width.
Definition: Trajectory.py:1531
def is_trajectory_property(self, prop)
Method to check that a property describes a trajectory.
Definition: Trajectory.py:661
labels
A list of SoSwitches containing colored translated labels.
Definition: Trajectory.py:1398
def attach(self, vp)
Method called by FreeCAD after initialization to attach Coin3D constructs.
Definition: Trajectory.py:932
Proxy class for Gui.ViewProviderDocumentObject Trajectory.ViewObject.
Definition: Trajectory.py:824
def setupContextMenu(self, vp, menu)
Method called by the FreeCAD to customize a context menu for a Trajectory.
Definition: Trajectory.py:1374
frame_arrowhead
A SoSwitch translated cone for frame axes.
Definition: Trajectory.py:1446
frame_drawstyle
A SoDrawStyle controlling frame axes shaft line width.
Definition: Trajectory.py:1484
def __init__(self, trajectories, forms)
Initialization method for TrajectoryPanel.
Definition: Trajectory.py:87
def find_timestamp_indices_and_weights(self, fp)
Method to find weighted timestamps indices corresponding to a given time.
Definition: Trajectory.py:777
def claimChildren(self)
Method called by FreeCAD to retrieve assigned children.
Definition: Trajectory.py:1206
def execute(self, fp)
Method called when recomputing a DocumentObjectGroupPython.
Definition: Trajectory.py:316
rot_axis_shaft
A SoLineSet shaft for a rotation axis.
Definition: Trajectory.py:1510
def close(self)
Method used to close TrajectoryPanel.
Definition: Trajectory.py:149
def makeRotationAxis(self, axis_label)
Method which makes a Coin3D rotation axis to show in the FreeCAD View.
Definition: Trajectory.py:1505
frame
A SoSeparator with a coordinate frame made from 3 RGB arrows.
Definition: Trajectory.py:948
def makeLabels(self)
Method which makes Coin3D labels to be displayed in the FreeCAD View.
Definition: Trajectory.py:1389
pose
A dict describing a pose - position, rotation axis, point and angle.
Definition: Trajectory.py:386
trajectories
A list of DocumentObjectGroupPython Trajectory instances.
Definition: Trajectory.py:89
def canDropObject(self, obj)
Method called by FreeCAD to ask if an object obj can be dropped into a Group.
Definition: Trajectory.py:1220
form
A list of QDialog instances to show in the TaskView.
Definition: Trajectory.py:103
def onDocumentRestored(self, fp)
Method called when document is restored to make sure everything is as it was.
Definition: Trajectory.py:371