diff options
| author | Carl Hetherington <cth@carlh.net> | 2025-07-14 22:51:44 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2025-09-27 22:41:38 +0200 |
| commit | 1dddce26733fc87e559e547003890357969350ca (patch) | |
| tree | cb6be65361fb492e88d7a008f08be9f5cf5372c9 /src/lib | |
| parent | 4e6e9f6303a0ea00c2bbc2978bde6f9284f6e02f (diff) | |
Draw markers better next to the position slider (#3005).
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/layout_markers.cc | 158 | ||||
| -rw-r--r-- | src/lib/layout_markers.h | 93 | ||||
| -rw-r--r-- | src/lib/wscript | 1 |
3 files changed, 252 insertions, 0 deletions
diff --git a/src/lib/layout_markers.cc b/src/lib/layout_markers.cc new file mode 100644 index 000000000..7b16b07f1 --- /dev/null +++ b/src/lib/layout_markers.cc @@ -0,0 +1,158 @@ +/* + Copyright (C) 2025 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "dcpomatic_time.h" +#include "layout_markers.h" +#include <dcp/types.h> +#include <functional> +#include <iostream> +#include <map> +#include <string> +#include <vector> + +#include "i18n.h" + + +using std::function; +using std::map; +using std::max; +using std::min; +using std::pair; +using std::string; +using std::vector; +using namespace std::placeholders; + + +vector<MarkerLayoutComponent> +layout_markers( + map<dcp::Marker, dcpomatic::DCPTime> const& markers, + int width_in_pixels, + dcpomatic::DCPTime width_in_time, + int label_to_end_gap, + int outside_label_gap, + function<int (string)> text_width +) +{ + float const pixels_per_unit_time = static_cast<float>(width_in_pixels) / width_in_time.get(); + auto pixels_between = [&](dcpomatic::DCPTime t1, dcpomatic::DCPTime t2) { + return pixels_per_unit_time * t2.get() - pixels_per_unit_time * t1.get(); + }; + + vector<AllocationRow> allocations; + + auto allocate = [&](int x1, int x2) -> int { + int index = 0; + for (auto& row: allocations) { + if (row.allocate(x1, x2)) { + return index; + } + ++index; + } + + auto row = AllocationRow(); + row.allocate(x1, x2); + allocations.push_back(row); + return index; + }; + + vector<MarkerLayoutComponent> components; + auto layout_pair = [&](string name, pair<dcp::Marker, dcpomatic::DCPTime> start, pair<dcp::Marker, dcpomatic::DCPTime> end) { + auto const width = text_width(name); + int const x1 = floor(pixels_per_unit_time * start.second.get()); + int const x2 = ceil(pixels_per_unit_time * end.second.get()); + + int label_x = 0; + int y = 0; + + if (pixels_between(start.second, end.second) <= text_width(name)) { + if (x1 > width_in_pixels / 2) { + label_x = x1 - outside_label_gap - width; + y = allocate(x1 - outside_label_gap - width, x2); + } else { + label_x = x2 + outside_label_gap; + y = allocate(x1, x2 + outside_label_gap + width); + } + } else { + label_x = (x1 + x2 - width) / 2; + y = allocate(x1, x2); + } + + components.push_back(MarkerLayoutComponent{MarkerLayoutComponent::Type::LEFT, x1, y, start.first, start.second}); + components.push_back(MarkerLayoutComponent{MarkerLayoutComponent::Type::RIGHT, x2, y, end.first, end.second}); + components.push_back(MarkerLayoutComponent{MarkerLayoutComponent::Type::LABEL, label_x, width, y, name}); + components.push_back(MarkerLayoutComponent{MarkerLayoutComponent::Type::LINE, x1, x2, y}); + }; + + auto layout_left = [&](string name, pair<dcp::Marker, dcpomatic::DCPTime> marker) { + int const x1 = floor(pixels_per_unit_time * marker.second.get()); + auto const width = text_width(name); + auto const y = allocate(x1, x1 + label_to_end_gap + width); + components.push_back(MarkerLayoutComponent{MarkerLayoutComponent::Type::LEFT, x1, y, marker.first, marker.second}); + components.push_back(MarkerLayoutComponent{MarkerLayoutComponent::Type::LABEL, x1 + label_to_end_gap, width, y, name}); + components.push_back(MarkerLayoutComponent{MarkerLayoutComponent::Type::LINE, x1, x1 + label_to_end_gap, y}); + }; + + auto layout_right = [&](string name, pair<dcp::Marker, dcpomatic::DCPTime> marker) { + int const x2 = floor(pixels_per_unit_time * marker.second.get()); + auto const width = text_width(name); + auto const y = allocate(x2 - label_to_end_gap - width, x2); + components.push_back(MarkerLayoutComponent{MarkerLayoutComponent::Type::RIGHT, x2, y, marker.first, marker.second}); + components.push_back(MarkerLayoutComponent{MarkerLayoutComponent::Type::LABEL, x2 - label_to_end_gap - width, width, y, name}); + components.push_back(MarkerLayoutComponent{MarkerLayoutComponent::Type::LINE, x2 - label_to_end_gap, x2, y}); + }; + + auto check_pair = [&](string name, dcp::Marker a, dcp::Marker b) { + auto fa = markers.find(a); + auto fb = markers.find(b); + if (fa != markers.end() && fb != markers.end()) { + layout_pair(name, *fa, *fb); + } else if (fa != markers.end()) { + layout_left(name, *fa); + } else if (fb != markers.end()) { + layout_right(name, *fb); + } + }; + + check_pair(_("RB"), dcp::Marker::FFOB, dcp::Marker::LFOB); + check_pair(_("TC"), dcp::Marker::FFTC, dcp::Marker::LFTC); + check_pair(_("IN"), dcp::Marker::FFOI, dcp::Marker::LFOI); + check_pair(_("EC"), dcp::Marker::FFEC, dcp::Marker::LFEC); + check_pair(_("MC"), dcp::Marker::FFMC, dcp::Marker::LFMC); + + return components; +} + + +bool +AllocationRow::allocate(int x1, int x2) +{ + auto overlaps = [](pair<int, int> a, pair<int, int> b) { + return max(a.first, b.first) <= min(a.second, b.second); + }; + + if (std::none_of(_allocated.begin(), _allocated.end(), std::bind(overlaps, pair{x1, x2}, _1))) { + _allocated.push_back({x1, x2}); + return true; + } + + return false; +} + diff --git a/src/lib/layout_markers.h b/src/lib/layout_markers.h new file mode 100644 index 000000000..5c615f20e --- /dev/null +++ b/src/lib/layout_markers.h @@ -0,0 +1,93 @@ +/* + Copyright (C) 2025 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "dcpomatic_time.h" +#include <dcp/types.h> +#include <string> +#include <utility> +#include <vector> + + +class MarkerLayoutComponent +{ +public: + enum class Type { + LEFT, + RIGHT, + LINE, + LABEL + }; + + MarkerLayoutComponent(Type type_, int x1_, int y_, dcp::Marker marker_, dcpomatic::DCPTime t1_) + : type(type_) + , x1(x1_) + , y(y_) + , marker(marker_) + , t1(t1_) + {} + + MarkerLayoutComponent(Type type_, int x1_, int x2_, int y_) + : type(type_) + , x1(x1_) + , x2(x2_) + , y(y_) + {} + + MarkerLayoutComponent(Type type_, int x1_, int width, int y_, std::string text_) + : type(type_) + , x1(x1_) + , x2(x1_ + width) + , y(y_) + , text(text_) + {} + + Type type = Type::LINE; + int x1 = 0; + int x2 = 0; + int y = 0; + boost::optional<dcp::Marker> marker; + dcpomatic::DCPTime t1; + std::string text; +}; + + +class AllocationRow +{ +public: + AllocationRow() = default; + + /** @return true if allocation succeded, otherwise false */ + bool allocate(int x1, int x2); + +private: + std::vector<std::pair<int, int>> _allocated; +}; + + +std::vector<MarkerLayoutComponent> layout_markers( + std::map<dcp::Marker, dcpomatic::DCPTime> const& markers, + int width_in_pixels, + dcpomatic::DCPTime width_in_time, + int label_to_end_gap, + int outside_label_gap, + std::function<int (std::string)> text_width +); + diff --git a/src/lib/wscript b/src/lib/wscript index ce93b5824..2e7b0339c 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -157,6 +157,7 @@ sources = """ kdm_recipient.cc kdm_with_metadata.cc kdm_util.cc + layout_markers.cc log.cc log_entry.cc make_dcp.cc |
