ViewportHelpers.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "EditorCommon.h"
  9. #include "ViewportPivot.h"
  10. #include <LyShine/Bus/UiLayoutFitterBus.h>
  11. namespace ViewportHelpers
  12. {
  13. bool IsControlledByLayout(const AZ::Entity* element)
  14. {
  15. AZ::Entity* parentElement = EntityHelpers::GetParentElement(element);
  16. bool isControlledByParent = false;
  17. if (parentElement)
  18. {
  19. UiLayoutBus::EventResult(
  20. isControlledByParent, parentElement->GetId(), &UiLayoutBus::Events::IsControllingChild, element->GetId());
  21. }
  22. return isControlledByParent;
  23. }
  24. float GetDpiScaledSize(float size)
  25. {
  26. return size * ViewportIcon::GetDpiScaleFactor();
  27. }
  28. bool IsHorizontallyFit(const AZ::Entity* element)
  29. {
  30. bool isHorizontallyFit = false;
  31. UiLayoutFitterBus::EventResult(isHorizontallyFit, element->GetId(), &UiLayoutFitterBus::Events::GetHorizontalFit);
  32. return isHorizontallyFit;
  33. }
  34. bool IsVerticallyFit(const AZ::Entity* element)
  35. {
  36. bool isVerticallyFit = false;
  37. UiLayoutFitterBus::EventResult(isVerticallyFit, element->GetId(), &UiLayoutFitterBus::Events::GetVerticalFit);
  38. return isVerticallyFit;
  39. }
  40. float GetPerpendicularAngle(float angle)
  41. {
  42. return fmodf(angle + 90.0f, 180.0f);
  43. }
  44. Qt::CursorShape GetSizingCursor(float angle)
  45. {
  46. Qt::CursorShape sizingCursors[] {
  47. Qt::SizeVerCursor, Qt::SizeBDiagCursor, Qt::SizeHorCursor, Qt::SizeFDiagCursor
  48. };
  49. // The expected angle range is [-180, +180]. Each cursor covers two 45 degree
  50. // sections that are opposite each other on that circle.
  51. float section = 45.0f;
  52. // Starting at -180, the transitions are Vert, BDiag (/), Horiz, FDiag (\) and
  53. // continues in that same pattern for 0 to 180 (V,B,H,F), so the full pattern is
  54. // V B H F V B H F which can be done with mod 4. However, the modulus operator
  55. // doesn't handle negative values the way that we want, so we need to get the angle
  56. // in the range [0, 360]. For our purposes, it's okay if the angle flips to its opposite.
  57. angle += 180.0f;
  58. // We shift the cursor sections by 45/2 degrees, so that the center of each cursor
  59. // section is directly on a multiple of 45 degrees (0, 45, 90, etc.).
  60. angle += 0.5f * section;
  61. // Compute which section this angle is in.
  62. int index = ((int)(angle / section)) % 4;
  63. // Return the appropriate sizing cursor.
  64. return sizingCursors[index];
  65. }
  66. void TransformIconScale(AZ::Vector2& iconSize, const AZ::Matrix4x4& transform)
  67. {
  68. // Make two unit vectors in untransformed space
  69. AZ::Vector3 widthVec(1, 0, 0);
  70. AZ::Vector3 heightVec(0, 1, 0);
  71. // Convert these two unit vectors into the transformed space
  72. widthVec = transform.Multiply3x3(widthVec);
  73. heightVec = transform.Multiply3x3(heightVec);
  74. // Divide the iconSize (for untransformed space) by the scale that each unit vector received
  75. iconSize.SetX(iconSize.GetX() / AZ::Vector2(widthVec.GetX(), widthVec.GetY()).GetLength());
  76. iconSize.SetY(iconSize.GetY() / AZ::Vector2(heightVec.GetX(), heightVec.GetY()).GetLength());
  77. }
  78. AZ::Vector2 ComputeAnchorPoint(AZ::Vector2 rectTopLeft, AZ::Vector2 rectSize, float anchorX, float anchorY)
  79. {
  80. rectSize.SetX(rectSize.GetX() * anchorX);
  81. rectSize.SetY(rectSize.GetY() * anchorY);
  82. return rectTopLeft + rectSize;
  83. }
  84. bool IsPointInIconRect(AZ::Vector2 point, AZ::Vector2 iconCenter, AZ::Vector2 iconSize, float leftPart, float rightPart, float topPart, float bottomPart)
  85. {
  86. float left = iconCenter.GetX() + leftPart * iconSize.GetX();
  87. float right = iconCenter.GetX() + rightPart * iconSize.GetX();
  88. float top = iconCenter.GetY() + topPart * iconSize.GetY();
  89. float bottom = iconCenter.GetY() + bottomPart * iconSize.GetY();
  90. return (left < point.GetX() && point.GetX() < right &&
  91. top < point.GetY() && point.GetY() < bottom);
  92. }
  93. void GetHorizTargetPoints(const UiTransformInterface::RectPoints& elemRect, float y, AZ::Vector2& leftTarget, AZ::Vector2& rightTarget)
  94. {
  95. leftTarget = (elemRect.TopLeft() + elemRect.BottomLeft()) * 0.5f;
  96. rightTarget = (elemRect.TopRight() + elemRect.BottomRight()) * 0.5f;
  97. if (y >= elemRect.TopLeft().GetY())
  98. {
  99. if (y <= elemRect.BottomLeft().GetY())
  100. {
  101. leftTarget.SetY(y);
  102. rightTarget.SetY(leftTarget.GetY());
  103. }
  104. else
  105. {
  106. leftTarget.SetY(elemRect.BottomLeft().GetY());
  107. rightTarget.SetY(leftTarget.GetY());
  108. }
  109. }
  110. else
  111. {
  112. leftTarget.SetY(elemRect.TopLeft().GetY());
  113. rightTarget.SetY(leftTarget.GetY());
  114. }
  115. }
  116. void GetVerticalTargetPoints(const UiTransformInterface::RectPoints& elemRect, float x, AZ::Vector2& topTarget, AZ::Vector2& bottomTarget)
  117. {
  118. topTarget = (elemRect.TopLeft() + elemRect.TopRight()) * 0.5f;
  119. bottomTarget = (elemRect.BottomLeft() + elemRect.BottomRight()) * 0.5f;
  120. if (x >= elemRect.TopLeft().GetX())
  121. {
  122. if (x <= elemRect.TopRight().GetX())
  123. {
  124. topTarget.SetX(x);
  125. bottomTarget.SetX(topTarget.GetX());
  126. }
  127. else
  128. {
  129. topTarget.SetX(elemRect.TopRight().GetX());
  130. bottomTarget.SetX(topTarget.GetX());
  131. }
  132. }
  133. else
  134. {
  135. topTarget.SetX(elemRect.TopLeft().GetX());
  136. bottomTarget.SetX(topTarget.GetX());
  137. }
  138. }
  139. UiTransform2dInterface::Offsets MoveGrabbedEdges(const UiTransform2dInterface::Offsets& offset,
  140. const ViewportHelpers::ElementEdges& grabbedEdges,
  141. const AZ::Vector2& v)
  142. {
  143. UiTransform2dInterface::Offsets outOffset(offset);
  144. outOffset.m_left += (grabbedEdges.m_left ? v.GetX() : 0.0f);
  145. outOffset.m_right += (grabbedEdges.m_right ? v.GetX() : 0.0f);
  146. outOffset.m_top += (grabbedEdges.m_top ? v.GetY() : 0.0f);
  147. outOffset.m_bottom += (grabbedEdges.m_bottom ? v.GetY() : 0.0f);
  148. return outOffset;
  149. }
  150. UiTransform2dInterface::Anchors MoveGrabbedAnchor(const UiTransform2dInterface::Anchors& anchor,
  151. const ViewportHelpers::SelectedAnchors& grabbedAnchors, bool keepTogetherHorizontally,
  152. bool keepTogetherVertically, const AZ::Vector2& v)
  153. {
  154. UiTransform2dInterface::Anchors outAnchor(anchor);
  155. outAnchor.m_left += (grabbedAnchors.m_left) ? v.GetX() : 0.0f;
  156. outAnchor.m_right += (grabbedAnchors.m_right) ? v.GetX() : 0.0f;
  157. outAnchor.m_top += (grabbedAnchors.m_top) ? v.GetY() : 0.0f;
  158. outAnchor.m_bottom += (grabbedAnchors.m_bottom) ? v.GetY() : 0.0f;
  159. if (keepTogetherHorizontally)
  160. {
  161. if (grabbedAnchors.m_left && !grabbedAnchors.m_right)
  162. {
  163. outAnchor.m_right = outAnchor.m_left;
  164. }
  165. else if (grabbedAnchors.m_right && !grabbedAnchors.m_left)
  166. {
  167. outAnchor.m_left = outAnchor.m_right;
  168. }
  169. }
  170. if (keepTogetherVertically)
  171. {
  172. if (grabbedAnchors.m_top && !grabbedAnchors.m_bottom)
  173. {
  174. outAnchor.m_bottom = outAnchor.m_top;
  175. }
  176. else if (grabbedAnchors.m_bottom && !grabbedAnchors.m_top)
  177. {
  178. outAnchor.m_top = outAnchor.m_bottom;
  179. }
  180. }
  181. // Clamp the anchors
  182. outAnchor.UnitClamp();
  183. return outAnchor;
  184. }
  185. void MoveGrabbedEdges(UiTransformInterface::RectPoints& points,
  186. const ViewportHelpers::ElementEdges& grabbedEdges,
  187. const AZ::Vector2& topEdge,
  188. const AZ::Vector2& leftEdge)
  189. {
  190. if (grabbedEdges.m_left)
  191. {
  192. points.TopLeft() += topEdge;
  193. points.BottomLeft() += topEdge;
  194. }
  195. if (grabbedEdges.m_right)
  196. {
  197. points.TopRight() += topEdge;
  198. points.BottomRight() += topEdge;
  199. }
  200. if (grabbedEdges.m_top)
  201. {
  202. points.TopLeft() += leftEdge;
  203. points.TopRight() += leftEdge;
  204. }
  205. if (grabbedEdges.m_bottom)
  206. {
  207. points.BottomLeft() += leftEdge;
  208. points.BottomRight() += leftEdge;
  209. }
  210. }
  211. const char* InteractionModeToString(int mode)
  212. {
  213. switch (static_cast<ViewportInteraction::InteractionMode>(mode))
  214. {
  215. case ViewportInteraction::InteractionMode::SELECTION:
  216. return "Selection";
  217. break;
  218. case ViewportInteraction::InteractionMode::MOVE:
  219. return "Move";
  220. break;
  221. case ViewportInteraction::InteractionMode::ANCHOR:
  222. return "Anchor";
  223. break;
  224. case ViewportInteraction::InteractionMode::ROTATE:
  225. return "Rotate";
  226. break;
  227. case ViewportInteraction::InteractionMode::RESIZE:
  228. return "Resize";
  229. break;
  230. default:
  231. AZ_Assert(false, "Invalid mode");
  232. return "UNKNOWN";
  233. break;
  234. }
  235. }
  236. const char* CoordinateSystemToString(int s)
  237. {
  238. switch (static_cast<ViewportInteraction::CoordinateSystem>(s))
  239. {
  240. case ViewportInteraction::CoordinateSystem::LOCAL:
  241. return "Local";
  242. break;
  243. case ViewportInteraction::CoordinateSystem::VIEW:
  244. return "View";
  245. break;
  246. default:
  247. AZ_Assert(false, "Invalid coordinate system enum value");
  248. return "UNKNOWN";
  249. break;
  250. }
  251. }
  252. const char* InteractionTypeToString(int type)
  253. {
  254. switch (static_cast<ViewportInteraction::InteractionType>(type))
  255. {
  256. case ViewportInteraction::InteractionType::DIRECT:
  257. return "DIRECT";
  258. break;
  259. case ViewportInteraction::InteractionType::TRANSFORM_GIZMO:
  260. return "TRANSFORM_GIZMO";
  261. break;
  262. case ViewportInteraction::InteractionType::ANCHORS:
  263. return "ANCHORS";
  264. break;
  265. case ViewportInteraction::InteractionType::PIVOT:
  266. return "PIVOT";
  267. break;
  268. case ViewportInteraction::InteractionType::NONE:
  269. return "NONE";
  270. break;
  271. default:
  272. AZ_Assert(false, "Invalid interaction type");
  273. return "UNKNOWN";
  274. break;
  275. }
  276. }
  277. void DrawRotationValue(const AZ::Entity* element,
  278. ViewportInteraction* viewportInteraction,
  279. const ViewportPivot* viewportPivot,
  280. Draw2dHelper& draw2d)
  281. {
  282. // Draw the rotation in degrees when the left mouse button is down on the rotation gizmo
  283. if ((viewportInteraction->GetInteractionType() == ViewportInteraction::InteractionType::TRANSFORM_GIZMO)
  284. && viewportInteraction->GetLeftButtonIsActive())
  285. {
  286. float rotation = 0.0f;
  287. UiTransformBus::EventResult(rotation, element->GetId(), &UiTransformBus::Events::GetZRotation);
  288. QString rotationString = QString("%1").number(rotation, 'f', 2);
  289. QChar degChar(0xB0);
  290. rotationString.append(degChar);
  291. AZ::Vector2 pivotPos;
  292. UiTransformBus::EventResult(pivotPos, element->GetId(), &UiTransformBus::Events::GetViewportSpacePivot);
  293. float offset = (viewportPivot->GetSize().GetY() * 0.5f) + (GetDpiScaledSize(4.0f));
  294. AZ::Vector2 rotationStringPos(pivotPos.GetX(), pivotPos.GetY() - offset);
  295. draw2d.SetTextAlignment(IDraw2d::HAlign::Center, IDraw2d::VAlign::Bottom);
  296. draw2d.SetTextRotation(0.0f);
  297. draw2d.DrawText(rotationString.toUtf8().data(), rotationStringPos, 8.0f, 1.0f);
  298. }
  299. }
  300. void DrawCursorText(const AZStd::string& textLabel,
  301. Draw2dHelper& draw2d,
  302. const ViewportWidget* viewport)
  303. {
  304. const AZ::Vector2 textLabelOffset(10.0f, -10.0f);
  305. QPoint viewportCursorPos = viewport->mapFromGlobal(QCursor::pos());
  306. AZ::Vector2 textPos = AZ::Vector2(aznumeric_cast<float>(viewportCursorPos.x()), aznumeric_cast<float>(viewportCursorPos.y())) + textLabelOffset;
  307. float dpiScale = viewport->WidgetToViewportFactor();
  308. textPos *= dpiScale;
  309. draw2d.SetTextAlignment(IDraw2d::HAlign::Left, IDraw2d::VAlign::Bottom);
  310. draw2d.SetTextRotation(0.0f);
  311. draw2d.DrawText(textLabel.c_str(), textPos, 8.0f, 1.0f);
  312. }
  313. } // namespace ViewportHelpers