[gimp-gap] fixed move path unwanted object jumps forth and back as reported in #607927



commit 397624f656b88b89a478aea8246a20bb5e646719
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date:   Mon Apr 19 08:44:54 2010 +0200

    fixed move path unwanted object jumps forth and back as reported in #607927

 ChangeLog                                    |   16 +
 docs/reference/txt/plug-in-gap-move-path.txt |   21 +-
 gap/gap_mov_dialog.c                         |  189 ++++++++++-
 gap/gap_mov_dialog.h                         |   16 +
 gap/gap_mov_exec.c                           |  451 ++++++++++++++++++++------
 gap/gap_mov_exec.h                           |    1 +
 gap/gap_mov_render.c                         |    7 +-
 7 files changed, 581 insertions(+), 120 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 76c947a..b319e61 100755
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2010-04-19 Wolfgang Hofer <hof gimp org>
+
+- move path fix for unwanted object jumps forth and back as reported in #607927
+  (the 1st frame in a segment was sometimes calculated based on the controlpoint
+   of the previous segment. this resulted in the reported  pixel jumps)
+
+- added information label to the move path dialog to show max speed for current segment
+  and current segment length in pixels.
+  (this shall help to adjust ontrolpoints in a way to avoid unwanted speed jumps)
+  
+  * gap/gap_mov_render.c
+  * gap/gap_mov_exec.c [.h]
+  * gap/gap_mov_dialog.c [.h]
+  * docs/reference/txt/plug-in-gap-move-path.txt
+  
+
 2010-03-21 Wolfgang Hofer <hof gimp org>
 
 - added gimprc parameters to specify width and height
diff --git a/docs/reference/txt/plug-in-gap-move-path.txt b/docs/reference/txt/plug-in-gap-move-path.txt
index 6e8ef39..7534bcb 100755
--- a/docs/reference/txt/plug-in-gap-move-path.txt
+++ b/docs/reference/txt/plug-in-gap-move-path.txt
@@ -439,13 +439,19 @@ Move Path (make things move)
 	 the keyframe is internally stored as 2 (7 - 5)
 	 
 	Keyframes are also used to define path segments.
-	Where a Path segment includes all followin controlpoints
+	Where a Path segment includes all following controlpoints
         until the next Keyframe.
         
         Independent acceleration characteristics can be set at
         those controlpoints that are the begin of a path segment.
         (e.g the 1st controlpoint and all keyframes)
         
+        The current Segment Number, current segment length (in pixels)
+        and minimal / maximal speed (in pixels per tween) are displayed
+        below the preview.
+        The current Segment is the segment that includes the current
+        displayed controlpoint.
+        
         Example:
           You have 500 frames and want to render an object
           that stands still at coordinate 320/200 in the 1st 50 frames,
@@ -453,9 +459,18 @@ Move Path (make things move)
           until frame 400, decelerates until frame 450 and stays still
           at coordinate 470/150 until the end frame 500.
           
+          Tip: 
+          To avoid unwanted speed jumps in this multi segmented
+          example, the controlpoints should be set in a way 
+          that max speed value of segment B is nearly equal
+          to the max speed value of Segment C and segment D.
+          Note that changes of control point coordinates affect the
+          length of the path segment, acceleration characteristic,
+          and number of involved frames (or tweens) also have
+          influence on the speed of the object within a segment.
           
         
-        #Segment A  (standstill)
+        #Segment  A  (standstill in Segment Number 0)
         [ 0]  x:320 y:200
 
         #Segment B  (increasing speed due to acceleration value 20 for Movement)
@@ -478,7 +493,7 @@ Move Path (make things move)
         [13]  x:475 y:155
         [14]  x:475 y:155
 
-        #Segment D  (standstill)
+        #Segment E  (standstill)
         [15]  x:470 y:150  keyframe:450
         [16]  x:470 y:150
 
diff --git a/gap/gap_mov_dialog.c b/gap/gap_mov_dialog.c
index c71b2e1..035fd44 100755
--- a/gap/gap_mov_dialog.c
+++ b/gap/gap_mov_dialog.c
@@ -276,6 +276,12 @@ typedef struct
   GdkCursor     *cursor_wait;
   GdkCursor     *cursor_acitve;
   GimpRGB               pathcolor;
+  
+  
+  GtkWidget            *segNumberLabel;
+  GtkWidget            *segLengthLabel;
+  GtkWidget            *segSpeedLabel;
+  
 } t_mov_gui_stuff;
 
 
@@ -350,6 +356,7 @@ static void        mov_grab_anchorpoints_path(t_mov_gui_stuff *mgp
                                              );
 
 
+static void     mov_upd_seg_labels       (GtkWidget *widget, t_mov_gui_stuff *mgp);
 static void     mov_padd_callback        (GtkWidget *widget,gpointer data);
 static void     mov_pgrab_callback       (GtkWidget *widget,GdkEventButton *bevent,gpointer data);
 static void     mov_pins_callback        (GtkWidget *widget,gpointer data);
@@ -472,9 +479,11 @@ long      gap_mov_dlg_move_dialog    (GapMovData *mov_ptr)
   char      *l_str;
   t_mov_gui_stuff *mgp;
 
-  if(gap_debug) printf("GAP-DEBUG: START gap_mov_dlg_move_dialog\n");
-
   mgp = g_new( t_mov_gui_stuff, 1 );
+  if(gap_debug)
+  {
+    printf("gap_mov_dlg_move_dialog START mgp:%d\n", (int)mgp);
+  }
   if(mgp == NULL)
   {
     printf("error can't alloc path_preview structure\n");
@@ -499,7 +508,10 @@ long      gap_mov_dlg_move_dialog    (GapMovData *mov_ptr)
   mgp->tween_opacity_desc_adj = NULL;
   mgp->trace_opacity_initial_adj = NULL;
   mgp->sel_feather_radius_adj = NULL;
-
+  mgp->segNumberLabel = NULL;
+  mgp->segLengthLabel = NULL;
+  mgp->segSpeedLabel = NULL;
+  
   pvals = mov_ptr->val_ptr;
 
   l_str = gap_base_strdup_del_underscore(mov_ptr->dst_ainfo_ptr->basename);
@@ -1560,6 +1572,67 @@ mov_pgrab_callback (GtkWidget *widget,
 }  /* end mov_pgrab_callback */
 
 
+/* --------------------------------
+ * mov_upd_seg_labels
+ * --------------------------------
+ * update information about max speed and path segment length
+ * 
+ */
+static void
+mov_upd_seg_labels(GtkWidget *widget, t_mov_gui_stuff *mgp)
+{
+  if(gap_debug)
+  {
+    printf("mov_upd_seg_labels START mgp:%d widget:%d\n", (int)mgp, (int)widget);
+  }
+
+  if(mgp != NULL)
+  {
+    if((mgp->segLengthLabel != NULL)
+    && (mgp->segSpeedLabel != NULL)
+    && (mgp->segNumberLabel != NULL))
+    {
+       GapMovQuery mov_query;
+       char *numString;
+       
+       mov_query.pointIndexToQuery = pvals->point_idx;
+
+      if(gap_debug)
+      {
+        printf("mov_upd_seg_labels pointIndexToQuery:%d dst_range_end:%d\n"
+              , (int)mov_query.pointIndexToQuery
+              , (int)pvals->dst_range_end
+              );
+      }
+       
+       /* query path segment length and max speed per frame values */
+       gap_mov_exec_query(pvals, mgp->ainfo_ptr, &mov_query);
+       
+       numString = g_strdup_printf("%d"
+                                  , (int)mov_query.segmentNumber
+                                  );
+       gtk_label_set_text( GTK_LABEL(mgp->segNumberLabel), numString);
+       g_free(numString);
+
+       numString = g_strdup_printf("%.1f"
+                                  , (float)mov_query.pathSegmentLengthInPixels
+                                  );
+       gtk_label_set_text( GTK_LABEL(mgp->segLengthLabel), numString);
+       g_free(numString);
+       
+       
+       numString = g_strdup_printf("%.1f / %.1f"
+                                  , (float)mov_query.minSpeedInPixelsPerFrame
+                                  , (float)mov_query.maxSpeedInPixelsPerFrame
+                                  );
+       gtk_label_set_text( GTK_LABEL(mgp->segSpeedLabel), numString);
+       g_free(numString);
+       
+    }
+  }
+}
+
+
 static void
 mov_padd_callback (GtkWidget *widget,
                       gpointer   data)
@@ -1620,7 +1693,7 @@ mov_pdel_callback (GtkWidget *widget,
   l_idx = pvals->point_idx_max;
   if(pvals->point_idx_max == 0)
   {
-    /* This is the las t point to delete */
+    /* This is the last point to delete */
     p_reset_points();
   }
   else
@@ -2089,6 +2162,8 @@ p_point_refresh(t_mov_gui_stuff *mgp)
   gtk_adjustment_set_value (mgp->accSelFeatherRadius_adj,
                             (gdouble)mgp->accSelFeatherRadius);
 
+  mov_upd_seg_labels(NULL, mgp);
+  
   mgp->in_call = FALSE;
 }       /* end p_point_refresh */
 
@@ -3576,7 +3651,6 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
                               ,gboolean vertical_layout
                               )
 {
-  GtkWidget *vcbox;
   GtkWidget *master_table;
   GtkWidget *table;
   GtkObject *adj;
@@ -3584,6 +3658,7 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
   gint  master_rows;
   gint  master_cols;
   gint  tabcol, tabrow, boxcol, boxrow;
+  gint  row;
 
   if(vertical_layout)
   {
@@ -3633,6 +3708,9 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
   g_signal_connect (G_OBJECT (adj), "value_changed",
                     G_CALLBACK (gimp_int_adjustment_update),
                     &pvals->dst_range_start);
+  g_signal_connect (adj, "value-changed",
+                            G_CALLBACK (mov_upd_seg_labels),
+                            mgp);
 
   /* the end frame scale_entry */
   adj = gimp_scale_entry_new( GTK_TABLE (table), 0, 1,          /* table col, row */
@@ -3651,6 +3729,9 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
   g_signal_connect (G_OBJECT (adj), "value_changed",
                     G_CALLBACK (gimp_int_adjustment_update),
                     &pvals->dst_range_end);
+  g_signal_connect (adj, "value-changed",
+                            G_CALLBACK (mov_upd_seg_labels),
+                            mgp);
 
   /* the Layerstack scale_entry */
   adj = gimp_scale_entry_new( GTK_TABLE (table), 0, 2,          /* table col, row */
@@ -3671,10 +3752,11 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
                     G_CALLBACK (mov_instant_int_adjustment_update),
                     &pvals->dst_layerstack);
 
-  /* the vbox for checkbuttons */
-  vcbox = gtk_vbox_new (FALSE, 3);
-  gtk_widget_show (vcbox);
+  /* the table for checkbuttons and info labels */
+  table = gtk_table_new (3, 3, FALSE);
+  gtk_widget_show (table);
 
+  row = 0;
 
   /* toggle force visibility  */
   check_button = gtk_check_button_new_with_label ( _("Force Visibility"));
@@ -3688,7 +3770,11 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
   g_signal_connect (G_OBJECT (check_button), "toggled",
                     G_CALLBACK  (mov_force_visibility_toggle_callback),
                     &pvals->src_force_visible);
-  gtk_box_pack_start (GTK_BOX (vcbox), check_button, TRUE, TRUE, 0);
+  gtk_table_attach(GTK_TABLE(table), check_button, 0, 1, row, row+1
+                  , GTK_FILL, GTK_FILL, 4, 0);
+
+
+  row = 1;
 
   /* toggle clip_to_image */
   check_button = gtk_check_button_new_with_label ( _("Clip To Frame"));
@@ -3701,11 +3787,17 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
   g_signal_connect (G_OBJECT (check_button), "toggled",
                     G_CALLBACK (mov_gint_toggle_callback),
                     &pvals->clip_to_img);
-  gtk_box_pack_start (GTK_BOX (vcbox), check_button, TRUE, TRUE, 0);
+  gtk_table_attach(GTK_TABLE(table), check_button, 0, 1, row, row+1
+                  , GTK_FILL, GTK_FILL, 4, 0);
+
+
 
-  gtk_table_attach(GTK_TABLE(master_table), vcbox, boxcol, boxcol+1, boxrow, boxrow+1
+
+
+  gtk_table_attach(GTK_TABLE(master_table), table, boxcol, boxcol+1, boxrow, boxrow+1
                   , GTK_FILL, GTK_FILL, 4, 0);
 
+
   return(master_table);
 }  /* end mov_path_framerange_box_create */
 
@@ -4510,6 +4602,67 @@ mov_path_prevw_create ( GimpDrawable *drawable, t_mov_gui_stuff *mgp, gboolean v
   }
 
 
+  /* segmnt information labels */
+  {
+    GtkWidget *label;
+    GtkWidget *seg_table;
+    gint seg_row;
+    
+    seg_row = 0;
+    
+    /* the preview sub table (1 row) */
+    seg_table = gtk_table_new ( 1, 6, FALSE );
+    gtk_widget_show (seg_table);
+
+    label = gtk_label_new(_("Segment:"));
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+    gtk_widget_show (label);
+    gtk_table_attach(GTK_TABLE(seg_table), label, 0, 1, seg_row, seg_row+1
+                  , GTK_FILL, GTK_FILL, 4, 0);
+ 
+    label = gtk_label_new("0");
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+    gtk_widget_show (label);
+    mgp->segNumberLabel = label;
+    gtk_table_attach(GTK_TABLE(seg_table), label, 1, 2, seg_row, seg_row+1
+                    , GTK_FILL, GTK_FILL, 4, 0);
+  
+
+    label = gtk_label_new(_("Length:"));
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+    gtk_widget_show (label);
+    gtk_table_attach(GTK_TABLE(seg_table), label, 2, 3, seg_row, seg_row+1
+                  , GTK_FILL, GTK_FILL, 4, 0);
+  
+ 
+    label = gtk_label_new("0.0");
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+    gtk_widget_show (label);
+    mgp->segLengthLabel = label;
+    gtk_table_attach(GTK_TABLE(seg_table), label, 3, 4, seg_row, seg_row+1
+                    , GTK_FILL, GTK_FILL, 4, 0);
+
+    label = gtk_label_new(_("Speed Min/Max:"));
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+    gtk_widget_show (label);
+    gtk_table_attach(GTK_TABLE(seg_table), label, 4, 5, seg_row, seg_row+1
+                    , GTK_FILL, GTK_FILL, 4, 0);
+
+    label = gtk_label_new("0.0");
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+    gtk_widget_show (label);
+    mgp->segSpeedLabel = label;
+    gtk_table_attach(GTK_TABLE(seg_table), label, 5, 6, seg_row, seg_row+1
+                    , GTK_FILL, GTK_FILL, 4, 0);
+
+
+    gtk_table_attach(GTK_TABLE(pv_table), seg_table, 0, 1, 1, 2,
+                     GTK_FILL|GTK_EXPAND, 0, 0, 0);
+  
+  }
+
+
+
   /* hbox_show block */
   {
     GtkWidget *hbox_show;
@@ -4518,7 +4671,7 @@ mov_path_prevw_create ( GimpDrawable *drawable, t_mov_gui_stuff *mgp, gboolean v
     hbox_show = gtk_hbox_new (FALSE, 3);
     gtk_widget_show (hbox_show);
 
-    /* pathclor selction button */
+    /* pathclor selection button */
     {
       GtkWidget      *color_button;
 
@@ -4604,8 +4757,8 @@ mov_path_prevw_create ( GimpDrawable *drawable, t_mov_gui_stuff *mgp, gboolean v
                       mgp);
 
 
-    gtk_table_attach(GTK_TABLE(pv_table), hbox_show, 0, 1, 1, 2,
-                     0, 0, 16, 0);
+    gtk_table_attach(GTK_TABLE(pv_table), hbox_show, 0, 1, 2, 3,
+                     GTK_FILL, 0, 0, 0);
   }
 
   /* the preview sub table (1 row) */
@@ -4632,7 +4785,7 @@ mov_path_prevw_create ( GimpDrawable *drawable, t_mov_gui_stuff *mgp, gboolean v
   mgp->preview_frame_nr_adj = GTK_ADJUSTMENT(adj);
 
 
-  gtk_table_attach( GTK_TABLE(pv_table), pv_sub_table, 0, 1, 2, 3,
+  gtk_table_attach( GTK_TABLE(pv_table), pv_sub_table, 0, 1, 3, 4,
                     GTK_FILL|GTK_EXPAND, 0, 0, 0 );
   gtk_widget_show (pv_sub_table);
 
@@ -4746,6 +4899,8 @@ mov_path_prevw_draw ( t_mov_gui_stuff *mgp, gint update )
 
   if(gap_debug) printf("mov_path_prevw_draw: START update:%d\n", (int)update);
 
+  mov_upd_seg_labels(NULL, mgp);
+
   if(mgp->pv_ptr == NULL)
   {
     return;
@@ -4995,6 +5150,8 @@ mov_path_keyframe_update ( GtkWidget *widget, t_mov_gui_stuff *mgp)
 {
   gimp_int_adjustment_update(GTK_ADJUSTMENT(widget), &mgp->keyframe_abs);
   p_accel_widget_sensitivity(mgp);
+  p_points_to_tab(mgp);
+  mov_upd_seg_labels(NULL, mgp);
 }
 
 
@@ -5094,6 +5251,8 @@ mov_path_acceleration_adjustment_update(GtkWidget *widget,
   if(mgp == NULL) return;
   old_val = *val;
   gimp_int_adjustment_update(GTK_ADJUSTMENT(widget), (gpointer)val);
+  p_points_to_tab(mgp);
+  mov_upd_seg_labels(NULL, mgp);
 
   return;
 
diff --git a/gap/gap_mov_dialog.h b/gap/gap_mov_dialog.h
index a37ec08..c3be9a2 100755
--- a/gap/gap_mov_dialog.h
+++ b/gap/gap_mov_dialog.h
@@ -243,6 +243,22 @@ typedef struct {
         GapMovValues *val_ptr;
 } GapMovData;
 
+
+typedef struct {
+    /* IN */
+   gint  pointIndexToQuery;
+   gint  startOfSegmentIndexToQuery;
+   gint  endOfSegmentIndexToQuery;
+   gint  tweenCount;
+   
+   /* OUT */
+   gint    segmentNumber;
+   gdouble pathSegmentLengthInPixels;
+   gdouble maxSpeedInPixelsPerFrame;
+   gdouble minSpeedInPixelsPerFrame;
+} GapMovQuery;
+
+
 long  gap_mov_dlg_move_dialog (GapMovData *mov_ptr);
 
 #endif
diff --git a/gap/gap_mov_exec.c b/gap/gap_mov_exec.c
index add6c30..d3fe865 100755
--- a/gap/gap_mov_exec.c
+++ b/gap/gap_mov_exec.c
@@ -81,6 +81,7 @@ static void p_add_tween_and_trace(gint32 dest_image_id, GapMovData *mov_ptr, Gap
 static gint p_mov_call_render(GapMovData *mov_ptr, GapMovCurrent *cur_ptr, gint apv_layerstack);
 static void p_mov_advance_src_layer(GapMovCurrent *cur_ptr, GapMovValues  *pvals);
 static void p_mov_advance_src_frame(GapMovCurrent *cur_ptr, GapMovValues  *pvals);
+static long   p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query);
 static long   p_mov_execute(GapMovData *mov_ptr);
 static gdouble  p_calc_angle(gint p1x, gint p1y, gint p2x, gint p2y);
 static gdouble  p_rotatate_less_than_180(gdouble angle, gdouble angle_new, gint *turns);
@@ -117,6 +118,7 @@ static gint     p_calculate_settings_for_current_FrameTween(
                    , long     availableCtrlPoints   /* number of available controlpoints */
                    , gint     startOfSegmentIndex
                    , gint     endOfSegmentIndex
+                   , GapMovQuery *mov_query
                    );
                     
 
@@ -935,11 +937,12 @@ p_calculate_settings_for_current_FrameTween(
    , long     availableCtrlPoints   /* number of available controlpoints */
    , gint     startOfSegmentIndex
    , gint     endOfSegmentIndex
-  )
+   , GapMovQuery *mov_query
+   )
 {
+  gint     frameNrAtEndOfSegment;
 
   gdouble tweenMultiplicator;
-  gint frameNrAtEndOfSegment;
   gdouble lengthFactorLinear;      /* 0.0 at begin of segment 1.0 at end of segment position */
   gdouble frameTweensInSegment;
   gdouble currFrameTweenInSegment; /* frame number relative to 0 at each starting point of a new segment 
@@ -949,11 +952,17 @@ p_calculate_settings_for_current_FrameTween(
   gdouble  posFactor;
   gdouble  posFactorMovement;
   gint     segmPtidx;              /* calculated controlpoint index of relevant line begin with current segment */
+  gdouble   prevX;
+  gdouble   prevY;
 
   posFactor = 0.0;
   posFactorMovement = 0.0;
   segmPtidx = -1;                  /* inital value -1 indicates that movement does NOT use acceleration characteristic */
 
+
+  prevX = cur_ptr->currX;
+  prevY = cur_ptr->currY;
+
   tweenMultiplicator = 1.0;
   if(val_ptr->tween_steps > 1.0)
   {
@@ -961,7 +970,7 @@ p_calculate_settings_for_current_FrameTween(
   }
 
   frameNrAtEndOfSegment = p_calculate_relframe_nr_at_index(val_ptr, endOfSegmentIndex, affectedFrames);
-  
+
   frameTweensInSegment = abs ( frameNrAtEndOfSegment
                              - p_calculate_relframe_nr_at_index(val_ptr, startOfSegmentIndex, affectedFrames)
                              ) + 1;
@@ -978,6 +987,17 @@ p_calculate_settings_for_current_FrameTween(
 
   pathSegmentLength = p_calculate_path_segment_length(val_ptr, startOfSegmentIndex, endOfSegmentIndex);
 
+  if(gap_debug)
+  {
+    printf("p_mov_execute:startOfSegmentIndex:%d accPosition:%d pathSegmentLength:%.4f currFrameIndex:%d\n"
+          ,(int)startOfSegmentIndex
+          ,(int)val_ptr->point[startOfSegmentIndex].accPosition
+          ,(float)pathSegmentLength
+          ,(int)currFrameIndex
+          );
+  }
+
+  
   /* calculate Movement settings for the currently processed Frame (or tween)
    * position dependent acceleration processing requires a path segment length > 0
    * AND accPosition != 0
@@ -997,7 +1017,6 @@ p_calculate_settings_for_current_FrameTween(
     cur_ptr->currX  =       GAP_BASE_MIX_VALUE(posFactorMovement, (gdouble)val_ptr->point[segmPtidx].p_x,      (gdouble)val_ptr->point[segmPtidx +1].p_x);
     cur_ptr->currY  =       GAP_BASE_MIX_VALUE(posFactorMovement, (gdouble)val_ptr->point[segmPtidx].p_y,      (gdouble)val_ptr->point[segmPtidx +1].p_y);
 
-
     if(gap_debug)
     {
        printf("p_mov_execute: currFrameIndex:%d Position, start/endOfSegmentIndex=%d/%d currFrameTweenInSegment=%d  frameTweensInSegment=%d\n"
@@ -1014,6 +1033,20 @@ p_calculate_settings_for_current_FrameTween(
              , (float)posFactorMovement
              , (int)segmPtidx
              );
+       printf("p_mov_execute: p_x[%d]:%.4f p_x[%d]:%.4f  currX:%.4f\n"
+             , (int)segmPtidx
+             , (float)val_ptr->point[segmPtidx].p_x
+             , (int)segmPtidx+1
+             , (float)val_ptr->point[segmPtidx +1].p_x
+             , (float)cur_ptr->currX
+             );
+       printf("p_mov_execute: p_y[%d]:%.4f p_y[%d]:%.4f  currY:%.4f\n"
+             , (int)segmPtidx
+             , (float)val_ptr->point[segmPtidx].p_y
+             , (int)segmPtidx+1
+             , (float)val_ptr->point[segmPtidx +1].p_y
+             , (float)cur_ptr->currY
+             );
     }
   }
   else
@@ -1033,6 +1066,69 @@ p_calculate_settings_for_current_FrameTween(
   }
 
 
+  /* for the query mode calculate max speed in the query segment */
+  if(mov_query != NULL)
+  {
+    gdouble dx;
+    gdouble dy;
+    gdouble pixelLengthInOneTweenStep;
+    gint    refPtidx;                    /* index to controlpoint that marks end of current line */
+
+    dx = fabs(cur_ptr->currX - prevX);
+    dy = fabs(cur_ptr->currY - prevY);
+    pixelLengthInOneTweenStep = sqrt((dx * dx) + (dy * dy));
+
+    refPtidx = currPtidx;
+    if (segmPtidx >= 0)
+    {
+      refPtidx = segmPtidx +1;
+    }
+
+    if(gap_debug) 
+    {
+      printf("p_mov_execute: pixelLengthInOneTweenStep=%f  pointIndexToQuery:%d  currPtidx:%d refPtidx:%d StartQuery:%d EndQuery:%d\n"
+           , (float)pixelLengthInOneTweenStep
+           , (int)mov_query->pointIndexToQuery
+           , (int)currPtidx
+           , (int)refPtidx
+           , (int)mov_query->startOfSegmentIndexToQuery
+           , (int)mov_query->endOfSegmentIndexToQuery
+           );
+    }
+
+    if((refPtidx >  mov_query->startOfSegmentIndexToQuery)
+    && (refPtidx <= mov_query->endOfSegmentIndexToQuery))
+    {
+      if(mov_query->tweenCount == 0)
+      {
+        mov_query->tweenCount++;
+        if(gap_debug) 
+        {
+          printf("p_mov_execute: SKIP max speed calculation at first frame of segment\n");
+        }
+      }
+      else
+      {
+        mov_query->maxSpeedInPixelsPerFrame = MAX(mov_query->maxSpeedInPixelsPerFrame, pixelLengthInOneTweenStep);
+        if (mov_query->minSpeedInPixelsPerFrame < 0)
+        {
+           mov_query->minSpeedInPixelsPerFrame = pixelLengthInOneTweenStep;
+        }
+        else
+        {
+           mov_query->minSpeedInPixelsPerFrame = MIN(mov_query->minSpeedInPixelsPerFrame, pixelLengthInOneTweenStep);
+        }
+      }
+      mov_query->pathSegmentLengthInPixels = pathSegmentLength;
+    }
+
+
+      
+  }
+
+
+
+
   /* calculate Opacity settings for the currently processed Frame (or tween) */
   if ((val_ptr->point[startOfSegmentIndex].accOpacity != 0)
   && (frameTweensInSegment > 0))
@@ -1208,7 +1304,7 @@ p_calculate_settings_for_current_FrameTween(
 
 
 /* ============================================================================
- * p_mov_execute
+ * p_mov_execute_or_query
  * Copy layer(s) from Sourceimage to given destination frame range,
  * varying koordinates and opacity of the copied layer.
  * To each affected destination frame exactly one copy of a source layer is added.
@@ -1219,7 +1315,7 @@ p_calculate_settings_for_current_FrameTween(
  * ============================================================================
  */
 long
-p_mov_execute(GapMovData *mov_ptr)
+p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
 {
    gint l_idx;
    GapMovCurrent l_current_data;
@@ -1259,7 +1355,9 @@ p_mov_execute(GapMovData *mov_ptr)
   frameNrAtEndOfSegment = 0;
   l_apv_layerstack = 0;
   l_percentage = 0.0;
-  if(mov_ptr->dst_ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
+
+  if((mov_ptr->dst_ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
+  && (mov_query == NULL))
   {
     if(mov_ptr->val_ptr->apv_mlayer_image < 0)
     {
@@ -1328,9 +1426,12 @@ p_mov_execute(GapMovData *mov_ptr)
    cur_ptr = &l_current_data;
    val_ptr = mov_ptr->val_ptr;
 
-   if(gap_image_is_alive(val_ptr->tmpsel_image_id))
+   if(mov_query == NULL)
    {
-     gimp_image_delete(val_ptr->tmpsel_image_id);
+     if(gap_image_is_alive(val_ptr->tmpsel_image_id))
+     {
+       gimp_image_delete(val_ptr->tmpsel_image_id);
+     }
    }
 
    val_ptr->tmpsel_image_id = -1;
@@ -1400,66 +1501,69 @@ p_mov_execute(GapMovData *mov_ptr)
 
    cur_ptr->dst_frame_nr = val_ptr->dst_range_start;
    cur_ptr->src_layers = NULL;
-
-   if(mov_ptr->val_ptr->src_stepmode < GAP_STEP_FRAME)
+ 
+   if(mov_query == NULL)
    {
-     gint32        l_sel_channel_id;
-     gboolean      l_all_empty;
-
-     if(val_ptr->src_selmode != GAP_MOV_SEL_IGNORE)
+     if(mov_ptr->val_ptr->src_stepmode < GAP_STEP_FRAME)
      {
-       l_all_empty = FALSE;
-       if(gimp_selection_is_empty(val_ptr->src_image_id))
+       gint32        l_sel_channel_id;
+       gboolean      l_all_empty;
+
+       if(val_ptr->src_selmode != GAP_MOV_SEL_IGNORE)
        {
-         l_all_empty = TRUE;
+         l_all_empty = FALSE;
+         if(gimp_selection_is_empty(val_ptr->src_image_id))
+         {
+           l_all_empty = TRUE;
+         }
+         l_sel_channel_id = gimp_image_get_selection(val_ptr->src_image_id);
+         gap_mov_render_create_or_replace_tempsel_image(l_sel_channel_id, val_ptr, l_all_empty);
        }
-       l_sel_channel_id = gimp_image_get_selection(val_ptr->src_image_id);
-       gap_mov_render_create_or_replace_tempsel_image(l_sel_channel_id, val_ptr, l_all_empty);
-     }
 
-     cur_ptr->src_layers = gimp_image_get_layers (val_ptr->src_image_id, &l_nlayers);
-     if(cur_ptr->src_layers == NULL)
-     {
-       printf("ERROR (in p_mov_execute): Got no layers from SrcImage\n");
-       return -1;
+       cur_ptr->src_layers = gimp_image_get_layers (val_ptr->src_image_id, &l_nlayers);
+       if(cur_ptr->src_layers == NULL)
+       {
+         printf("ERROR (in p_mov_execute): Got no layers from SrcImage\n");
+         return -1;
+       }
+       if(l_nlayers < 1)
+       {
+         printf("ERROR (in p_mov_execute): Source Image has no layers\n");
+         return -1;
+       }
+       cur_ptr->src_last_layer = l_nlayers -1;
+
+       /* findout index of src_layer_id */
+       for(cur_ptr->src_layer_idx = 0;
+           cur_ptr->src_layer_idx  < l_nlayers;
+           cur_ptr->src_layer_idx++)
+       {
+          if(cur_ptr->src_layers[cur_ptr->src_layer_idx] == val_ptr->src_layer_id)
+          {
+             cur_ptr->src_layer_idx_dbl = (gdouble)cur_ptr->src_layer_idx;
+             break;
+          }
+       }
+       cur_ptr->src_last_layer = l_nlayers -1;   /* index of last layer */
      }
-     if(l_nlayers < 1)
+     else
      {
-       printf("ERROR (in p_mov_execute): Source Image has no layers\n");
-       return -1;
-     }
-     cur_ptr->src_last_layer = l_nlayers -1;
+       /* for FRAME stepmodes we use flattened Sorce frames
+        * (instead of one multilayer source image )
+        */
+       gap_mov_render_fetch_src_frame (val_ptr, -1);  /* negative value fetches the selected frame number */
+       cur_ptr->src_frame_idx = val_ptr->cache_ainfo_ptr->curr_frame_nr;
+       cur_ptr->src_frame_idx_dbl = (gdouble)cur_ptr->src_frame_idx;
 
-     /* findout index of src_layer_id */
-     for(cur_ptr->src_layer_idx = 0;
-         cur_ptr->src_layer_idx  < l_nlayers;
-         cur_ptr->src_layer_idx++)
-     {
-        if(cur_ptr->src_layers[cur_ptr->src_layer_idx] == val_ptr->src_layer_id)
-        {
-           cur_ptr->src_layer_idx_dbl = (gdouble)cur_ptr->src_layer_idx;
-           break;
-        }
-     }
-     cur_ptr->src_last_layer = l_nlayers -1;   /* index of last layer */
-   }
-   else
-   {
-     /* for FRAME stepmodes we use flattened Sorce frames
-      * (instead of one multilayer source image )
-      */
-     gap_mov_render_fetch_src_frame (val_ptr, -1);  /* negative value fetches the selected frame number */
-     cur_ptr->src_frame_idx = val_ptr->cache_ainfo_ptr->curr_frame_nr;
-     cur_ptr->src_frame_idx_dbl = (gdouble)cur_ptr->src_frame_idx;
+       if((val_ptr->cache_ainfo_ptr->first_frame_nr < 0)
+       && (val_ptr->src_stepmode != GAP_STEP_FRAME_NONE))
+       {
+          gap_lib_dir_ainfo(val_ptr->cache_ainfo_ptr);
+       }
 
-     if((val_ptr->cache_ainfo_ptr->first_frame_nr < 0)
-     && (val_ptr->src_stepmode != GAP_STEP_FRAME_NONE))
-     {
-        gap_lib_dir_ainfo(val_ptr->cache_ainfo_ptr);
+       /* set offsets (in cur_ptr)  according to handle mode and cache_tmp_img dimension */
+       gap_mov_exec_set_handle_offsets(val_ptr, cur_ptr);
      }
-
-     /* set offsets (in cur_ptr)  according to handle mode and cache_tmp_img dimension */
-     gap_mov_exec_set_handle_offsets(val_ptr, cur_ptr);
    }
 
    cur_ptr->currX   = (gdouble)val_ptr->point[0].p_x;
@@ -1493,6 +1597,7 @@ p_mov_execute(GapMovData *mov_ptr)
    val_ptr->trace_layer_id = -1;
 
    /* create temp images for tween processing and object tracing */
+   if(mov_query == NULL)
    {
      gint32 master_image_id;
 
@@ -1582,12 +1687,39 @@ p_mov_execute(GapMovData *mov_ptr)
      l_flt_count += l_fpl;
      l_flt_timing[l_ptidx] = l_flt_count;
 
-     if(l_fpl < 1.0)
+     if((l_fpl < 1.0) && (mov_query == NULL))
      {
         printf("p_mov_execute: ** Error frames per line at point[%d] = %f  (is less than 1.0 !!)\n",
           (int)l_ptidx, (float)l_fpl);
      }
    }
+   
+   /* in query mode: find start end end of segment that contains the pointIndexToQuery */
+   if(mov_query != NULL)
+   {
+     mov_query->tweenCount = 0;
+     mov_query->startOfSegmentIndexToQuery = 0;
+     mov_query->endOfSegmentIndexToQuery = l_points - 1;
+     for(l_ptidx=1;  l_ptidx < l_points - 1; l_ptidx++)
+     {
+       if (val_ptr->point[l_ptidx].keyframe > 0)
+       {
+         if (l_ptidx <= mov_query->pointIndexToQuery)
+         {
+           mov_query->segmentNumber++;
+           mov_query->startOfSegmentIndexToQuery = l_ptidx;
+         }
+         
+         if (l_ptidx > mov_query->pointIndexToQuery)
+         {
+           mov_query->endOfSegmentIndexToQuery = l_ptidx;
+           break;
+         }
+       }
+       
+     }
+      
+   }
 
    if(gap_debug)
    {
@@ -1611,26 +1743,46 @@ p_mov_execute(GapMovData *mov_ptr)
      if(gap_debug) printf("\np_mov_execute: l_fridx=%ld, l_flt_timing[l_ptidx]=%f, l_rc=%d l_ptidx=%d, l_prev_keyptidx=%d\n",
                            l_fridx, (float)l_flt_timing[l_ptidx], (int)l_rc, (int)l_ptidx, (int)l_prev_keyptidx);
 
-     if(l_rc != 0) break;
+     if(l_rc != 0)
+     {
+       break;
+     }
 
       /* advance frame_nr, (1st frame was done outside this loop) */
       cur_ptr->dst_frame_nr += l_frame_step;  /* +1  or -1 */
 
       if((gdouble)l_fridx > l_flt_timing[l_ptidx])
       {
+         /*  fix for object jumps forth and back as reported in #607927 */
+         if(val_ptr->point[l_ptidx].keyframe > 0)
+         {
+           if((endOfSegmentIndex != l_points -1) && (endOfSegmentIndex > 0))
+           {
+             startOfSegmentIndex = endOfSegmentIndex;
+           }
+         }
+ 
          /* change deltas for next line of the move path */
          if(l_ptidx < l_points-1)
          {
            l_ptidx++;
+           
            if(gap_debug)
            {
-              printf("p_mov_execute: advance to controlpoint l_ptidx=%d, l_flt_timing[l_ptidx]=%f\n"
-                     , (int)l_ptidx, (float)l_flt_timing[l_ptidx]);
+              printf("p_mov_execute: advance to controlpoint l_ptidx=%d, l_flt_timing[l_ptidx]=%f  startOfSegmentIndex:%d endOfSegmentIndex:%d\n"
+                     , (int)l_ptidx
+                     , (float)l_flt_timing[l_ptidx]
+                     , (int)startOfSegmentIndex
+                     , (int)endOfSegmentIndex
+                     );
            }
          }
          else
          {
-           if(gap_debug) printf("p_mov_execute: ** ERROR overflow l_ptidx=%d\n", (int)l_ptidx);
+           if(gap_debug)
+           {
+             printf("p_mov_execute: ** ERROR overflow l_ptidx=%d\n", (int)l_ptidx);
+           }
          }
       }
 
@@ -1661,7 +1813,8 @@ p_mov_execute(GapMovData *mov_ptr)
         l_flt_posfactor = CLAMP (l_flt_posfactor, 0.0, 1.0);
       
         endOfSegmentIndex = p_findEndOfSegmentIndex(val_ptr, startOfSegmentIndex, l_points);
-      
+        frameNrAtEndOfSegment = p_calculate_relframe_nr_at_index(val_ptr, endOfSegmentIndex, l_frames);
+
         frameNrAtEndOfSegment = p_calculate_settings_for_current_FrameTween(val_ptr, cur_ptr
              , l_fpl
              , l_fridx
@@ -1671,33 +1824,55 @@ p_mov_execute(GapMovData *mov_ptr)
              , l_points
              , startOfSegmentIndex
              , endOfSegmentIndex
+             , mov_query
              );
 
 
-
-        if(val_ptr->src_stepmode < GAP_STEP_FRAME )
+        
+        if(mov_query != NULL)
         {
-           /* advance settings for next src layer */
-           p_mov_advance_src_layer(cur_ptr, val_ptr);
+          /* we run in query mode, just check if controlpoint for query is reached
+           * and stop (without rendering)
+           */
+          if(gap_debug) 
+          {
+            printf("BREAK check: endOfSegmentIndexToQuery:%d  l_ptidx:%d\n"
+                 , (int)mov_query->endOfSegmentIndexToQuery
+                 , (int)l_ptidx
+                 );
+          }
+          
+          if(l_ptidx > mov_query->endOfSegmentIndexToQuery)
+          {
+            return (l_rc);
+          }
         }
         else
         {
-          /* advance settings for next source frame */
-          p_mov_advance_src_frame(cur_ptr, val_ptr);
-        }
+          if(val_ptr->src_stepmode < GAP_STEP_FRAME )
+          {
+             /* advance settings for next src layer */
+             p_mov_advance_src_layer(cur_ptr, val_ptr);
+          }
+          else
+          {
+            /* advance settings for next source frame */
+            p_mov_advance_src_frame(cur_ptr, val_ptr);
+          }
 
-        if(l_frame_step < 0)
-        {
-          /* if we step down, we have to insert the layer
-           * as lowest layer in the existing layerstack
-           * of the animated preview multilayer image.
-           * (if we step up, we always use 0 as l_apv_layerstack,
-           *  that means always insert on top of the layerstack)
-           */
-          l_apv_layerstack++;
+          if(l_frame_step < 0)
+          {
+            /* if we step down, we have to insert the layer
+             * as lowest layer in the existing layerstack
+             * of the animated preview multilayer image.
+             * (if we step up, we always use 0 as l_apv_layerstack,
+             *  that means always insert on top of the layerstack)
+             */
+            l_apv_layerstack++;
+          }
+          /* RENDER add current src_layer to current frame */
+          l_rc = p_mov_call_render(mov_ptr, cur_ptr, l_apv_layerstack);
         }
-        /* RENDER add current src_layer to current frame */
-        l_rc = p_mov_call_render(mov_ptr, cur_ptr, l_apv_layerstack);
 
       }  /* end tweenindex subloop */
       
@@ -1708,7 +1883,8 @@ p_mov_execute(GapMovData *mov_ptr)
       }
 
       /* show progress */
-      if(mov_ptr->dst_ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
+      if((mov_ptr->dst_ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
+      && (mov_query == NULL))
       {
         l_percentage = (gdouble)l_fridx / (gdouble)(l_cnt -1);
         gimp_progress_update (l_percentage);
@@ -1716,24 +1892,27 @@ p_mov_execute(GapMovData *mov_ptr)
 
    }  /* end frameindex loop */
 
-   /* delete the tween image */
-   if(val_ptr->tween_image_id >= 0)
+   if(mov_query == NULL)
    {
-     gimp_image_delete(val_ptr->tween_image_id);
-     val_ptr->tween_image_id = -1;
-   }
+     /* delete the tween image */
+     if(val_ptr->tween_image_id >= 0)
+     {
+       gimp_image_delete(val_ptr->tween_image_id);
+       val_ptr->tween_image_id = -1;
+     }
 
-   /* delete the trace image */
-   if(val_ptr->trace_image_id >= 0)
-   {
-     gimp_image_delete(val_ptr->trace_image_id);
-     val_ptr->trace_image_id = -1;
-   }
+     /* delete the trace image */
+     if(val_ptr->trace_image_id >= 0)
+     {
+       gimp_image_delete(val_ptr->trace_image_id);
+       val_ptr->trace_image_id = -1;
+     }
 
-   /* delete the temp selection image */
-   if(gap_image_is_alive(val_ptr->tmpsel_image_id))
-   {
-     gimp_image_delete(val_ptr->tmpsel_image_id);
+     /* delete the temp selection image */
+     if(gap_image_is_alive(val_ptr->tmpsel_image_id))
+     {
+       gimp_image_delete(val_ptr->tmpsel_image_id);
+     }
    }
 
    val_ptr->tmpsel_image_id = -1;
@@ -1746,7 +1925,29 @@ p_mov_execute(GapMovData *mov_ptr)
 
    return l_rc;
 
-}       /* end p_mov_execute */
+}       /* end p_mov_execute_or_query */
+
+/* ============================================================================
+ * p_mov_execute
+ * Copy layer(s) from Sourceimage to given destination frame range,
+ * varying koordinates and opacity of the copied layer.
+ * To each affected destination frame exactly one copy of a source layer is added.
+ * The source layer is iterated through all layers of the sourceimage
+ * according to stemmode parameter.
+ * For the placement the layers act as if their size is equal to their
+ * Sourceimages size.
+ * ============================================================================
+ */
+static long
+p_mov_execute(GapMovData *mov_ptr)
+{
+  GapMovQuery *mov_query;
+
+  mov_query = NULL;
+  return(p_mov_execute_or_query(mov_ptr, mov_query));
+}
+
+
 
 
 /* ============================================================================
@@ -2828,6 +3029,56 @@ gap_mov_exec_move_path(GimpRunMode run_mode, gint32 image_id, GapMovValues *pval
 }       /* end gap_mov_exec_move_path */
 
 
+
+
+/* ============================================================================
+ * gap_mov_exec_query
+ *
+ * fill information  of the mov_query struct.
+ * ============================================================================
+ */
+void
+gap_mov_exec_query(GapMovValues *val_ptr, GapAnimInfo *ainfo_ptr, GapMovQuery *mov_query)
+{
+  GapMovData    l_mov_data;
+  GapMovData   *l_mov_ptr;
+  GapMovValues  l_mov_vals;
+  GapMovValues *l_pvals;
+
+  l_mov_ptr = &l_mov_data;
+  l_pvals = &l_mov_vals;
+  
+  if((mov_query != NULL) && (val_ptr != NULL))
+  {
+    /* init query results */
+    mov_query->pathSegmentLengthInPixels = 0.0;
+    mov_query->maxSpeedInPixelsPerFrame = 0.0;
+    mov_query->minSpeedInPixelsPerFrame = -1.0;
+    mov_query->segmentNumber = 0;
+    /* copy settings */
+    memcpy(l_pvals, val_ptr, sizeof(GapMovValues));
+    l_mov_ptr->val_ptr = l_pvals;
+    l_mov_ptr->dst_ainfo_ptr = ainfo_ptr;
+
+    /* init local cached src image for anim preview generation.
+     * (never mix cached src image for normal and anim preview
+     *  because anim previews are often scaled down)
+     */
+    l_pvals->cache_src_image_id  = -1;
+    l_pvals->cache_tmp_image_id  = -1;
+    l_pvals->cache_tmp_layer_id  = -1;
+    l_pvals->cache_frame_number  = -1;
+    l_pvals->cache_ainfo_ptr = NULL;
+    
+    p_mov_execute_or_query(l_mov_ptr, mov_query);
+    if(mov_query->minSpeedInPixelsPerFrame < 0)
+    {
+      mov_query->minSpeedInPixelsPerFrame = -1.0;
+    }
+  }
+}
+
+
 /* ============================================================================
  * gap_mov_exec_set_handle_offsets
  *  set handle offsets according to handle mode and src image dimensions
diff --git a/gap/gap_mov_exec.h b/gap/gap_mov_exec.h
index dcc2338..81dca03 100644
--- a/gap/gap_mov_exec.h
+++ b/gap/gap_mov_exec.h
@@ -51,6 +51,7 @@ gint    gap_mov_exec_gap_save_pointfile(char *filename, GapMovValues *pvals);
 gint    gap_mov_exec_gap_load_pointfile(char *filename, GapMovValues *pvals);
 void    gap_mov_exec_calculate_rotate_follow(GapMovValues *pvals, gdouble startangle);
 void    gap_mov_exec_set_handle_offsets(GapMovValues *val_ptr, GapMovCurrent *cur_ptr);
+void    gap_mov_exec_query(GapMovValues *val_ptr, GapAnimInfo *ainfo_ptr, GapMovQuery *mov_query);
 
 #endif
 
diff --git a/gap/gap_mov_render.c b/gap/gap_mov_render.c
index b792db9..4d594d0 100644
--- a/gap/gap_mov_render.c
+++ b/gap/gap_mov_render.c
@@ -369,7 +369,9 @@ gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur
   guint        l_image_height;
   GimpLayerModeEffects l_mode;
 
-  if(gap_debug) printf("gap_mov_render_render: frame/layer: %ld/%ld  X=%f, Y=%f\n"
+  if(gap_debug) 
+  {
+    printf("gap_mov_render_render: frame/layer: %ld/%ld  X=%f, Y=%f\n"
                 "       Width=%f Height=%f\n"
                 "       Opacity=%f  Rotate=%f  clip_to_img = %d force_visibility = %d\n"
                 "       src_stepmode = %d\n",
@@ -382,7 +384,8 @@ gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur
                      val_ptr->clip_to_img,
                      val_ptr->src_force_visible,
                      val_ptr->src_stepmode);
-
+  }
+  
   if(val_ptr->src_stepmode < GAP_STEP_FRAME)
   {
     if(gap_debug)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]