/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "stdafx.h" #include "EditorCommon.h" #include "ViewportPivot.h" #include namespace ViewportHelpers { bool IsControlledByLayout(const AZ::Entity* element) { AZ::Entity* parentElement = EntityHelpers::GetParentElement(element); bool isControlledByParent = false; if (parentElement) { EBUS_EVENT_ID_RESULT(isControlledByParent, parentElement->GetId(), UiLayoutBus, IsControllingChild, element->GetId()); } return isControlledByParent; } bool IsHorizontallyFit(const AZ::Entity* element) { bool isHorizontallyFit = false; EBUS_EVENT_ID_RESULT(isHorizontallyFit, element->GetId(), UiLayoutFitterBus, GetHorizontalFit); return isHorizontallyFit; } bool IsVerticallyFit(const AZ::Entity* element) { bool isVerticallyFit = false; EBUS_EVENT_ID_RESULT(isVerticallyFit, element->GetId(), UiLayoutFitterBus, GetVerticalFit); return isVerticallyFit; } float GetPerpendicularAngle(float angle) { return fmodf(angle + 90.0f, 180.0f); } Qt::CursorShape GetSizingCursor(float angle) { Qt::CursorShape sizingCursors[] { Qt::SizeVerCursor, Qt::SizeBDiagCursor, Qt::SizeHorCursor, Qt::SizeFDiagCursor }; // The expected angle range is [-180, +180]. Each cursor covers two 45 degree // sections that are opposite each other on that circle. float section = 45.0f; // Starting at -180, the transitions are Vert, BDiag (/), Horiz, FDiag (\) and // continues in that same pattern for 0 to 180 (V,B,H,F), so the full pattern is // V B H F V B H F which can be done with mod 4. However, the modulus operator // doesn't handle negative values the way that we want, so we need to get the angle // in the range [0, 360]. For our purposes, it's okay if the angle flips to its opposite. angle += 180.0f; // We shift the cursor sections by 45/2 degrees, so that the center of each cursor // section is directly on a multiple of 45 degrees (0, 45, 90, etc.). angle += 0.5f * section; // Compute which section this angle is in. int index = ((int)(angle / section)) % 4; // Return the appropriate sizing cursor. return sizingCursors[index]; } void TransformIconScale(AZ::Vector2& iconSize, const AZ::Matrix4x4& transform) { // Make two unit vectors in untransformed space AZ::Vector3 widthVec(1, 0, 0); AZ::Vector3 heightVec(0, 1, 0); // Convert these two unit vectors into the transformed space widthVec = transform.Multiply3x3(widthVec); heightVec = transform.Multiply3x3(heightVec); // Divide the iconSize (for untransformed space) by the scale that each unit vector received iconSize.SetX(iconSize.GetX() / AZ::Vector2(widthVec.GetX(), widthVec.GetY()).GetLength()); iconSize.SetY(iconSize.GetY() / AZ::Vector2(heightVec.GetX(), heightVec.GetY()).GetLength()); } AZ::Vector2 ComputeAnchorPoint(AZ::Vector2 rectTopLeft, AZ::Vector2 rectSize, float anchorX, float anchorY) { rectSize.SetX(rectSize.GetX() * anchorX); rectSize.SetY(rectSize.GetY() * anchorY); return rectTopLeft + rectSize; } bool IsPointInIconRect(AZ::Vector2 point, AZ::Vector2 iconCenter, AZ::Vector2 iconSize, float leftPart, float rightPart, float topPart, float bottomPart) { float left = iconCenter.GetX() + leftPart * iconSize.GetX(); float right = iconCenter.GetX() + rightPart * iconSize.GetX(); float top = iconCenter.GetY() + topPart * iconSize.GetY(); float bottom = iconCenter.GetY() + bottomPart * iconSize.GetY(); return (left < point.GetX() && point.GetX() < right && top < point.GetY() && point.GetY() < bottom); } void GetHorizTargetPoints(const UiTransformInterface::RectPoints& elemRect, float y, AZ::Vector2& leftTarget, AZ::Vector2& rightTarget) { leftTarget = (elemRect.TopLeft() + elemRect.BottomLeft()) * 0.5f; rightTarget = (elemRect.TopRight() + elemRect.BottomRight()) * 0.5f; if (y >= elemRect.TopLeft().GetY()) { if (y <= elemRect.BottomLeft().GetY()) { leftTarget.SetY(y); rightTarget.SetY(leftTarget.GetY()); } else { leftTarget.SetY(elemRect.BottomLeft().GetY()); rightTarget.SetY(leftTarget.GetY()); } } else { leftTarget.SetY(elemRect.TopLeft().GetY()); rightTarget.SetY(leftTarget.GetY()); } } void GetVerticalTargetPoints(const UiTransformInterface::RectPoints& elemRect, float x, AZ::Vector2& topTarget, AZ::Vector2& bottomTarget) { topTarget = (elemRect.TopLeft() + elemRect.TopRight()) * 0.5f; bottomTarget = (elemRect.BottomLeft() + elemRect.BottomRight()) * 0.5f; if (x >= elemRect.TopLeft().GetX()) { if (x <= elemRect.TopRight().GetX()) { topTarget.SetX(x); bottomTarget.SetX(topTarget.GetX()); } else { topTarget.SetX(elemRect.TopRight().GetX()); bottomTarget.SetX(topTarget.GetX()); } } else { topTarget.SetX(elemRect.TopLeft().GetX()); bottomTarget.SetX(topTarget.GetX()); } } UiTransform2dInterface::Offsets MoveGrabbedEdges(const UiTransform2dInterface::Offsets& offset, const ViewportHelpers::ElementEdges& grabbedEdges, const AZ::Vector2& v) { UiTransform2dInterface::Offsets outOffset(offset); outOffset.m_left += (grabbedEdges.m_left ? v.GetX() : 0.0f); outOffset.m_right += (grabbedEdges.m_right ? v.GetX() : 0.0f); outOffset.m_top += (grabbedEdges.m_top ? v.GetY() : 0.0f); outOffset.m_bottom += (grabbedEdges.m_bottom ? v.GetY() : 0.0f); return outOffset; } UiTransform2dInterface::Anchors MoveGrabbedAnchor(const UiTransform2dInterface::Anchors& anchor, const ViewportHelpers::SelectedAnchors& grabbedAnchors, bool keepTogetherHorizontally, bool keepTogetherVertically, const AZ::Vector2& v) { UiTransform2dInterface::Anchors outAnchor(anchor); outAnchor.m_left += (grabbedAnchors.m_left) ? v.GetX() : 0.0f; outAnchor.m_right += (grabbedAnchors.m_right) ? v.GetX() : 0.0f; outAnchor.m_top += (grabbedAnchors.m_top) ? v.GetY() : 0.0f; outAnchor.m_bottom += (grabbedAnchors.m_bottom) ? v.GetY() : 0.0f; if (keepTogetherHorizontally) { if (grabbedAnchors.m_left && !grabbedAnchors.m_right) { outAnchor.m_right = outAnchor.m_left; } else if (grabbedAnchors.m_right && !grabbedAnchors.m_left) { outAnchor.m_left = outAnchor.m_right; } } if (keepTogetherVertically) { if (grabbedAnchors.m_top && !grabbedAnchors.m_bottom) { outAnchor.m_bottom = outAnchor.m_top; } else if (grabbedAnchors.m_bottom && !grabbedAnchors.m_top) { outAnchor.m_top = outAnchor.m_bottom; } } // Clamp the anchors outAnchor.UnitClamp(); return outAnchor; } void MoveGrabbedEdges(UiTransformInterface::RectPoints& points, const ViewportHelpers::ElementEdges& grabbedEdges, const AZ::Vector2& topEdge, const AZ::Vector2& leftEdge) { if (grabbedEdges.m_left) { points.TopLeft() += topEdge; points.BottomLeft() += topEdge; } if (grabbedEdges.m_right) { points.TopRight() += topEdge; points.BottomRight() += topEdge; } if (grabbedEdges.m_top) { points.TopLeft() += leftEdge; points.TopRight() += leftEdge; } if (grabbedEdges.m_bottom) { points.BottomLeft() += leftEdge; points.BottomRight() += leftEdge; } } const char* InteractionModeToString(int mode) { switch (static_cast(mode)) { case ViewportInteraction::InteractionMode::SELECTION: return "Selection"; break; case ViewportInteraction::InteractionMode::MOVE: return "Move"; break; case ViewportInteraction::InteractionMode::ANCHOR: return "Anchor"; break; case ViewportInteraction::InteractionMode::ROTATE: return "Rotate"; break; case ViewportInteraction::InteractionMode::RESIZE: return "Resize"; break; default: AZ_Assert(false, "Invalid mode"); return "UNKNOWN"; break; } } const char* CoordinateSystemToString(int s) { switch (static_cast(s)) { case ViewportInteraction::CoordinateSystem::LOCAL: return "Local"; break; case ViewportInteraction::CoordinateSystem::VIEW: return "View"; break; default: AZ_Assert(false, "Invalid coordinate system enum value"); return "UNKNOWN"; break; } } const char* InteractionTypeToString(int type) { switch (static_cast(type)) { case ViewportInteraction::InteractionType::DIRECT: return "DIRECT"; break; case ViewportInteraction::InteractionType::TRANSFORM_GIZMO: return "TRANSFORM_GIZMO"; break; case ViewportInteraction::InteractionType::ANCHORS: return "ANCHORS"; break; case ViewportInteraction::InteractionType::PIVOT: return "PIVOT"; break; case ViewportInteraction::InteractionType::NONE: return "NONE"; break; default: AZ_Assert(false, "Invalid interaction type"); return "UNKNOWN"; break; } } void DrawRotationValue(const AZ::Entity* element, ViewportInteraction* viewportInteraction, const ViewportPivot* viewportPivot, Draw2dHelper& draw2d) { // Draw the rotation in degrees when the left mouse button is down on the rotation gizmo if ((viewportInteraction->GetInteractionType() == ViewportInteraction::InteractionType::TRANSFORM_GIZMO) && viewportInteraction->GetLeftButtonIsActive()) { float rotation = 0.0f; EBUS_EVENT_ID_RESULT(rotation, element->GetId(), UiTransformBus, GetZRotation); QString rotationString = QString("%1").number(rotation, 'f', 2); QChar degChar(0xB0); rotationString.append(degChar); AZ::Vector2 pivotPos; EBUS_EVENT_ID_RESULT(pivotPos, element->GetId(), UiTransformBus, GetViewportSpacePivot); AZ::Vector2 rotationStringPos(pivotPos.GetX(), pivotPos.GetY() - ((viewportPivot->GetSize().GetY() * 0.5f) + 4.0f)); draw2d.SetTextAlignment(IDraw2d::HAlign::Center, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(0.0f); draw2d.DrawText(rotationString.toUtf8().data(), rotationStringPos, 16.0f, 1.0f); } } void DrawCursorText(const AZStd::string& textLabel, Draw2dHelper& draw2d, const ViewportWidget* viewport) { const AZ::Vector2 textLabelOffset(10.0f, -10.0f); QPoint viewportCursorPos = viewport->mapFromGlobal(QCursor::pos()); AZ::Vector2 textPos = AZ::Vector2(aznumeric_cast(viewportCursorPos.x()), aznumeric_cast(viewportCursorPos.y())) + textLabelOffset; draw2d.SetTextAlignment(IDraw2d::HAlign::Left, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(0.0f); draw2d.DrawText(textLabel.c_str(), textPos, 16.0f, 1.0f); } } // namespace ViewportHelpers