d2/align: Move astate_t back from Libale.
[Ale.git] / d2 / align.h
index e943424..bd78134 100644 (file)
@@ -322,6 +322,322 @@ private:
        static int ax_count;
 
        /*
+        * XXX: note that following in the case of non-primary elements must be
+        * split, so that Libale performs adjustments according to the
+        * immediate change in the parent element (likely depending on the
+        * alignment properties specified); for this and other reasons, it
+        * would be desirable to rewrite following logic more concisely, using,
+        * e.g., the recently-introduced ale_eval to facilitate abstraction, so
+        * that the split in functionality can be performed more cleanly.
+        */
+
+       /*
+        * Alignment state
+        *
+        * This structure contains alignment state information.  The change
+        * between the non-default old initial alignment and old final
+        * alignment is used to adjust the non-default current initial
+        * alignment.  If either the old or new initial alignment is a default
+        * alignment, the old --follow semantics are preserved.
+        */
+
+       class astate_t {
+               ale_trans old_initial_alignment;
+               ale_trans old_final_alignment;
+               ale_trans default_initial_alignment;
+               int old_is_default;
+               std::vector<int> is_default;
+               ale_image input_frame;
+
+       public:
+               astate_t() : 
+                               is_default(1) {
+
+                       old_initial_alignment = ale_new_trans(accel::context(), NULL);
+                       old_final_alignment = ale_new_trans(accel::context(), NULL);
+                       default_initial_alignment = ale_new_trans(accel::context(), NULL);
+
+                       input_frame = NULL;
+                       is_default[0] = 1;
+                       old_is_default = 1;
+               }
+
+               ale_image get_input_frame() const {
+                       return input_frame;
+               }
+
+               void set_is_default(unsigned int index, int value) {
+
+                       /*
+                        * Expand the array, if necessary.
+                        */
+                       if (index == is_default.size());
+                               is_default.resize(index + 1);
+
+                       assert (index < is_default.size());
+                       is_default[index] = value;
+               }
+
+               int get_is_default(unsigned int index) {
+                       assert (index < is_default.size());
+                       return is_default[index];
+               }
+
+               ale_trans get_default() {
+                       return default_initial_alignment;
+               }
+
+               void set_default(ale_trans t) {
+                       default_initial_alignment = t;
+               }
+               
+               void default_set_original_bounds(ale_image i) {
+                       ale_trans_set_original_bounds(default_initial_alignment, i);
+               }
+
+               void set_final(ale_trans t) {
+                       old_final_alignment = t;
+               }
+
+               void set_input_frame(ale_image i) {
+                       input_frame = i;
+               }
+
+               /*
+                * Implement new delta --follow semantics.
+                *
+                * If we have a transformation T such that
+                *
+                *      prev_final == T(prev_init)
+                *
+                * Then we also have
+                *
+                *      current_init_follow == T(current_init)
+                *
+                * We can calculate T as follows:
+                *
+                *      T == prev_final(prev_init^-1)
+                *
+                * Where ^-1 is the inverse operator.
+                */
+               static trans_single follow(trans_single a, trans_single b, trans_single c) {
+                       trans_single cc = c;
+
+                       if (alignment_class == 0) {
+                               /*
+                                * Translational transformations
+                                */
+
+                               ale_pos t0 = -a.eu_get(0) +  b.eu_get(0);
+                               ale_pos t1 = -a.eu_get(1) +  b.eu_get(1);
+
+                               cc.eu_modify(0, t0);
+                               cc.eu_modify(1, t1);
+
+                       } else if (alignment_class == 1) {
+                               /*
+                                * Euclidean transformations
+                                */
+
+                               ale_pos t2 = -a.eu_get(2) +  b.eu_get(2);
+
+                               cc.eu_modify(2, t2);
+
+                               point p( c.scaled_height()/2 + c.eu_get(0) - a.eu_get(0),
+                                        c.scaled_width()/2 + c.eu_get(1) - a.eu_get(1) );
+
+                               p = b.transform_scaled(p);
+
+                               cc.eu_modify(0, p[0] - c.scaled_height()/2 - c.eu_get(0));
+                               cc.eu_modify(1, p[1] - c.scaled_width()/2 - c.eu_get(1));
+                               
+                       } else if (alignment_class == 2) {
+                               /*
+                                * Projective transformations
+                                */
+
+                               point p[4];
+
+                               p[0] = b.transform_scaled(a
+                                    . scaled_inverse_transform(c.transform_scaled(point(      0        ,       0       ))));
+                               p[1] = b.transform_scaled(a
+                                    . scaled_inverse_transform(c.transform_scaled(point(c.scaled_height(),       0       ))));
+                               p[2] = b.transform_scaled(a
+                                    . scaled_inverse_transform(c.transform_scaled(point(c.scaled_height(), c.scaled_width()))));
+                               p[3] = b.transform_scaled(a
+                                    . scaled_inverse_transform(c.transform_scaled(point(      0        , c.scaled_width()))));
+
+                               cc.gpt_set(p);
+                       }
+
+                       return cc;
+               }
+
+               /* 
+                * For multi-alignment following, we use the following approach, not
+                * guaranteed to work with large changes in scene or perspective, but
+                * which should be somewhat flexible:
+                *
+                * For 
+                *
+                *      t[][] calculated final alignments 
+                *      s[][] alignments as loaded from file
+                *      previous frame n
+                *      current frame n+1
+                *      fundamental (primary) 0
+                *      non-fundamental (non-primary) m!=0
+                *      parent element m'
+                *      follow(a, b, c) applying the (a, b) delta T=b(a^-1) to c
+                *
+                * following in the case of missing file data might be generated by 
+                *
+                *      t[n+1][0] = t[n][0]
+                *      t[n+1][m!=0] = follow(t[n][m'], t[n+1][m'], t[n][m])
+                *
+                * cases with all noted file data present might be generated by 
+                *
+                *      t[n+1][0] = follow(s[n][0], t[n][0], s[n+1][0])
+                *      t[n+1][m!=0] = follow(s[n+1][m'], t[n+1][m'], s[n+1][m])
+                *
+                * For non-following behavior, or where assigning the above is
+                * impossible, we assign the following default
+                *
+                *      t[n+1][0] = Identity
+                *      t[n+1][m!=0] = t[n+1][m']
+                */
+
+               void init_frame_alignment_primary(transformation *offset, int lod, ale_pos perturb) {
+
+                       if (perturb > 0 && !old_is_default && !get_is_default(0)
+                        && default_initial_alignment_type == 1) {
+
+                               /*
+                                * Apply following logic for the primary element.
+                                */
+
+                               ui::get()->following();
+
+                               trans_single new_offset = follow(old_initial_alignment.get_element(0),
+                                                               old_final_alignment.get_element(0), 
+                                                               offset->get_element(0));
+
+                               old_initial_alignment = *offset;
+
+                               offset->set_element(0, new_offset);
+
+                               ui::get()->set_offset(new_offset);
+                       } else {
+                               old_initial_alignment = *offset;
+                       }
+
+                       is_default.resize(old_initial_alignment.stack_depth());
+               }
+
+               void init_frame_alignment_nonprimary(transformation *offset, 
+                               int lod, ale_pos perturb, unsigned int index) {
+
+                       assert (index > 0);
+
+                       unsigned int parent_index = offset->parent_index(index);
+
+                       if (perturb > 0 
+                        && !get_is_default(parent_index)
+                        && !get_is_default(index)
+                        && default_initial_alignment_type == 1) {
+
+                               /*
+                                * Apply file-based following logic for the
+                                * given element.
+                                */
+
+                               ui::get()->following();
+
+                               trans_single new_offset = follow(old_initial_alignment.get_element(parent_index),
+                                                               offset->get_element(parent_index), 
+                                                               offset->get_element(index));
+
+                               old_initial_alignment.set_element(index, offset->get_element(index));
+                               offset->set_element(index, new_offset);
+
+                               ui::get()->set_offset(new_offset);
+
+                               return;
+                       }
+
+                       offset->get_coordinate(parent_index);
+
+
+                       if (perturb > 0
+                        && old_final_alignment.exists(offset->get_coordinate(parent_index))
+                        && old_final_alignment.exists(offset->get_current_coordinate())
+                        && default_initial_alignment_type == 1) {
+
+                               /*
+                                * Apply nonfile-based following logic for
+                                * the given element.
+                                */
+
+                               ui::get()->following();
+
+                               /*
+                                * XXX: Although it is different, the below
+                                * should be equivalent to the comment
+                                * description.
+                                */
+
+                               trans_single a = old_final_alignment.get_element(offset->get_coordinate(parent_index));
+                               trans_single b = old_final_alignment.get_element(offset->get_current_coordinate());
+                               trans_single c = offset->get_element(parent_index);
+                               
+                               trans_single new_offset = follow(a, b, c);
+
+                               offset->set_element(index, new_offset);
+                               ui::get()->set_offset(new_offset);
+
+                               return;
+                       }
+
+                       /*
+                        * Handle other cases.
+                        */
+
+                       if (get_is_default(index)) {
+                               offset->set_element(index, offset->get_element(parent_index));
+                               ui::get()->set_offset(offset->get_element(index));
+                       }
+               }
+
+               void init_default() {
+
+                       if (default_initial_alignment_type == 0) {
+                               
+                               /*
+                                * Follow the transformation of the original frame,
+                                * setting new image dimensions.
+                                */
+
+                               // astate->default_initial_alignment = orig_t;
+                               default_initial_alignment.set_current_element(orig_t.get_element(0));
+                               default_initial_alignment.set_dimensions(input_frame);
+
+                       } else if (default_initial_alignment_type == 1)
+
+                               /*
+                                * Follow previous transformation, setting new image
+                                * dimensions.
+                                */
+
+                               default_initial_alignment.set_dimensions(input_frame);
+
+                       else
+                               assert(0);
+
+                       old_is_default = get_is_default(0);
+               }
+       };
+
+
+       /*
         * Types for scale clusters.
         */