Advertisement
h_bjergen

Rects

Jun 6th, 2025
274
0
6 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.07 KB | Source Code | 0 0
  1. #include <windows.h>
  2. #include <commctrl.h>
  3. #include <vector>
  4. #include <string>
  5. #include <sstream>
  6. #include <set>
  7. #include <map>
  8.  
  9. #pragma comment(lib, "Comctl32.lib")
  10.  
  11. // Actual code block for the logc
  12. struct RectModel {
  13.     int Index;
  14.     double X, Y, Width, Height;
  15. };
  16.  
  17. struct Edges {
  18.     double Left, Right, Top, Bottom;
  19. };
  20.  
  21. Edges GetEdges(const RectModel& rect) {
  22.     return Edges{
  23.         rect.X,
  24.         rect.X + rect.Width,
  25.         rect.Y,
  26.         rect.Y + rect.Height
  27.     };
  28. }
  29.  
  30. bool IsValidPosition(int index, const std::vector<RectModel>& rects) {
  31.     if (index < 0 || index >= rects.size()) return false;
  32.  
  33.     const RectModel& rect = rects[index];
  34.     int role = index % 4;
  35.  
  36.     Edges pos = GetEdges(rect);
  37.  
  38.     const RectModel* leftNeighbor = (index - 2 >= 0) ? &rects[index - 2] : nullptr;
  39.     const RectModel* rightNeighbor = (index + 2 < rects.size()) ? &rects[index + 2] : nullptr;
  40.  
  41.     int pairIndex = (role == 0 || role == 2) ? index + 1 : index - 1;
  42.     const RectModel* pair = (pairIndex >= 0 && pairIndex < rects.size()) ? &rects[pairIndex] : nullptr;
  43.  
  44.     if (leftNeighbor) {
  45.         Edges left = GetEdges(*leftNeighbor);
  46.         if (pos.Left < left.Right)
  47.             return false;
  48.     }
  49.  
  50.     if (rightNeighbor) {
  51.         Edges right = GetEdges(*rightNeighbor);
  52.         if (pos.Right > right.Left)
  53.             return false;
  54.     }
  55.  
  56.     if (pair) {
  57.         Edges pairPos = GetEdges(*pair);
  58.         if (role == 0 || role == 2) {
  59.             if (pos.Bottom > pairPos.Top)
  60.                 return false;
  61.         }
  62.         else {
  63.             if (pos.Top < pairPos.Bottom)
  64.                 return false;
  65.         }
  66.     }
  67.  
  68.     return true;
  69. }
  70.  
  71. std::set<int> GetInvalidRectIndexes(const std::vector<RectModel>& rects) {
  72.     std::set<int> invalid;
  73.     for (int i = 0; i < rects.size(); ++i) {
  74.         if (!IsValidPosition(i, rects)) {
  75.             invalid.insert(i);
  76.         }
  77.     }
  78.     return invalid;
  79. }
  80. //********************************************************************************//
  81. // --- Input Data ---
  82. std::vector<RectModel> GetRectData() {
  83.     return {
  84.         {0, 80, 275, 130, 150},
  85.         {1, 80, 720, 130, 150},
  86.         {2, 250, 275, 130, 150},
  87.         {3, 250, 720, 130, 150},
  88.         {4, 460, 275, 130, 150},
  89.         {5, 460, 720, 130, 150},
  90.         {6, 640, 275, 130, 150},
  91.         {7, 640, 720, 130, 150},
  92.         {8, 830, 275, 130, 150},
  93.         {9, 830, 720, 130, 150},
  94.         {10, 1000, 275, 130, 150},
  95.         {11, 1000, 720, 130, 150},
  96.         {12, 1220, 275, 130, 150},
  97.         {13, 1220, 720, 130, 150},
  98.         {14, 1380, 275, 130, 150},
  99.         {15, 1380, 720, 130, 150},
  100.         {16, 1580, 275, 130, 150},
  101.         {17, 1580, 720, 130, 150},
  102.         {18, 1730, 275, 130, 150},
  103.         {19, 1730, 720, 130, 150}
  104.     };
  105. }
  106.  
  107. // --- Globals ---
  108. std::vector<RectModel> rects;
  109. std::set<int> invalidIndexes;
  110. std::map<int, std::wstring> InvalidReasons;
  111. std::vector<std::wstring> OverlapMessages;
  112.  
  113. int selectedIndex = -1;
  114.  
  115.  
  116.  
  117.  
  118. // *******************************************Visuals*****************************************************
  119. HWND hSliderX, hSliderY, hEditBox, hEditWidth, hEditHeight;
  120.  
  121.  
  122. std::wstring GetInvalidReasonString(int index, const std::vector<RectModel>& rects) {
  123.     if (index < 0 || index >= rects.size()) return L"Invalid index";
  124.  
  125.     const RectModel& rect = rects[index];
  126.     int role = index % 4;
  127.     Edges pos = GetEdges(rect);
  128.  
  129.     const RectModel* leftNeighbor = (index - 2 >= 0) ? &rects[index - 2] : nullptr;
  130.     const RectModel* rightNeighbor = (index + 2 < rects.size()) ? &rects[index + 2] : nullptr;
  131.  
  132.     int pairIndex = (role == 0 || role == 2) ? index + 1 : index - 1;
  133.     const RectModel* pair = (pairIndex >= 0 && pairIndex < rects.size()) ? &rects[pairIndex] : nullptr;
  134.  
  135.     std::wstring error;
  136.  
  137.     // X constraint
  138.     if (leftNeighbor) {
  139.         Edges left = GetEdges(*leftNeighbor);
  140.         if (pos.Left < left.Right)
  141.             error += L"Crosses vertical axis of left neighbor. ";
  142.     }
  143.     if (rightNeighbor) {
  144.         Edges right = GetEdges(*rightNeighbor);
  145.         if (pos.Right > right.Left)
  146.             error += L"Crosses vertical axis of right neighbor. ";
  147.     }
  148.  
  149.     // Y constraint
  150.     if (pair) {
  151.         Edges pairPos = GetEdges(*pair);
  152.         if (role == 0 || role == 2) {
  153.             if (pos.Bottom > pairPos.Top)
  154.                 error += L"Crosses horizontal axis of its pair. ";
  155.         }
  156.         else {
  157.             if (pos.Top < pairPos.Bottom)
  158.                 error += L"Crosses horizontal axis of its pair. ";
  159.         }
  160.     }
  161.  
  162.     // Role mismatch
  163.     int groupStart = (index / 4) * 4;
  164.     int groupEnd = min((int)rects.size(), groupStart + 4);
  165.     if (groupEnd - groupStart == 4) {
  166.         const Edges& current = GetEdges(rect);
  167.         const Edges& TL = GetEdges(rects[groupStart]);
  168.         const Edges& BL = GetEdges(rects[groupStart + 1]);
  169.         const Edges& TR = GetEdges(rects[groupStart + 2]);
  170.         const Edges& BR = GetEdges(rects[groupStart + 3]);
  171.  
  172.         switch (role) {
  173.         case 0: if (!(current.Left <= TR.Left && current.Top <= BL.Top)) error += L"Expected Top-Left. "; break;
  174.         case 1: if (!(current.Left <= BR.Left && current.Top >= TL.Bottom)) error += L"Expected Bottom-Left. "; break;
  175.         case 2: if (!(current.Left >= TL.Right && current.Top <= BR.Top)) error += L"Expected Top-Right. "; break;
  176.         case 3: if (!(current.Left >= BL.Right && current.Top >= TR.Bottom)) error += L"Expected Bottom-Right. "; break;
  177.         }
  178.     }
  179.  
  180.     return error;
  181. }
  182.  
  183. void UpdateValidation() {
  184.     invalidIndexes.clear();
  185.     InvalidReasons.clear();
  186.     OverlapMessages.clear();
  187.  
  188.     std::map<std::wstring, std::vector<int>> crossGroups;
  189.  
  190.     for (int i = 0; i < rects.size(); ++i) {
  191.         if (!IsValidPosition(i, rects)) {
  192.             invalidIndexes.insert(i);
  193.             std::wstring reason = GetInvalidReasonString(i, rects);
  194.             InvalidReasons[i] = reason;
  195.  
  196.             // Check if it's a cross-axis issue
  197.             if (reason.find(L"Crosses vertical axis") != std::wstring::npos ||
  198.                 reason.find(L"Crosses horizontal axis") != std::wstring::npos) {
  199.                 std::wstring key = L"group_" + std::to_wstring(i / 4);
  200.                 crossGroups[key].push_back(i);
  201.             }
  202.         }
  203.     }
  204.  
  205.     for (const auto& group : crossGroups) {
  206.         const auto& affected = group.second;
  207.  
  208.         std::wstringstream ss;
  209.         ss << L"Rectangles at indices [";
  210.         for (size_t i = 0; i < affected.size(); ++i) {
  211.             ss << affected[i];
  212.             if (i < affected.size() - 1) ss << L", ";
  213.         }
  214.  
  215.         ss << L"] cross the horizontal or vertical axis of neighbors. ";
  216.         ss << L"Currently these are marked invalid, but only those that mismatch their role should be marked.";
  217.  
  218.         OverlapMessages.push_back(ss.str());
  219.     }
  220. }
  221.  
  222.  
  223.  
  224. void UpdateSelectedRectFromSliders() {
  225.     if (selectedIndex < 0 || selectedIndex >= rects.size()) return;
  226.  
  227.     int x = SendMessage(hSliderX, TBM_GETPOS, 0, 0);
  228.     int y = SendMessage(hSliderY, TBM_GETPOS, 0, 0);
  229.     rects[selectedIndex].X = x;
  230.     rects[selectedIndex].Y = y;
  231.     UpdateValidation();
  232. }
  233.  
  234. void SetSlidersToRectPosition() {
  235.     if (selectedIndex < 0 || selectedIndex >= rects.size()) return;
  236.  
  237.     const RectModel& r = rects[selectedIndex];
  238.  
  239.     SendMessage(hSliderX, TBM_SETPOS, TRUE, static_cast<int>(r.X));
  240.     SendMessage(hSliderY, TBM_SETPOS, TRUE, static_cast<int>(r.Y));
  241.  
  242.     wchar_t buf[32];
  243.     swprintf_s(buf, L"%d", static_cast<int>(r.Width));
  244.     SetWindowText(hEditWidth, buf);
  245.  
  246.     swprintf_s(buf, L"%d", static_cast<int>(r.Height));
  247.     SetWindowText(hEditHeight, buf);
  248. }
  249.  
  250.  
  251. LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  252.     switch (msg) {
  253.     case WM_CREATE: {
  254.         InitCommonControls();
  255.  
  256.         // Label for Index
  257.         CreateWindow(L"STATIC", L"Index:", WS_VISIBLE | WS_CHILD,
  258.             10, 10, 40, 20, hwnd, nullptr, nullptr, nullptr);
  259.  
  260.         // Edit box for entering the index
  261.         hEditBox = CreateWindow(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER,
  262.             60, 10, 50, 20, hwnd, (HMENU)1001, nullptr, nullptr);
  263.  
  264.         // Label for X slider
  265.         CreateWindow(L"STATIC", L"X Position:", WS_VISIBLE | WS_CHILD,
  266.             10, 50, 100, 20, hwnd, nullptr, nullptr, nullptr);
  267.  
  268.         // X position slider
  269.         hSliderX = CreateWindow(TRACKBAR_CLASS, L"X Slider", WS_VISIBLE | WS_CHILD | TBS_AUTOTICKS,
  270.             130, 50, 300, 30, hwnd, (HMENU)1002, nullptr, nullptr);
  271.         SendMessage(hSliderX, TBM_SETRANGE, TRUE, MAKELPARAM(0, 1800));
  272.  
  273.         // Label for Y slider
  274.         CreateWindow(L"STATIC", L"Y Position:", WS_VISIBLE | WS_CHILD,
  275.             10, 90, 100, 20, hwnd, nullptr, nullptr, nullptr);
  276.  
  277.         // Y position slider
  278.         hSliderY = CreateWindow(TRACKBAR_CLASS, L"Y Slider", WS_VISIBLE | WS_CHILD | TBS_AUTOTICKS,
  279.             130, 90, 300, 30, hwnd, (HMENU)1003, nullptr, nullptr);
  280.         SendMessage(hSliderY, TBM_SETRANGE, TRUE, MAKELPARAM(0, 1000));
  281.  
  282.         // Label for Width
  283.         CreateWindow(L"STATIC", L"Width:", WS_VISIBLE | WS_CHILD,
  284.             460, 50, 50, 20, hwnd, nullptr, nullptr, nullptr);
  285.  
  286.         // Width input
  287.         hEditWidth = CreateWindow(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER,
  288.             520, 50, 60, 20, hwnd, (HMENU)1004, nullptr, nullptr);
  289.  
  290.         // Label for Height
  291.         CreateWindow(L"STATIC", L"Height:", WS_VISIBLE | WS_CHILD,
  292.             460, 90, 50, 20, hwnd, nullptr, nullptr, nullptr);
  293.  
  294.         // Height input
  295.         hEditHeight = CreateWindow(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER,
  296.             520, 90, 60, 20, hwnd, (HMENU)1005, nullptr, nullptr);
  297.  
  298.  
  299.         break;
  300.     }
  301.  
  302.     case WM_COMMAND: {
  303.         if (LOWORD(wParam) == 1001 && HIWORD(wParam) == EN_CHANGE) {
  304.             wchar_t buf[16];
  305.             GetWindowText(hEditBox, buf, 16);
  306.             int idx = _wtoi(buf);
  307.             if (idx >= 0 && idx < rects.size()) {
  308.                 selectedIndex = idx;
  309.                 SetSlidersToRectPosition();
  310.             }
  311.         }
  312.         else if ((LOWORD(wParam) == 1004 || LOWORD(wParam) == 1005) && HIWORD(wParam) == EN_CHANGE) {
  313.             if (selectedIndex >= 0 && selectedIndex < rects.size()) {
  314.                 wchar_t buf[16];
  315.  
  316.                 GetWindowText(hEditWidth, buf, 16);
  317.                 int newWidth = _wtoi(buf);
  318.  
  319.                 GetWindowText(hEditHeight, buf, 16);
  320.                 int newHeight = _wtoi(buf);
  321.  
  322.                 if (newWidth > 0 && newHeight > 0) {
  323.                     rects[selectedIndex].Width = newWidth;
  324.                     rects[selectedIndex].Height = newHeight;
  325.  
  326.                     UpdateValidation();
  327.  
  328.                     RECT canvasRect = { 0, 150, 1920, 1080 };
  329.                     InvalidateRect(hwnd, &canvasRect, FALSE);
  330.                 }
  331.             }
  332.         }
  333.  
  334.         break;
  335.     }
  336.  
  337.     case WM_HSCROLL: {
  338.         if ((HWND)lParam == hSliderX || (HWND)lParam == hSliderY) {
  339.             UpdateSelectedRectFromSliders();
  340.             RECT canvasRect = { 0, 150, 1920, 1080 };
  341.             InvalidateRect(hwnd, &canvasRect, FALSE);
  342.         }
  343.         break;
  344.     }
  345.  
  346.     case WM_ERASEBKGND:
  347.         return 1;
  348.  
  349.  
  350.     case WM_PAINT: {
  351.         PAINTSTRUCT ps;
  352.         HDC hdc = BeginPaint(hwnd, &ps);
  353.  
  354.         RECT clientRect;
  355.         GetClientRect(hwnd, &clientRect);
  356.  
  357.         HDC memDC = CreateCompatibleDC(hdc);
  358.         HBITMAP memBitmap = CreateCompatibleBitmap(hdc, clientRect.right, clientRect.bottom);
  359.         HBITMAP oldBitmap = (HBITMAP)SelectObject(memDC, memBitmap);
  360.  
  361.         HBRUSH bgBrush = CreateSolidBrush(RGB(255, 255, 255));
  362.         FillRect(memDC, &clientRect, bgBrush);
  363.         DeleteObject(bgBrush);
  364.  
  365.         if (!OverlapMessages.empty()) {
  366.             SetBkMode(memDC, TRANSPARENT);
  367.             SetTextColor(memDC, RGB(200, 0, 0));
  368.  
  369.             int msgY = 170;
  370.             for (const auto& msg : OverlapMessages) {
  371.                 RECT textRect = { 400, msgY, 1600, msgY + 40 };
  372.                 DrawText(memDC, msg.c_str(), -1, &textRect, DT_CENTER | DT_TOP | DT_WORDBREAK);
  373.                 msgY += 40;
  374.             }
  375.         }
  376.  
  377.  
  378.         // Draw rects onto memDC
  379.         for (const auto& rect : rects) {
  380.             RECT r;
  381.             r.left = static_cast<LONG>(rect.X);
  382.             r.top = static_cast<LONG>(rect.Y);
  383.             r.right = r.left + static_cast<LONG>(rect.Width);
  384.             r.bottom = r.top + static_cast<LONG>(rect.Height);
  385.  
  386.             bool isInvalid = invalidIndexes.count(rect.Index) > 0;
  387.  
  388.             HPEN pen = CreatePen(PS_SOLID, 3, isInvalid ? RGB(255, 0, 0) : RGB(0, 200, 0));
  389.             HPEN oldPen = (HPEN)SelectObject(memDC, pen);
  390.             HBRUSH oldBrush = (HBRUSH)SelectObject(memDC, GetStockObject(NULL_BRUSH));
  391.             Rectangle(memDC, r.left, r.top, r.right, r.bottom);
  392.             SelectObject(memDC, oldBrush);
  393.             SelectObject(memDC, oldPen);
  394.             DeleteObject(pen);
  395.  
  396.             std::wstringstream ss;
  397.             ss << rect.Index;
  398.             std::wstring label = ss.str();
  399.  
  400.             SetBkMode(memDC, TRANSPARENT);
  401.             SetTextColor(memDC, RGB(0, 0, 0));
  402.             DrawText(memDC, label.c_str(), -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  403.  
  404.             // Draw validation message if invalid
  405.             auto it = InvalidReasons.find(rect.Index);
  406.             if (it != InvalidReasons.end()) {
  407.                 RECT msgRect = r;
  408.                 msgRect.top = r.bottom + 5;
  409.                 msgRect.bottom = msgRect.top + 80;
  410.                 msgRect.left -= 50;                
  411.                 msgRect.right += 50;
  412.  
  413.                 SetTextColor(memDC, RGB(200, 0, 0));
  414.                 DrawText(memDC, it->second.c_str(), -1, &msgRect, DT_CENTER | DT_TOP | DT_WORDBREAK);
  415.             }
  416.  
  417.         }
  418.  
  419.         BitBlt(hdc, 0, 0, clientRect.right, clientRect.bottom, memDC, 0, 0, SRCCOPY);
  420.  
  421.         // Clean up
  422.         SelectObject(memDC, oldBitmap);
  423.         DeleteObject(memBitmap);
  424.         DeleteDC(memDC);
  425.  
  426.         EndPaint(hwnd, &ps);
  427.         break;
  428.     }
  429.  
  430.  
  431.     case WM_DESTROY:
  432.         PostQuitMessage(0);
  433.         break;
  434.  
  435.     default:
  436.         return DefWindowProc(hwnd, msg, wParam, lParam);
  437.     }
  438.  
  439.     return 0;
  440. }
  441.  
  442. int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
  443.     const wchar_t CLASS_NAME[] = L"RectEditorWin32";
  444.  
  445.     WNDCLASS wc = {};
  446.     wc.lpfnWndProc = WndProc;
  447.     wc.hInstance = hInstance;
  448.     wc.lpszClassName = CLASS_NAME;
  449.     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  450.  
  451.     RegisterClass(&wc);
  452.  
  453.     HWND hwnd = CreateWindowEx(
  454.         0, CLASS_NAME, L"RectModel Editor - Win32",
  455.         WS_OVERLAPPEDWINDOW,
  456.         CW_USEDEFAULT, CW_USEDEFAULT, 1920, 1080,
  457.         nullptr, nullptr, hInstance, nullptr
  458.     );
  459.  
  460.     if (!hwnd) return 0;
  461.  
  462.     rects = GetRectData();
  463.     UpdateValidation();
  464.  
  465.     ShowWindow(hwnd, nCmdShow);
  466.     UpdateWindow(hwnd);
  467.  
  468.     MSG msg = {};
  469.     while (GetMessage(&msg, nullptr, 0, 0)) {
  470.         TranslateMessage(&msg);
  471.         DispatchMessage(&msg);
  472.     }
  473.  
  474.     return 0;
  475. }
  476.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement