mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-12 19:18:20 +00:00
Added quad alignment/snapping to other quads/points
This commit is contained in:
parent
a41da8182b
commit
5dbd5a1f49
|
@ -101,6 +101,8 @@ MACRO_CONFIG_INT(EdSmoothZoomTime, ed_smooth_zoom_time, 250, 0, 5000, CFGFLAG_CL
|
||||||
MACRO_CONFIG_INT(EdLimitMaxZoomLevel, ed_limit_max_zoom_level, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Specifies, if zooming in the editor should be limited or not (0 = no limit)")
|
MACRO_CONFIG_INT(EdLimitMaxZoomLevel, ed_limit_max_zoom_level, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Specifies, if zooming in the editor should be limited or not (0 = no limit)")
|
||||||
MACRO_CONFIG_INT(EdZoomTarget, ed_zoom_target, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Zoom to the current mouse target")
|
MACRO_CONFIG_INT(EdZoomTarget, ed_zoom_target, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Zoom to the current mouse target")
|
||||||
MACRO_CONFIG_INT(EdShowkeys, ed_showkeys, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show pressed keys")
|
MACRO_CONFIG_INT(EdShowkeys, ed_showkeys, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show pressed keys")
|
||||||
|
MACRO_CONFIG_INT(EdAlignQuads, ed_align_quads, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Enable/disable quad alignment. When enabled, red lines appear to show how quad/points are aligned and snapped to other quads/points when moving them")
|
||||||
|
MACRO_CONFIG_INT(EdShowQuadsRect, ed_show_quads_rect, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show the bounds of the selected quad. In case of multiple quads, it shows the bounds of the englobing rect. Can be helpful when aligning a group of quads")
|
||||||
|
|
||||||
MACRO_CONFIG_INT(ClShowWelcome, cl_show_welcome, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show welcome message indicating the first launch of the client")
|
MACRO_CONFIG_INT(ClShowWelcome, cl_show_welcome, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show welcome message indicating the first launch of the client")
|
||||||
MACRO_CONFIG_INT(ClMotdTime, cl_motd_time, 10, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "How long to show the server message of the day")
|
MACRO_CONFIG_INT(ClMotdTime, cl_motd_time, 10, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "How long to show the server message of the day")
|
||||||
|
|
|
@ -1536,9 +1536,8 @@ void CEditor::PreparePointDrag(const std::shared_ptr<CLayerQuads> &pLayer, CQuad
|
||||||
|
|
||||||
void CEditor::DoPointDrag(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int PointIndex, int OffsetX, int OffsetY)
|
void CEditor::DoPointDrag(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int PointIndex, int OffsetX, int OffsetY)
|
||||||
{
|
{
|
||||||
EAxis Axis = GetDragAxis(OffsetX, OffsetY);
|
pQuad->m_aPoints[PointIndex].x = m_QuadDragOriginalPoints[QuadIndex][PointIndex].x + OffsetX;
|
||||||
pQuad->m_aPoints[PointIndex].x = m_QuadDragOriginalPoints[QuadIndex][PointIndex].x + ((Axis == EAxis::AXIS_NONE || Axis == EAxis::AXIS_X) ? OffsetX : 0);
|
pQuad->m_aPoints[PointIndex].y = m_QuadDragOriginalPoints[QuadIndex][PointIndex].y + OffsetY;
|
||||||
pQuad->m_aPoints[PointIndex].y = m_QuadDragOriginalPoints[QuadIndex][PointIndex].y + ((Axis == EAxis::AXIS_NONE || Axis == EAxis::AXIS_Y) ? OffsetY : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CEditor::EAxis CEditor::GetDragAxis(int OffsetX, int OffsetY)
|
CEditor::EAxis CEditor::GetDragAxis(int OffsetX, int OffsetY)
|
||||||
|
@ -1552,7 +1551,7 @@ CEditor::EAxis CEditor::GetDragAxis(int OffsetX, int OffsetY)
|
||||||
return EAxis::AXIS_NONE;
|
return EAxis::AXIS_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEditor::DrawAxis(EAxis Axis, CPoint &Point)
|
void CEditor::DrawAxis(EAxis Axis, CPoint &OriginalPoint, CPoint &Point)
|
||||||
{
|
{
|
||||||
if(Axis == EAxis::AXIS_NONE)
|
if(Axis == EAxis::AXIS_NONE)
|
||||||
return;
|
return;
|
||||||
|
@ -1560,20 +1559,431 @@ void CEditor::DrawAxis(EAxis Axis, CPoint &Point)
|
||||||
Graphics()->SetColor(1, 0, 0.1f, 1);
|
Graphics()->SetColor(1, 0, 0.1f, 1);
|
||||||
if(Axis == EAxis::AXIS_X)
|
if(Axis == EAxis::AXIS_X)
|
||||||
{
|
{
|
||||||
IGraphics::CQuadItem QuadItem(0, fx2f(Point.y), Graphics()->ScreenWidth() * m_MouseWScale, 1.0f * m_MouseWScale);
|
IGraphics::CQuadItem Line(fx2f(OriginalPoint.x + Point.x) / 2.0f, fx2f(OriginalPoint.y), fx2f(Point.x - OriginalPoint.x), 1.0f * m_MouseWScale);
|
||||||
Graphics()->QuadsDraw(&QuadItem, 1);
|
Graphics()->QuadsDraw(&Line, 1);
|
||||||
}
|
}
|
||||||
else if(Axis == EAxis::AXIS_Y)
|
else if(Axis == EAxis::AXIS_Y)
|
||||||
{
|
{
|
||||||
IGraphics::CQuadItem QuadItem(fx2f(Point.x), 0, 1.0f * m_MouseWScale, Graphics()->ScreenHeight() * m_MouseWScale);
|
IGraphics::CQuadItem Line(fx2f(OriginalPoint.x), fx2f(OriginalPoint.y + Point.y) / 2.0f, 1.0f * m_MouseWScale, fx2f(Point.y - OriginalPoint.y));
|
||||||
Graphics()->QuadsDraw(&QuadItem, 1);
|
Graphics()->QuadsDraw(&Line, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw ghost of original point
|
// Draw ghost of original point
|
||||||
IGraphics::CQuadItem QuadItem(fx2f(Point.x), fx2f(Point.y), 5.0f * m_MouseWScale, 5.0f * m_MouseWScale);
|
IGraphics::CQuadItem QuadItem(fx2f(OriginalPoint.x), fx2f(OriginalPoint.y), 5.0f * m_MouseWScale, 5.0f * m_MouseWScale);
|
||||||
Graphics()->QuadsDraw(&QuadItem, 1);
|
Graphics()->QuadsDraw(&QuadItem, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CEditor::ComputePointAlignments(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int PointIndex, int OffsetX, int OffsetY, std::vector<SAlignmentInfo> &vAlignments, bool Append) const
|
||||||
|
{
|
||||||
|
if(!Append)
|
||||||
|
vAlignments.clear();
|
||||||
|
if(!g_Config.m_EdAlignQuads)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Perform computation from the original position of this point
|
||||||
|
int Threshold = f2fx(maximum(10.0f, 10.0f * m_MouseWScale));
|
||||||
|
CPoint OrigPoint = m_QuadDragOriginalPoints.at(QuadIndex)[PointIndex];
|
||||||
|
// Get the "current" point by applying the offset
|
||||||
|
CPoint Point = OrigPoint + ivec2(OffsetX, OffsetY);
|
||||||
|
|
||||||
|
// Save smallest diff on both axis to only keep closest alignments
|
||||||
|
int SmallestDiffX = Threshold + 1, SmallestDiffY = Threshold + 1;
|
||||||
|
// Store both axis alignments in separate vectors
|
||||||
|
std::vector<SAlignmentInfo> vAlignmentsX, vAlignmentsY;
|
||||||
|
|
||||||
|
// Check if we can align/snap to a specific point
|
||||||
|
auto &&CheckAlignment = [&](CPoint *pQuadPoint) {
|
||||||
|
int DX = pQuadPoint->x - Point.x;
|
||||||
|
int DY = pQuadPoint->y - Point.y;
|
||||||
|
int DiffX = absolute(DX);
|
||||||
|
int DiffY = absolute(DY);
|
||||||
|
|
||||||
|
// Check the X axis
|
||||||
|
if(DiffX <= Threshold)
|
||||||
|
{
|
||||||
|
// Only store alignments that have the smallest difference
|
||||||
|
if(DiffX < SmallestDiffX)
|
||||||
|
{
|
||||||
|
vAlignmentsX.clear();
|
||||||
|
SmallestDiffX = DiffX;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can have multiple alignments having the same difference/distance
|
||||||
|
if(DiffX == SmallestDiffX)
|
||||||
|
{
|
||||||
|
vAlignmentsX.push_back(SAlignmentInfo{
|
||||||
|
*pQuadPoint, // Aligned point
|
||||||
|
{OrigPoint.y}, // Value that can change (which is not snapped), original position
|
||||||
|
EAxis::AXIS_Y, // The alignment axis
|
||||||
|
PointIndex, // The index of the point
|
||||||
|
DX,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(DiffY <= Threshold)
|
||||||
|
{
|
||||||
|
// Only store alignments that have the smallest difference
|
||||||
|
if(DiffY < SmallestDiffY)
|
||||||
|
{
|
||||||
|
vAlignmentsY.clear();
|
||||||
|
SmallestDiffY = DiffY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(DiffY == SmallestDiffY)
|
||||||
|
{
|
||||||
|
vAlignmentsY.push_back(SAlignmentInfo{
|
||||||
|
*pQuadPoint,
|
||||||
|
{OrigPoint.x},
|
||||||
|
EAxis::AXIS_X,
|
||||||
|
PointIndex,
|
||||||
|
DY,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate through all the quads of the current layer
|
||||||
|
// Check alignment with each point of the quad (corners & pivot)
|
||||||
|
// Compute an AABB (Axis Aligned Bounding Box) to get the center of the quad
|
||||||
|
// Check alignment with the center of the quad
|
||||||
|
for(size_t i = 0; i < pLayer->m_vQuads.size(); i++)
|
||||||
|
{
|
||||||
|
auto *pCurrentQuad = &pLayer->m_vQuads[i];
|
||||||
|
CPoint Min = pCurrentQuad->m_aPoints[0];
|
||||||
|
CPoint Max = pCurrentQuad->m_aPoints[0];
|
||||||
|
|
||||||
|
for(int v = 0; v < 5; v++)
|
||||||
|
{
|
||||||
|
CPoint *pQuadPoint = &pCurrentQuad->m_aPoints[v];
|
||||||
|
|
||||||
|
if(v != 4)
|
||||||
|
{ // Don't use pivot to compute AABB
|
||||||
|
if(pQuadPoint->x < Min.x)
|
||||||
|
Min.x = pQuadPoint->x;
|
||||||
|
if(pQuadPoint->y < Min.y)
|
||||||
|
Min.y = pQuadPoint->y;
|
||||||
|
if(pQuadPoint->x > Max.x)
|
||||||
|
Max.x = pQuadPoint->x;
|
||||||
|
if(pQuadPoint->y > Max.y)
|
||||||
|
Max.y = pQuadPoint->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't check alignment with current point
|
||||||
|
if(pQuadPoint == &pQuad->m_aPoints[PointIndex])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Don't check alignment with other selected points
|
||||||
|
bool IsCurrentPointSelected = IsQuadSelected(i) && (IsQuadCornerSelected(v) || (v == PointIndex && PointIndex == 4));
|
||||||
|
if(IsCurrentPointSelected)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CheckAlignment(pQuadPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
CPoint Center = (Min + Max) / 2.0f;
|
||||||
|
CheckAlignment(&Center);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally concatenate both alignment vectors into the output
|
||||||
|
vAlignments.reserve(vAlignmentsX.size() + vAlignmentsY.size());
|
||||||
|
vAlignments.insert(vAlignments.end(), vAlignmentsX.begin(), vAlignmentsX.end());
|
||||||
|
vAlignments.insert(vAlignments.end(), vAlignmentsY.begin(), vAlignmentsY.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEditor::ComputePointsAlignments(const std::shared_ptr<CLayerQuads> &pLayer, bool Pivot, int OffsetX, int OffsetY, std::vector<SAlignmentInfo> &vAlignments) const
|
||||||
|
{
|
||||||
|
// This method is used to compute alignments from selected points
|
||||||
|
// and only apply the closest alignment on X and Y to the offset.
|
||||||
|
|
||||||
|
vAlignments.clear();
|
||||||
|
std::vector<SAlignmentInfo> vAllAlignments;
|
||||||
|
|
||||||
|
for(int Selected : m_vSelectedQuads)
|
||||||
|
{
|
||||||
|
CQuad *pQuad = &pLayer->m_vQuads[Selected];
|
||||||
|
|
||||||
|
if(!Pivot)
|
||||||
|
{
|
||||||
|
for(int m = 0; m < 4; m++)
|
||||||
|
{
|
||||||
|
if(IsQuadPointSelected(Selected, m))
|
||||||
|
{
|
||||||
|
ComputePointAlignments(pLayer, pQuad, Selected, m, OffsetX, OffsetY, vAllAlignments, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ComputePointAlignments(pLayer, pQuad, Selected, 4, OffsetX, OffsetY, vAllAlignments, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SmallestDiffX, SmallestDiffY;
|
||||||
|
SmallestDiffX = SmallestDiffY = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
|
std::vector<SAlignmentInfo> vAlignmentsX, vAlignmentsY;
|
||||||
|
|
||||||
|
for(const auto &Alignment : vAllAlignments)
|
||||||
|
{
|
||||||
|
int AbsDiff = absolute(Alignment.m_Diff);
|
||||||
|
if(Alignment.m_Axis == EAxis::AXIS_X)
|
||||||
|
{
|
||||||
|
if(AbsDiff < SmallestDiffY)
|
||||||
|
{
|
||||||
|
SmallestDiffY = AbsDiff;
|
||||||
|
vAlignmentsY.clear();
|
||||||
|
}
|
||||||
|
if(AbsDiff == SmallestDiffY)
|
||||||
|
vAlignmentsY.emplace_back(Alignment);
|
||||||
|
}
|
||||||
|
else if(Alignment.m_Axis == EAxis::AXIS_Y)
|
||||||
|
{
|
||||||
|
if(AbsDiff < SmallestDiffX)
|
||||||
|
{
|
||||||
|
SmallestDiffX = AbsDiff;
|
||||||
|
vAlignmentsX.clear();
|
||||||
|
}
|
||||||
|
if(AbsDiff == SmallestDiffX)
|
||||||
|
vAlignmentsX.emplace_back(Alignment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vAlignments.reserve(vAlignmentsX.size() + vAlignmentsY.size());
|
||||||
|
vAlignments.insert(vAlignments.end(), vAlignmentsX.begin(), vAlignmentsX.end());
|
||||||
|
vAlignments.insert(vAlignments.end(), vAlignmentsY.begin(), vAlignmentsY.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEditor::ComputeAABBAlignments(const std::shared_ptr<CLayerQuads> &pLayer, const SAxisAlignedBoundingBox &AABB, int OffsetX, int OffsetY, std::vector<SAlignmentInfo> &vAlignments) const
|
||||||
|
{
|
||||||
|
vAlignments.clear();
|
||||||
|
if(!g_Config.m_EdAlignQuads)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// This method is a bit different than the point alignment in the way where instead of trying to aling 1 point to all quads,
|
||||||
|
// we try to align 5 points to all quads, these 5 points being 5 points of an AABB.
|
||||||
|
// Otherwise, the concept is the same, we use the original position of the AABB to make the computations.
|
||||||
|
int Threshold = f2fx(maximum(10.0f, 10.0f * m_MouseWScale));
|
||||||
|
int SmallestDiffX = Threshold + 1, SmallestDiffY = Threshold + 1;
|
||||||
|
std::vector<SAlignmentInfo> vAlignmentsX, vAlignmentsY;
|
||||||
|
|
||||||
|
auto &&CheckAlignment = [&](CPoint &Aligned, int Point) {
|
||||||
|
CPoint ToCheck = AABB.m_aPoints[Point] + ivec2(OffsetX, OffsetY);
|
||||||
|
int DX = Aligned.x - ToCheck.x;
|
||||||
|
int DY = Aligned.y - ToCheck.y;
|
||||||
|
int DiffX = absolute(DX);
|
||||||
|
int DiffY = absolute(DY);
|
||||||
|
|
||||||
|
if(DiffX <= Threshold)
|
||||||
|
{
|
||||||
|
if(DiffX < SmallestDiffX)
|
||||||
|
{
|
||||||
|
SmallestDiffX = DiffX;
|
||||||
|
vAlignmentsX.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(DiffX == SmallestDiffX)
|
||||||
|
{
|
||||||
|
vAlignmentsX.push_back(SAlignmentInfo{
|
||||||
|
Aligned,
|
||||||
|
{AABB.m_aPoints[Point].y},
|
||||||
|
EAxis::AXIS_Y,
|
||||||
|
Point,
|
||||||
|
DX,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(DiffY <= Threshold)
|
||||||
|
{
|
||||||
|
if(DiffY < SmallestDiffY)
|
||||||
|
{
|
||||||
|
SmallestDiffY = DiffY;
|
||||||
|
vAlignmentsY.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(DiffY == SmallestDiffY)
|
||||||
|
{
|
||||||
|
vAlignmentsY.push_back(SAlignmentInfo{
|
||||||
|
Aligned,
|
||||||
|
{AABB.m_aPoints[Point].x},
|
||||||
|
EAxis::AXIS_X,
|
||||||
|
Point,
|
||||||
|
DY,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto &&CheckAABBAlignment = [&](CPoint &QuadMin, CPoint &QuadMax) {
|
||||||
|
CPoint QuadCenter = (QuadMin + QuadMax) / 2.0f;
|
||||||
|
CPoint aQuadPoints[5] = {
|
||||||
|
QuadMin, // Top left
|
||||||
|
{QuadMax.x, QuadMin.y}, // Top right
|
||||||
|
{QuadMin.x, QuadMax.y}, // Bottom left
|
||||||
|
QuadMax, // Bottom right
|
||||||
|
QuadCenter,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check all points with all the other points
|
||||||
|
for(auto &QuadPoint : aQuadPoints)
|
||||||
|
{
|
||||||
|
// i is the quad point which is "aligned" and that we want to compare with
|
||||||
|
for(int j = 0; j < 5; j++)
|
||||||
|
{
|
||||||
|
// j is the point we try to align
|
||||||
|
CheckAlignment(QuadPoint, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate through all quads of the current layer
|
||||||
|
// Compute AABB of all quads and check if the dragged AABB can be aligned to this AABB.
|
||||||
|
for(size_t i = 0; i < pLayer->m_vQuads.size(); i++)
|
||||||
|
{
|
||||||
|
auto *pCurrentQuad = &pLayer->m_vQuads[i];
|
||||||
|
if(IsQuadSelected(i)) // Don't check with other selected quads
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get AABB of this quad
|
||||||
|
CPoint QuadMin = pCurrentQuad->m_aPoints[0], QuadMax = pCurrentQuad->m_aPoints[0];
|
||||||
|
for(int v = 1; v < 4; v++)
|
||||||
|
{
|
||||||
|
QuadMin.x = minimum(QuadMin.x, pCurrentQuad->m_aPoints[v].x);
|
||||||
|
QuadMin.y = minimum(QuadMin.y, pCurrentQuad->m_aPoints[v].y);
|
||||||
|
QuadMax.x = maximum(QuadMax.x, pCurrentQuad->m_aPoints[v].x);
|
||||||
|
QuadMax.y = maximum(QuadMax.y, pCurrentQuad->m_aPoints[v].y);
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckAABBAlignment(QuadMin, QuadMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, concatenate both alignment vectors into the output
|
||||||
|
vAlignments.reserve(vAlignmentsX.size() + vAlignmentsY.size());
|
||||||
|
vAlignments.insert(vAlignments.end(), vAlignmentsX.begin(), vAlignmentsX.end());
|
||||||
|
vAlignments.insert(vAlignments.end(), vAlignmentsY.begin(), vAlignmentsY.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEditor::DrawPointAlignments(const std::vector<SAlignmentInfo> &vAlignments, int OffsetX, int OffsetY)
|
||||||
|
{
|
||||||
|
if(!g_Config.m_EdAlignQuads)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Drawing an alignment is easy, we convert fixed to float for the aligned point coords
|
||||||
|
// and we also convert the "changing" value after applying the offset (which might be edited to actually align the value with the alignment).
|
||||||
|
Graphics()->SetColor(1, 0, 0.1f, 1);
|
||||||
|
for(const SAlignmentInfo &Alignment : vAlignments)
|
||||||
|
{
|
||||||
|
// We don't use IGraphics::CLineItem to draw because we don't want to stop QuadsBegin(), quads work just fine.
|
||||||
|
if(Alignment.m_Axis == EAxis::AXIS_X)
|
||||||
|
{ // Alignment on X axis is same Y values but different X values
|
||||||
|
IGraphics::CQuadItem Line(fx2f(Alignment.m_AlignedPoint.x), fx2f(Alignment.m_AlignedPoint.y), fx2f(Alignment.m_X + OffsetX - Alignment.m_AlignedPoint.x), 1.0f * m_MouseWScale);
|
||||||
|
Graphics()->QuadsDrawTL(&Line, 1);
|
||||||
|
}
|
||||||
|
else if(Alignment.m_Axis == EAxis::AXIS_Y)
|
||||||
|
{ // Alignment on Y axis is same X values but different Y values
|
||||||
|
IGraphics::CQuadItem Line(fx2f(Alignment.m_AlignedPoint.x), fx2f(Alignment.m_AlignedPoint.y), 1.0f * m_MouseWScale, fx2f(Alignment.m_Y + OffsetY - Alignment.m_AlignedPoint.y));
|
||||||
|
Graphics()->QuadsDrawTL(&Line, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEditor::DrawAABB(const SAxisAlignedBoundingBox &AABB, int OffsetX, int OffsetY)
|
||||||
|
{
|
||||||
|
// Drawing an AABB is simply converting the points from fixed to float
|
||||||
|
// Then making lines out of quads and drawing them
|
||||||
|
vec2 TL = {fx2f(AABB.m_aPoints[SAxisAlignedBoundingBox::POINT_TL].x + OffsetX), fx2f(AABB.m_aPoints[SAxisAlignedBoundingBox::POINT_TL].y + OffsetY)};
|
||||||
|
vec2 TR = {fx2f(AABB.m_aPoints[SAxisAlignedBoundingBox::POINT_TR].x + OffsetX), fx2f(AABB.m_aPoints[SAxisAlignedBoundingBox::POINT_TR].y + OffsetY)};
|
||||||
|
vec2 BL = {fx2f(AABB.m_aPoints[SAxisAlignedBoundingBox::POINT_BL].x + OffsetX), fx2f(AABB.m_aPoints[SAxisAlignedBoundingBox::POINT_BL].y + OffsetY)};
|
||||||
|
vec2 BR = {fx2f(AABB.m_aPoints[SAxisAlignedBoundingBox::POINT_BR].x + OffsetX), fx2f(AABB.m_aPoints[SAxisAlignedBoundingBox::POINT_BR].y + OffsetY)};
|
||||||
|
vec2 Center = {fx2f(AABB.m_aPoints[SAxisAlignedBoundingBox::POINT_CENTER].x + OffsetX), fx2f(AABB.m_aPoints[SAxisAlignedBoundingBox::POINT_CENTER].y + OffsetY)};
|
||||||
|
|
||||||
|
// We don't use IGraphics::CLineItem to draw because we don't want to stop QuadsBegin(), quads work just fine.
|
||||||
|
IGraphics::CQuadItem Lines[4] = {
|
||||||
|
{TL.x, TL.y, TR.x - TL.x, 1.0f * m_MouseWScale},
|
||||||
|
{TL.x, TL.y, 1.0f * m_MouseWScale, BL.y - TL.y},
|
||||||
|
{TR.x, TR.y, 1.0f * m_MouseWScale, BR.y - TR.y},
|
||||||
|
{BL.x, BL.y, BR.x - BL.x, 1.0f * m_MouseWScale},
|
||||||
|
};
|
||||||
|
Graphics()->SetColor(1, 0, 1, 1);
|
||||||
|
Graphics()->QuadsDrawTL(Lines, 4);
|
||||||
|
|
||||||
|
IGraphics::CQuadItem CenterQuad(Center.x, Center.y, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale);
|
||||||
|
Graphics()->QuadsDraw(&CenterQuad, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEditor::QuadSelectionAABB(const std::shared_ptr<CLayerQuads> &pLayer, SAxisAlignedBoundingBox &OutAABB)
|
||||||
|
{
|
||||||
|
// Compute an englobing AABB of the current selection of quads
|
||||||
|
CPoint Min{
|
||||||
|
std::numeric_limits<int>::max(),
|
||||||
|
std::numeric_limits<int>::max(),
|
||||||
|
};
|
||||||
|
CPoint Max{
|
||||||
|
std::numeric_limits<int>::min(),
|
||||||
|
std::numeric_limits<int>::min(),
|
||||||
|
};
|
||||||
|
for(int Selected : m_vSelectedQuads)
|
||||||
|
{
|
||||||
|
CQuad *pQuad = &pLayer->m_vQuads[Selected];
|
||||||
|
for(auto &Point : pQuad->m_aPoints)
|
||||||
|
{
|
||||||
|
Min.x = minimum(Min.x, Point.x);
|
||||||
|
Min.y = minimum(Min.y, Point.y);
|
||||||
|
Max.x = maximum(Max.x, Point.x);
|
||||||
|
Max.y = maximum(Max.y, Point.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CPoint Center = (Min + Max) / 2.0f;
|
||||||
|
CPoint aPoints[SAxisAlignedBoundingBox::NUM_POINTS] = {
|
||||||
|
Min, // Top left
|
||||||
|
{Max.x, Min.y}, // Top right
|
||||||
|
{Min.x, Max.y}, // Bottom left
|
||||||
|
Max, // Bottom right
|
||||||
|
Center,
|
||||||
|
};
|
||||||
|
mem_copy(OutAABB.m_aPoints, aPoints, sizeof(CPoint) * SAxisAlignedBoundingBox::NUM_POINTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEditor::ApplyAlignments(const std::vector<SAlignmentInfo> &vAlignments, int &OffsetX, int &OffsetY)
|
||||||
|
{
|
||||||
|
if(vAlignments.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find X and Y aligment
|
||||||
|
const int *pAlignedX = nullptr;
|
||||||
|
const int *pAlignedY = nullptr;
|
||||||
|
|
||||||
|
// To Find the alignments we simply iterate through the vector of alignments and find the first
|
||||||
|
// X and Y alignments.
|
||||||
|
// Then, we use the saved m_Diff to adjust the offset
|
||||||
|
int AdjustX = 0, AdjustY = 0;
|
||||||
|
for(const SAlignmentInfo &Alignment : vAlignments)
|
||||||
|
{
|
||||||
|
if(Alignment.m_Axis == EAxis::AXIS_X && !pAlignedY)
|
||||||
|
{
|
||||||
|
pAlignedY = &Alignment.m_AlignedPoint.y;
|
||||||
|
AdjustY = Alignment.m_Diff;
|
||||||
|
}
|
||||||
|
else if(Alignment.m_Axis == EAxis::AXIS_Y && !pAlignedX)
|
||||||
|
{
|
||||||
|
pAlignedX = &Alignment.m_AlignedPoint.x;
|
||||||
|
AdjustX = Alignment.m_Diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust offset
|
||||||
|
OffsetX += AdjustX;
|
||||||
|
OffsetY += AdjustY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEditor::ApplyAxisAlignment(int &OffsetX, int &OffsetY)
|
||||||
|
{
|
||||||
|
// This is used to preserve axis alignment when pressing `Shift`
|
||||||
|
// Should be called before any other computation
|
||||||
|
EAxis Axis = GetDragAxis(OffsetX, OffsetY);
|
||||||
|
OffsetX = ((Axis == EAxis::AXIS_NONE || Axis == EAxis::AXIS_X) ? OffsetX : 0);
|
||||||
|
OffsetY = ((Axis == EAxis::AXIS_NONE || Axis == EAxis::AXIS_Y) ? OffsetY : 0);
|
||||||
|
}
|
||||||
|
|
||||||
void CEditor::DoQuad(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int Index)
|
void CEditor::DoQuad(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int Index)
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
|
@ -1597,6 +2007,10 @@ void CEditor::DoQuad(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, i
|
||||||
float wx = UI()->MouseWorldX();
|
float wx = UI()->MouseWorldX();
|
||||||
float wy = UI()->MouseWorldY();
|
float wy = UI()->MouseWorldY();
|
||||||
static CPoint s_OriginalPosition;
|
static CPoint s_OriginalPosition;
|
||||||
|
static std::vector<SAlignmentInfo> s_PivotAlignments; // Alignments per pivot per quad
|
||||||
|
static std::vector<SAlignmentInfo> s_vAABBAlignments; // Alignments for one AABB (single quad or selection of multiple quads)
|
||||||
|
static SAxisAlignedBoundingBox s_SelectionAABB; // Selection AABB
|
||||||
|
static ivec2 s_LastOffset; // Last offset, stored as static so we can use it to draw every frame
|
||||||
|
|
||||||
// get pivot
|
// get pivot
|
||||||
float CenterX = fx2f(pQuad->m_aPoints[4].x);
|
float CenterX = fx2f(pQuad->m_aPoints[4].x);
|
||||||
|
@ -1643,21 +2057,26 @@ void CEditor::DoQuad(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, i
|
||||||
if(Input()->ShiftIsPressed())
|
if(Input()->ShiftIsPressed())
|
||||||
{
|
{
|
||||||
s_Operation = OP_MOVE_PIVOT;
|
s_Operation = OP_MOVE_PIVOT;
|
||||||
for(size_t i = 0; i < m_vSelectedQuads.size(); ++i)
|
// When moving, we need to save the original position of all selected pivots
|
||||||
|
for(int Selected : m_vSelectedQuads)
|
||||||
{
|
{
|
||||||
CQuad *pCurrentQuad = &pLayer->m_vQuads[m_vSelectedQuads[i]];
|
CQuad *pCurrentQuad = &pLayer->m_vQuads[Selected];
|
||||||
PreparePointDrag(pLayer, pCurrentQuad, m_vSelectedQuads[i], 4);
|
PreparePointDrag(pLayer, pCurrentQuad, Selected, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s_Operation = OP_MOVE_ALL;
|
s_Operation = OP_MOVE_ALL;
|
||||||
for(size_t i = 0; i < m_vSelectedQuads.size(); ++i)
|
// When moving, we need to save the original position of all selected quads points
|
||||||
|
for(int Selected : m_vSelectedQuads)
|
||||||
{
|
{
|
||||||
CQuad *pCurrentQuad = &pLayer->m_vQuads[m_vSelectedQuads[i]];
|
CQuad *pCurrentQuad = &pLayer->m_vQuads[Selected];
|
||||||
for(size_t v = 0; v < 5; v++)
|
for(size_t v = 0; v < 5; v++)
|
||||||
PreparePointDrag(pLayer, pCurrentQuad, m_vSelectedQuads[i], v);
|
PreparePointDrag(pLayer, pCurrentQuad, Selected, v);
|
||||||
}
|
}
|
||||||
|
// And precompute AABB of selection since it will not change during drag
|
||||||
|
if(g_Config.m_EdAlignQuads)
|
||||||
|
QuadSelectionAABB(pLayer, s_SelectionAABB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1667,26 +2086,36 @@ void CEditor::DoQuad(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, i
|
||||||
{
|
{
|
||||||
m_QuadTracker.BeginQuadTrack(pLayer, m_vSelectedQuads);
|
m_QuadTracker.BeginQuadTrack(pLayer, m_vSelectedQuads);
|
||||||
|
|
||||||
ivec2 Offset = GetDragOffset();
|
s_LastOffset = GetDragOffset(); // Update offset
|
||||||
|
ApplyAxisAlignment(s_LastOffset.x, s_LastOffset.y); // Apply axis alignment to the offset
|
||||||
|
|
||||||
|
ComputePointsAlignments(pLayer, true, s_LastOffset.x, s_LastOffset.y, s_PivotAlignments);
|
||||||
|
ApplyAlignments(s_PivotAlignments, s_LastOffset.x, s_LastOffset.y);
|
||||||
|
|
||||||
for(auto &Selected : m_vSelectedQuads)
|
for(auto &Selected : m_vSelectedQuads)
|
||||||
{
|
{
|
||||||
CQuad *pCurrentQuad = &pLayer->m_vQuads[Selected];
|
CQuad *pCurrentQuad = &pLayer->m_vQuads[Selected];
|
||||||
DoPointDrag(pLayer, pCurrentQuad, Selected, 4, Offset.x, Offset.y);
|
DoPointDrag(pLayer, pCurrentQuad, Selected, 4, s_LastOffset.x, s_LastOffset.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(s_Operation == OP_MOVE_ALL)
|
else if(s_Operation == OP_MOVE_ALL)
|
||||||
{
|
{
|
||||||
m_QuadTracker.BeginQuadTrack(pLayer, m_vSelectedQuads);
|
m_QuadTracker.BeginQuadTrack(pLayer, m_vSelectedQuads);
|
||||||
// move all points including pivot
|
|
||||||
ivec2 Offset = GetDragOffset();
|
|
||||||
|
|
||||||
for(size_t i = 0; i < m_vSelectedQuads.size(); i++)
|
// Compute drag offset
|
||||||
|
s_LastOffset = GetDragOffset();
|
||||||
|
ApplyAxisAlignment(s_LastOffset.x, s_LastOffset.y);
|
||||||
|
|
||||||
|
// Then compute possible alignments with the selection AABB
|
||||||
|
ComputeAABBAlignments(pLayer, s_SelectionAABB, s_LastOffset.x, s_LastOffset.y, s_vAABBAlignments);
|
||||||
|
// Apply alignments before drag
|
||||||
|
ApplyAlignments(s_vAABBAlignments, s_LastOffset.x, s_LastOffset.y);
|
||||||
|
// Then do the drag
|
||||||
|
for(int Selected : m_vSelectedQuads)
|
||||||
{
|
{
|
||||||
int Selected = m_vSelectedQuads[i];
|
|
||||||
CQuad *pCurrentQuad = &pLayer->m_vQuads[Selected];
|
CQuad *pCurrentQuad = &pLayer->m_vQuads[Selected];
|
||||||
for(int v = 0; v < 5; v++)
|
for(int v = 0; v < 5; v++)
|
||||||
DoPointDrag(pLayer, pCurrentQuad, Selected, v, Offset.x, Offset.y);
|
DoPointDrag(pLayer, pCurrentQuad, Selected, v, s_LastOffset.x, s_LastOffset.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(s_Operation == OP_ROTATE)
|
else if(s_Operation == OP_ROTATE)
|
||||||
|
@ -1710,12 +2139,22 @@ void CEditor::DoQuad(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw axis when moving
|
// Draw axis and aligments when moving
|
||||||
if(s_Operation == OP_MOVE_PIVOT || s_Operation == OP_MOVE_ALL)
|
if(s_Operation == OP_MOVE_PIVOT || s_Operation == OP_MOVE_ALL)
|
||||||
{
|
{
|
||||||
ivec2 Offset = GetDragOffset();
|
EAxis Axis = GetDragAxis(s_LastOffset.x, s_LastOffset.y);
|
||||||
EAxis Axis = GetDragAxis(Offset.x, Offset.y);
|
DrawAxis(Axis, s_OriginalPosition, pQuad->m_aPoints[4]);
|
||||||
DrawAxis(Axis, s_OriginalPosition);
|
}
|
||||||
|
|
||||||
|
if(s_Operation == OP_MOVE_PIVOT)
|
||||||
|
DrawPointAlignments(s_PivotAlignments, s_LastOffset.x, s_LastOffset.y);
|
||||||
|
|
||||||
|
if(s_Operation == OP_MOVE_ALL)
|
||||||
|
{
|
||||||
|
DrawPointAlignments(s_vAABBAlignments, s_LastOffset.x, s_LastOffset.y);
|
||||||
|
|
||||||
|
if(g_Config.m_EdShowQuadsRect)
|
||||||
|
DrawAABB(s_SelectionAABB, s_LastOffset.x, s_LastOffset.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s_Operation == OP_CONTEXT_MENU)
|
if(s_Operation == OP_CONTEXT_MENU)
|
||||||
|
@ -1791,6 +2230,11 @@ void CEditor::DoQuad(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, i
|
||||||
UI()->DisableMouseLock();
|
UI()->DisableMouseLock();
|
||||||
s_Operation = OP_NONE;
|
s_Operation = OP_NONE;
|
||||||
UI()->SetActiveItem(nullptr);
|
UI()->SetActiveItem(nullptr);
|
||||||
|
|
||||||
|
s_LastOffset = ivec2();
|
||||||
|
s_OriginalPosition = ivec2();
|
||||||
|
s_vAABBAlignments.clear();
|
||||||
|
s_PivotAlignments.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1892,6 +2336,8 @@ void CEditor::DoQuadPoint(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQu
|
||||||
static float s_MouseXStart = 0.0f;
|
static float s_MouseXStart = 0.0f;
|
||||||
static float s_MouseYStart = 0.0f;
|
static float s_MouseYStart = 0.0f;
|
||||||
static CPoint s_OriginalPoint;
|
static CPoint s_OriginalPoint;
|
||||||
|
static std::vector<SAlignmentInfo> s_Alignments; // Alignments
|
||||||
|
static ivec2 s_LastOffset;
|
||||||
|
|
||||||
const bool IgnoreGrid = Input()->AltIsPressed();
|
const bool IgnoreGrid = Input()->AltIsPressed();
|
||||||
|
|
||||||
|
@ -1929,6 +2375,7 @@ void CEditor::DoQuadPoint(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQu
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s_Operation = OP_MOVEPOINT;
|
s_Operation = OP_MOVEPOINT;
|
||||||
|
// Save original positions before moving
|
||||||
s_OriginalPoint = pQuad->m_aPoints[V];
|
s_OriginalPoint = pQuad->m_aPoints[V];
|
||||||
for(int m = 0; m < 4; m++)
|
for(int m = 0; m < 4; m++)
|
||||||
if(IsQuadPointSelected(QuadIndex, m))
|
if(IsQuadPointSelected(QuadIndex, m))
|
||||||
|
@ -1941,11 +2388,19 @@ void CEditor::DoQuadPoint(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQu
|
||||||
{
|
{
|
||||||
m_QuadTracker.BeginQuadTrack(pLayer, m_vSelectedQuads);
|
m_QuadTracker.BeginQuadTrack(pLayer, m_vSelectedQuads);
|
||||||
|
|
||||||
ivec2 Offset = GetDragOffset();
|
s_LastOffset = GetDragOffset(); // Update offset
|
||||||
|
ApplyAxisAlignment(s_LastOffset.x, s_LastOffset.y); // Apply axis alignment to offset
|
||||||
|
|
||||||
|
ComputePointsAlignments(pLayer, false, s_LastOffset.x, s_LastOffset.y, s_Alignments);
|
||||||
|
ApplyAlignments(s_Alignments, s_LastOffset.x, s_LastOffset.y);
|
||||||
|
|
||||||
for(int m = 0; m < 4; m++)
|
for(int m = 0; m < 4; m++)
|
||||||
|
{
|
||||||
if(IsQuadPointSelected(QuadIndex, m))
|
if(IsQuadPointSelected(QuadIndex, m))
|
||||||
DoPointDrag(pLayer, pQuad, QuadIndex, m, Offset.x, Offset.y);
|
{
|
||||||
|
DoPointDrag(pLayer, pQuad, QuadIndex, m, s_LastOffset.x, s_LastOffset.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(s_Operation == OP_MOVEUV)
|
else if(s_Operation == OP_MOVEUV)
|
||||||
{
|
{
|
||||||
|
@ -1972,12 +2427,17 @@ void CEditor::DoQuadPoint(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw axis when dragging
|
// Draw axis and alignments when dragging
|
||||||
if(s_Operation == OP_MOVEPOINT)
|
if(s_Operation == OP_MOVEPOINT)
|
||||||
{
|
{
|
||||||
ivec2 Offset = GetDragOffset();
|
Graphics()->SetColor(1, 0, 0.1f, 1);
|
||||||
EAxis Axis = GetDragAxis(Offset.x, Offset.y);
|
|
||||||
DrawAxis(Axis, s_OriginalPoint);
|
// Axis
|
||||||
|
EAxis Axis = GetDragAxis(s_LastOffset.x, s_LastOffset.y);
|
||||||
|
DrawAxis(Axis, s_OriginalPoint, pQuad->m_aPoints[V]);
|
||||||
|
|
||||||
|
// Alignments
|
||||||
|
DrawPointAlignments(s_Alignments, s_LastOffset.x, s_LastOffset.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s_Operation == OP_CONTEXT_MENU)
|
if(s_Operation == OP_CONTEXT_MENU)
|
||||||
|
@ -7402,7 +7862,7 @@ void CEditor::RenderMenubar(CUIRect MenuBar)
|
||||||
if(DoButton_Menu(&s_SettingsButton, "Settings", 0, &SettingsButton, 0, nullptr))
|
if(DoButton_Menu(&s_SettingsButton, "Settings", 0, &SettingsButton, 0, nullptr))
|
||||||
{
|
{
|
||||||
static SPopupMenuId s_PopupMenuEntitiesId;
|
static SPopupMenuId s_PopupMenuEntitiesId;
|
||||||
UI()->DoPopupMenu(&s_PopupMenuEntitiesId, SettingsButton.x, SettingsButton.y + SettingsButton.h - 1.0f, 200.0f, 64.0f, this, PopupMenuSettings, PopupProperties);
|
UI()->DoPopupMenu(&s_PopupMenuEntitiesId, SettingsButton.x, SettingsButton.y + SettingsButton.h - 1.0f, 200.0f, 92.0f, this, PopupMenuSettings, PopupProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
CUIRect ChangedIndicator, Info, Close;
|
CUIRect ChangedIndicator, Info, Close;
|
||||||
|
|
|
@ -859,6 +859,19 @@ public:
|
||||||
AXIS_X,
|
AXIS_X,
|
||||||
AXIS_Y
|
AXIS_Y
|
||||||
};
|
};
|
||||||
|
struct SAxisAlignedBoundingBox
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
POINT_TL = 0,
|
||||||
|
POINT_TR,
|
||||||
|
POINT_BL,
|
||||||
|
POINT_BR,
|
||||||
|
POINT_CENTER,
|
||||||
|
NUM_POINTS
|
||||||
|
};
|
||||||
|
CPoint m_aPoints[NUM_POINTS];
|
||||||
|
};
|
||||||
void DoMapEditor(CUIRect View);
|
void DoMapEditor(CUIRect View);
|
||||||
void DoToolbarLayers(CUIRect Toolbar);
|
void DoToolbarLayers(CUIRect Toolbar);
|
||||||
void DoToolbarSounds(CUIRect Toolbar);
|
void DoToolbarSounds(CUIRect Toolbar);
|
||||||
|
@ -866,9 +879,36 @@ public:
|
||||||
void PreparePointDrag(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int PointIndex);
|
void PreparePointDrag(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int PointIndex);
|
||||||
void DoPointDrag(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int PointIndex, int OffsetX, int OffsetY);
|
void DoPointDrag(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int PointIndex, int OffsetX, int OffsetY);
|
||||||
EAxis GetDragAxis(int OffsetX, int OffsetY);
|
EAxis GetDragAxis(int OffsetX, int OffsetY);
|
||||||
void DrawAxis(EAxis Axis, CPoint &Point);
|
void DrawAxis(EAxis Axis, CPoint &OriginalPoint, CPoint &Point);
|
||||||
|
void DrawAABB(const SAxisAlignedBoundingBox &AABB, int OffsetX = 0, int OffsetY = 0);
|
||||||
ColorRGBA GetButtonColor(const void *pID, int Checked);
|
ColorRGBA GetButtonColor(const void *pID, int Checked);
|
||||||
|
|
||||||
|
// Alignment methods
|
||||||
|
// These methods take `OffsetX` and `OffsetY` because the calculations are made with the original positions
|
||||||
|
// of the quad(s), before we started dragging. This allows us to edit `OffsetX` and `OffsetY` based on the previously
|
||||||
|
// calculated alignments.
|
||||||
|
struct SAlignmentInfo
|
||||||
|
{
|
||||||
|
CPoint m_AlignedPoint; // The "aligned" point, which we want to align/snap to
|
||||||
|
union
|
||||||
|
{
|
||||||
|
// The current changing value when aligned to this point. When aligning to a point on the X axis, then the X value is changing because
|
||||||
|
// we aligned the Y values (X axis aligned => Y values are the same, Y axis aligned => X values are the same).
|
||||||
|
int m_X;
|
||||||
|
int m_Y;
|
||||||
|
};
|
||||||
|
EAxis m_Axis; // The axis we are aligning on
|
||||||
|
int m_PointIndex; // The point index we are aligning
|
||||||
|
int m_Diff; // Store the difference
|
||||||
|
};
|
||||||
|
void ComputePointAlignments(const std::shared_ptr<CLayerQuads> &pLayer, CQuad *pQuad, int QuadIndex, int PointIndex, int OffsetX, int OffsetY, std::vector<SAlignmentInfo> &vAlignments, bool Append = false) const;
|
||||||
|
void ComputePointsAlignments(const std::shared_ptr<CLayerQuads> &pLayer, bool Pivot, int OffsetX, int OffsetY, std::vector<SAlignmentInfo> &vAlignments) const;
|
||||||
|
void ComputeAABBAlignments(const std::shared_ptr<CLayerQuads> &pLayer, const SAxisAlignedBoundingBox &AABB, int OffsetX, int OffsetY, std::vector<SAlignmentInfo> &vAlignments) const;
|
||||||
|
void DrawPointAlignments(const std::vector<SAlignmentInfo> &vAlignments, int OffsetX, int OffsetY);
|
||||||
|
void QuadSelectionAABB(const std::shared_ptr<CLayerQuads> &pLayer, SAxisAlignedBoundingBox &OutAABB);
|
||||||
|
void ApplyAlignments(const std::vector<SAlignmentInfo> &vAlignments, int &OffsetX, int &OffsetY);
|
||||||
|
void ApplyAxisAlignment(int &OffsetX, int &OffsetY);
|
||||||
|
|
||||||
bool ReplaceImage(const char *pFilename, int StorageType, bool CheckDuplicate);
|
bool ReplaceImage(const char *pFilename, int StorageType, bool CheckDuplicate);
|
||||||
static bool ReplaceImageCallback(const char *pFilename, int StorageType, void *pUser);
|
static bool ReplaceImageCallback(const char *pFilename, int StorageType, void *pUser);
|
||||||
bool ReplaceSound(const char *pFileName, int StorageType, bool CheckDuplicate);
|
bool ReplaceSound(const char *pFileName, int StorageType, bool CheckDuplicate);
|
||||||
|
@ -1034,7 +1074,7 @@ private:
|
||||||
void RedoLastAction();
|
void RedoLastAction();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<int, std::map<int, CPoint>> m_QuadDragOriginalPoints;
|
std::map<int, CPoint[5]> m_QuadDragOriginalPoints;
|
||||||
};
|
};
|
||||||
|
|
||||||
// make sure to inline this function
|
// make sure to inline this function
|
||||||
|
|
|
@ -328,6 +328,58 @@ CUI::EPopupMenuFunctionResult CEditor::PopupMenuSettings(void *pContext, CUIRect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
View.HSplitTop(2.0f, nullptr, &View);
|
||||||
|
View.HSplitTop(12.0f, &Slot, &View);
|
||||||
|
{
|
||||||
|
Slot.VMargin(5.0f, &Slot);
|
||||||
|
|
||||||
|
CUIRect Label, Selector;
|
||||||
|
Slot.VSplitMid(&Label, &Selector);
|
||||||
|
CUIRect No, Yes;
|
||||||
|
Selector.VSplitMid(&No, &Yes);
|
||||||
|
|
||||||
|
pEditor->UI()->DoLabel(&Label, "Align quads", 10.0f, TEXTALIGN_ML);
|
||||||
|
if(pEditor->m_AllowPlaceUnusedTiles != -1)
|
||||||
|
{
|
||||||
|
static int s_ButtonNo = 0;
|
||||||
|
static int s_ButtonYes = 0;
|
||||||
|
if(pEditor->DoButton_ButtonDec(&s_ButtonNo, "No", !g_Config.m_EdAlignQuads, &No, 0, "Do not perform quad alignment to other quads/points when moving quads"))
|
||||||
|
{
|
||||||
|
g_Config.m_EdAlignQuads = false;
|
||||||
|
}
|
||||||
|
if(pEditor->DoButton_ButtonInc(&s_ButtonYes, "Yes", g_Config.m_EdAlignQuads, &Yes, 0, "Allow quad alignment to other quads/points when moving quads"))
|
||||||
|
{
|
||||||
|
g_Config.m_EdAlignQuads = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
View.HSplitTop(2.0f, nullptr, &View);
|
||||||
|
View.HSplitTop(12.0f, &Slot, &View);
|
||||||
|
{
|
||||||
|
Slot.VMargin(5.0f, &Slot);
|
||||||
|
|
||||||
|
CUIRect Label, Selector;
|
||||||
|
Slot.VSplitMid(&Label, &Selector);
|
||||||
|
CUIRect No, Yes;
|
||||||
|
Selector.VSplitMid(&No, &Yes);
|
||||||
|
|
||||||
|
pEditor->UI()->DoLabel(&Label, "Show quads bounds", 10.0f, TEXTALIGN_ML);
|
||||||
|
if(pEditor->m_AllowPlaceUnusedTiles != -1)
|
||||||
|
{
|
||||||
|
static int s_ButtonNo = 0;
|
||||||
|
static int s_ButtonYes = 0;
|
||||||
|
if(pEditor->DoButton_ButtonDec(&s_ButtonNo, "No", !g_Config.m_EdShowQuadsRect, &No, 0, "Do not show quad bounds when moving quads"))
|
||||||
|
{
|
||||||
|
g_Config.m_EdShowQuadsRect = false;
|
||||||
|
}
|
||||||
|
if(pEditor->DoButton_ButtonInc(&s_ButtonYes, "Yes", g_Config.m_EdShowQuadsRect, &Yes, 0, "Show quad bounds when moving quads"))
|
||||||
|
{
|
||||||
|
g_Config.m_EdShowQuadsRect = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return CUI::POPUP_KEEP_OPEN;
|
return CUI::POPUP_KEEP_OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue