diff --git a/src/command/vis_tool.cpp b/src/command/vis_tool.cpp index 8079381db..56644304d 100644 --- a/src/command/vis_tool.cpp +++ b/src/command/vis_tool.cpp @@ -178,7 +178,7 @@ namespace { STR_HELP("When the surrounding plane is also visible, switches which quad is locked. If inactive, the inner quad can only be resized without changing the perspective plane. If active, this holds for the outer quad instead.") bool Validate(const agi::Context *c) override { - return c->videoDisplay->ToolIsType(typeid(VisualToolPerspective)) && c->videoDisplay->GetSubTool() | PERSP_OUTER; + return c->videoDisplay->ToolIsType(typeid(VisualToolPerspective)) && c->videoDisplay->GetSubTool() & PERSP_OUTER; } }; diff --git a/src/spline.cpp b/src/spline.cpp index ed7353f1c..d8c3fd828 100644 --- a/src/spline.cpp +++ b/src/spline.cpp @@ -41,17 +41,22 @@ #include -Spline::Spline(const VisualToolBase &tl) +Spline::Spline(const VisualToolBase *tl) : coord_translator(tl) { } Vector2D Spline::ToScript(Vector2D vec) const { - return coord_translator.ToScriptCoords(vec) * scale; + if (coord_translator) + vec = coord_translator->ToScriptCoords(vec); + return vec * scale; } Vector2D Spline::FromScript(Vector2D vec) const { - return coord_translator.FromScriptCoords(vec / scale); + vec = vec / scale; + if (coord_translator) + vec = coord_translator->FromScriptCoords(vec); + return vec; } void Spline::SetScale(int new_scale) { diff --git a/src/spline.h b/src/spline.h index 2594837e8..106df1d7d 100644 --- a/src/spline.h +++ b/src/spline.h @@ -36,7 +36,7 @@ class VisualToolBase; class Spline final : private std::vector { /// Visual tool to do the conversion between script and video pixels - const VisualToolBase &coord_translator; + const VisualToolBase *coord_translator = nullptr; /// Spline scale int scale = 0; int raw_scale = 0; @@ -47,7 +47,8 @@ class Spline final : private std::vector { /// Script coordinates -> Video coordinates Vector2D FromScript(Vector2D vec) const; public: - Spline(const VisualToolBase &scale); + Spline() {}; + Spline(const VisualToolBase *scale); /// Encode to an ASS vector drawing std::string EncodeToAss() const; diff --git a/src/spline_curve.cpp b/src/spline_curve.cpp index b1a799a0f..e59010124 100644 --- a/src/spline_curve.cpp +++ b/src/spline_curve.cpp @@ -115,6 +115,15 @@ Vector2D& SplineCurve::EndPoint() { } } +std::vector SplineCurve::AnchorPoints() { + switch (type) { + case POINT: return std::vector({p1}); + case LINE: return std::vector({p1, p2}); + case BICUBIC: return std::vector({p1, p2, p3, p4}); + default: return std::vector(); + } +} + Vector2D SplineCurve::GetClosestPoint(Vector2D ref) const { return GetPoint(GetClosestParam(ref)); } diff --git a/src/spline_curve.h b/src/spline_curve.h index 4630a64d2..59a713574 100644 --- a/src/spline_curve.h +++ b/src/spline_curve.h @@ -73,6 +73,7 @@ public: Vector2D GetPoint(float t) const; Vector2D& EndPoint(); + std::vector AnchorPoints(); /// Get point on the curve closest to reference Vector2D GetClosestPoint(Vector2D ref) const; /// Get t value for the closest point to reference diff --git a/src/visual_tool.cpp b/src/visual_tool.cpp index 24d1f40bb..0f8824a65 100644 --- a/src/visual_tool.cpp +++ b/src/visual_tool.cpp @@ -40,6 +40,8 @@ #include #include +#include +#include #include #include @@ -540,11 +542,9 @@ int VisualToolBase::GetLineAlignment(AssDialogue *diag) { return an; } -void VisualToolBase::GetLineBaseExtents(AssDialogue *diag, double &width, double &height, double &descent, double &extlead) { - width = 0.; - height = 0.; - descent = 0.; - extlead = 0.; +std::pair VisualToolBase::GetLineBaseExtents(AssDialogue *diag) { + double width = 0.; + double height = 0.; AssStyle style; if (AssStyle *basestyle = c->ass->GetStyle(diag->Style)) { @@ -554,25 +554,57 @@ void VisualToolBase::GetLineBaseExtents(AssDialogue *diag, double &width, double } auto blocks = diag->ParseTags(); - if (param_vec tag = find_tag(blocks, "\\fs")) - style.fontsize = tag->front().Get(style.fontsize); - if (param_vec tag = find_tag(blocks, "\\fn")) - style.font = tag->front().Get(style.font); + param_vec ptag = find_tag(blocks, "\\p"); - std::string text = diag->GetStrippedText(); - std::vector textlines; - boost::replace_all(text, "\\N", "\n"); - agi::Split(textlines, text, '\n'); - for (std::string line : textlines) { - double linewidth = 0; - double lineheight = 0; - if (!Automation4::CalculateTextExtents(&style, line, linewidth, lineheight, descent, extlead)) { - // meh... let's make some ballpark estimates - linewidth = style.fontsize * line.length(); - lineheight = style.fontsize; + if (ptag && ptag->front().Get(0)) { // A drawing + Spline spline; + spline.SetScale(ptag->front().Get(1)); + std::string drawing_text = join(blocks | agi::of_type() | boost::adaptors::transformed([&](AssDialogueBlock *d) { return d->GetText(); }), ""); + spline.DecodeFromAss(drawing_text); + + if (!spline.size()) + return std::make_pair(Vector2D(0, 0), Vector2D(0, 0)); + + float left = FLT_MAX; + float top = FLT_MAX; + float right = -FLT_MAX; + float bot = -FLT_MAX; + + for (SplineCurve curve : spline) { + for (Vector2D pt : curve.AnchorPoints()) { + left = std::min(left, pt.X()); + top = std::min(top, pt.Y()); + right = std::max(right, pt.X()); + bot = std::max(bot, pt.Y()); + } } - width = std::max(width, linewidth); - height += lineheight; + + return std::make_pair(Vector2D(left, top), Vector2D(right, bot)); + } else { + if (param_vec tag = find_tag(blocks, "\\fs")) + style.fontsize = tag->front().Get(style.fontsize); + if (param_vec tag = find_tag(blocks, "\\fn")) + style.font = tag->front().Get(style.font); + + std::string text = diag->GetStrippedText(); + std::vector textlines; + boost::replace_all(text, "\\N", "\n"); + agi::Split(textlines, text, '\n'); + for (std::string line : textlines) { + double linewidth = 0; + double lineheight = 0; + + double descent; + double extlead; + if (!Automation4::CalculateTextExtents(&style, line, linewidth, lineheight, descent, extlead)) { + // meh... let's make some ballpark estimates + linewidth = style.fontsize * line.length(); + lineheight = style.fontsize; + } + width = std::max(width, linewidth); + height += lineheight; + } + return std::make_pair(Vector2D(0, 0), Vector2D(width, height)); } } diff --git a/src/visual_tool.h b/src/visual_tool.h index 5e62920ae..c139c1733 100644 --- a/src/visual_tool.h +++ b/src/visual_tool.h @@ -133,11 +133,18 @@ protected: float GetLineFontSize(AssDialogue *diag); int GetLineAlignment(AssDialogue *diag); /// @brief Compute text extents of the given line without any formatting + /// @param diag The dialogue line + /// @return The top left and bottom right corners of the line's bounding box respectively. /// /// Formatting tags are stripped and \fs tags are respected, but \fscx and \fscy are kept as 100 even if /// they are different in the style. + /// For text the top left corner of the bounding box will always be at the origin, but this needn't be + /// the case for drawings. The width and height of the bounding box are the shifts used for text alignment. + /// + /// This function works for most common line formats, but can be inaccurate for more complex cases such as lines + /// containing both text and drawings. /// Returns a rough estimate when getting the precise extents fails - void GetLineBaseExtents(AssDialogue *diag, double &width, double &height, double &descent, double &extlead); + std::pair GetLineBaseExtents(AssDialogue *diag); void GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse); std::string GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse); diff --git a/src/visual_tool_perspective.cpp b/src/visual_tool_perspective.cpp index 4bf75ef74..deb0f7189 100644 --- a/src/visual_tool_perspective.cpp +++ b/src/visual_tool_perspective.cpp @@ -35,6 +35,8 @@ #include #include +#include + #include #include @@ -332,7 +334,7 @@ void VisualToolPerspective::Draw() { gl.SetRotation(angle_x, angle_y, angle_z); gl.SetScale(fsc); gl.SetShear(fax, fay); - Vector2D glScale = textheight * Vector2D(1, 1) / spacing / 4; + Vector2D glScale = (bbox.second.Y() - bbox.first.Y()) * Vector2D(1, 1) / spacing / 4; gl.SetScale(100 * glScale); // Draw grid @@ -678,17 +680,18 @@ bool VisualToolPerspective::InnerToText() { float quadwidth = ab.Len(); float quadheight = abs(ad.Y()); - float scalex = quadwidth / textwidth; - float scaley = quadheight / textheight; + float scalex = quadwidth / std::max(bbox.second.X() - bbox.first.X(), 1.0f); + float scaley = quadheight / std::max(bbox.second.Y() - bbox.first.Y(), 1.0f); + Vector2D scale = Vector2D(scalex, scaley); float shiftv = align <= 3 ? 1 : (align <= 6 ? 0.5 : 0); float shifth = align % 3 == 0 ? 1 : (align % 3 == 2 ? 0.5 : 0); - pos = org + r[0].XY() + Vector2D(quadwidth * shifth, quadheight * shiftv); + pos = org + r[0].XY() - bbox.first * scale + Vector2D(quadwidth * shifth, quadheight * shiftv); angle_x = rotx * rad2deg; angle_y = -roty * rad2deg; angle_z = -rotz * rad2deg; Vector2D oldfsc = fsc; - fsc = 100 * Vector2D(scalex, scaley); + fsc = 100 * scale; fax = rawfax * scaley / scalex; fay = 0; @@ -779,40 +782,39 @@ void VisualToolPerspective::TextToPersp() { align = GetLineAlignment(active_line); - double descend, extlead; - GetLineBaseExtents(active_line, textwidth, textheight, descend, extlead); - textwidth = std::max(textwidth, 1.); - textheight = std::max(textheight, 1.); - double textleft, texttop = 0.; + bbox = GetLineBaseExtents(active_line); + float textwidth = std::max(bbox.second.X() - bbox.first.X(), 1.f); + float textheight = std::max(bbox.second.Y() - bbox.first.Y(), 1.f); + double shiftx = 0., shifty = 0.; switch ((align - 1) % 3) { case 1: - textleft = -textwidth / 2; + shiftx = -textwidth / 2; break; case 2: - textleft = -textwidth; + shiftx = -textwidth; break; default: break; } switch ((align - 1) / 3) { case 0: - texttop = -textheight; + shifty = -textheight; break; case 1: - texttop = -textheight / 2; + shifty = -textheight / 2; break; default: break; } - std::vector textrect = MakeRect(Vector2D(0, 0), Vector2D(textwidth, textheight)); + std::vector textrect = MakeRect(bbox.first, bbox.second); for (int i = 0; i < 4; i++) { Vector2D p = textrect[i]; // Apply \fax and \fay p = Vector2D(p.X() + p.Y() * fax, p.X() * fay + p.Y()); // Translate to alignment point - p = p + Vector2D(textleft, texttop); + p = p + Vector2D(shiftx, shifty); // Apply scaling p = Vector2D(p.X() * fsc.X() / 100., p.Y() * fsc.Y() / 100.); // Translate relative to origin diff --git a/src/visual_tool_perspective.h b/src/visual_tool_perspective.h index 82647a7b6..3834f12cb 100644 --- a/src/visual_tool_perspective.h +++ b/src/visual_tool_perspective.h @@ -52,7 +52,7 @@ public: class VisualToolPerspective final : public VisualTool { wxToolBar *toolBar = nullptr; /// The subtoolbar - int settings = 0; + int settings = 0; agi::OptionValue* optOuter; agi::OptionValue* optOuterLocked; @@ -69,8 +69,9 @@ class VisualToolPerspective final : public VisualTool bbox; Vector2D fsc; diff --git a/src/visual_tool_vector_clip.cpp b/src/visual_tool_vector_clip.cpp index d90c4eec9..5397001ec 100644 --- a/src/visual_tool_vector_clip.cpp +++ b/src/visual_tool_vector_clip.cpp @@ -34,7 +34,7 @@ int BUTTON_ID_BASE = 1300; VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent, agi::Context *context) : VisualTool(parent, context) -, spline(*this) +, spline(this) { }