+ // Return a closeness metric of an "end" rectangle to a "start"
+ // rectangle in the upward (-1) or downward (+1) direction. Given
+ // several possible "end" rectangles, the one that seems visually
+ // closest in the given direction should have the highest value of
+ // this metric. This is necessarily a heuristic function!
+ double directed_closeness(const rectangle & start, const rectangle & end,
+ int y_dir)
+ {
+ // The obvious approach is to use the centres of the
+ // rectangles. However, for the "end" rectangle, using the
+ // horizontal position nearest the centre of the "start"
+ // rectangle seems to produce more reasonable results. For
+ // example, if there are two "end" rectangles equally near to
+ // the "start" rectangle in terms of vertical distance and one
+ // of them horizontally overlaps the centre of the "start"
+ // rectangle, we want to pick that one even if the centre of
+ // that rectangle is further away from the centre of the
+ // "start" rectangle.
+ int start_x = (start.left + start.right) / 2;
+ int start_y = (start.top + start.bottom) / 2;
+ int end_y = (end.top + end.bottom) / 2;
+ int end_x;
+ if (end.right < start_x)
+ end_x = end.right;
+ else if (end.left > start_x)
+ end_x = end.left;
+ else
+ end_x = start_x;
+
+ // Return cosine of angle between the line between these points
+ // and the vertical, divided by the distance between the points
+ // if that is defined and positive; otherwise return 0.
+ int vertical_distance = (end_y - start_y) * y_dir;
+ if (vertical_distance <= 0)
+ return 0.0;
+ double distance_squared =
+ (end_x - start_x) * (end_x - start_x)
+ + (end_y - start_y) * (end_y - start_y);
+ return vertical_distance / distance_squared;
+ }
+
+ std::string temp_file_name(const temp_dir & dir,
+ std::string base_name,
+ unsigned index=0)
+ {
+ if (index != 0)
+ {
+ std::size_t index_pos = base_name.find("%3d");
+ assert(index_pos != std::string::npos);
+ base_name[index_pos] = '0' + index / 100;
+ base_name[index_pos + 1] = '0' + (index / 10) % 10;
+ base_name[index_pos + 2] = '0' + index % 10;
+ }
+
+ return Glib::build_filename(dir.get_name(), base_name);
+ }
+
+ // We would like to use just a single frame for the menu but this
+ // seems not to be legal or compatible. The minimum length of a
+ // cell is 0.4 seconds but I've seen a static menu using 12 frames
+ // on a commercial "PAL" disc so let's use 12 frames regardless.
+ unsigned menu_duration_frames(const video::frame_params & params)
+ {
+ return 12;
+ }
+ double menu_duration_seconds(const video::frame_params & params)
+ {
+ return double(menu_duration_frames(params))
+ / double(params.rate_numer)
+ * double(params.rate_denom);
+ }
+
+ void throw_length_error(const char * limit_type, std::size_t limit)
+ {
+ std::ostringstream oss;
+ oss << "exceeded DVD limit: " << limit_type << " > " << limit;
+ throw std::length_error(oss.str());
+ }
+
+ // dvdauthor uses some menu numbers to represent entry points -
+ // distinct from the actual numbers of the menus assigned as those
+ // entry points - resulting in a practical limit of 119 per
+ // domain. This seems to be an oddity of the parser that could be
+ // fixed, but for now we'll have to work with it.
+ const unsigned dvdauthor_anonymous_menus_max = dvd::domain_pgcs_max - 8;
+
+ // The current navigation code packs menu and button number into a
+ // single register, so the number of menus is limited to
+ // dvd::reg_s8_button_mult - 1 == 1023. However temp_file_name()
+ // is limited to 999 numbered files and it seems pointless to
+ // change it to get another 24.
+ // If people really need more we could use separate menu and
+ // button number registers, possibly allowing up to 11900 menus
+ // (the size of the indirect jump tables might become a problem
+ // though).
+ const unsigned menus_max = 999;