Перейти к содержанию

Antonshka

Пользователи+
  • Постов

    410
  • Зарегистрирован

  • Посещение

  • Победитель дней

    16

Сообщения, опубликованные Antonshka

  1. В 26.06.2022 в 18:31, Xipho сказал:

    Блиттинг с серой маской.

    Или средневзвешенное значение цвета. Правда, может дать непредсказуемый результат

    Но самое быстрое и эффективное - применение формулы выше для картинки, загруженной в DIB секцию.

    Впрочем, картинку необязательно грузить в вышеупомянутую секцию, так как есть WinAPI функции для получения битов картинки в массив для последующей быстрой обработки

    Последний вариант мне пригляделся больше. Ты имел ввиду GetDIBits функцию?

     

    Я пытаюсь сделать меню как в Maya 3D, по внешнему виду и функционалу. Там оказывается при наведении на пункт меню иконка еще и подсвечивается, то есть становиться ярче.

    В интернете нашел две теории как сделать иконку ярче. Первая это через умножение каждого канала на множитель. Вторая это перевод RGB в HSV, затем изменение V (яркости), и затем обратный перевод HSV в RGB.

    С HSV не вышло, даже без изменения V некоторые цвета становятся другими. Я пробовал два разных исходника. Результат один и тот же.

    Пришлось использовать множитель.  С ним цвета нормальные.

     

    Это некоторые методы для иконки

    Спойлер
    //Public method.
    VOID PopupMenu::AddItem(MenuItemEnableState enableState, MenuItemType itemType, MenuItemCheckableState checkableState, MenuItemCheckedState checkedState, MenuItemCheckMarkType checkMarkType, DWORD iconID, std::wstring text, std::wstring hotkeysText, OnEvent itemClickEvent, PopupMenu* popupMenu, MenuItemSettingsButtonState settingsButtonState, OnEvent settingsButtonClickEvent)
    {
      HICON icon = NULL;
      if (iconID)
        icon = reinterpret_cast<HICON>(LoadImageW(WGW::hInstance, MAKEINTRESOURCE(iconID), IMAGE_ICON,
                                                  GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0));
      AddItem(enableState, itemType, checkableState, checkedState, checkMarkType, icon, text, hotkeysText, itemClickEvent, popupMenu, settingsButtonState, settingsButtonClickEvent);
    }
    
    //Public method.
    VOID PopupMenu::AddItem(MenuItemEnableState enableState, MenuItemType itemType, MenuItemCheckableState checkableState, MenuItemCheckedState checkedState, MenuItemCheckMarkType checkMarkType, std::wstring iconPath, std::wstring text, std::wstring hotkeysText, OnEvent itemClickEvent, PopupMenu* popupMenu, MenuItemSettingsButtonState settingsButtonState, OnEvent settingsButtonClickEvent)
    {
      HICON icon = NULL;
      if (iconPath.size())
        icon = reinterpret_cast<HICON>(LoadImageW(NULL, iconPath.data(), IMAGE_ICON,
                                                  GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE));
      AddItem(enableState, itemType, checkableState, checkedState, checkMarkType, icon, text, hotkeysText, itemClickEvent, popupMenu, settingsButtonState, settingsButtonClickEvent);
    }
    
    //Private method.
    VOID PopupMenu::AddItem(MenuItemEnableState enableState, MenuItemType itemType, MenuItemCheckableState checkableState, MenuItemCheckedState checkedState, MenuItemCheckMarkType checkMarkType, HICON icon, std::wstring text, std::wstring hotkeysText, OnEvent itemClickEvent, PopupMenu* popupMenu, MenuItemSettingsButtonState settingsButtonState, OnEvent settingsButtonClickEvent)
    {
      WindowPropsMenuItem* item = new WindowPropsMenuItem();
      items.push_back(item);
      item->enableState = enableState;
      item->type = itemType;
      item->checkableState = checkableState;
      item->checkedState = checkedState;
      item->checkMarkType = checkMarkType;
      if (icon)
      {
        item->coloredIcon = icon;
        CreateBrighterIconAndGrayscaleIcon(item);
      }
      item->text = text;
      item->hotkeysText = hotkeysText;
      item->clickEvent = itemClickEvent;
      item->popupMenu = popupMenu;
      item->settingsButtonState = settingsButtonState;
      item->settingsButtonClickEvent = settingsButtonClickEvent;
      CalculatePopupMenuSize();
      CalculatePopupMenuItemsRects();
    }
    
    //Private method.
    VOID PopupMenu::CreateBrighterIconAndGrayscaleIcon(WindowPropsMenuItem* item)
    {
      INT brightnessIncreased = 0;
      FLOAT brightnessIncreaseDegree = 1.2;
      std::unique_ptr<BYTE[]> bytesForBrighterIcon;
      std::unique_ptr<BYTE[]> bytesForGrayscaleIcon;
      INT bytesOffset = 0;
      INT bytesPerPixel = 0;
      INT bitsStride = 0;
      BITMAP coloredIconBitmap{ 0 };
      ICONINFO coloredIconInfo{ 0 };
      DWORD coloredIconSizeInBytes = 0;
      BITMAPINFO desiredBitmapInfo{ 0 };
      HDC desktopDC = NULL;
      BYTE grayscale = 0;
      GetIconInfo(item->coloredIcon, &coloredIconInfo);
      if (coloredIconInfo.hbmColor)
      {
        GetObjectW(coloredIconInfo.hbmColor, sizeof(BITMAP), &coloredIconBitmap);
        if (coloredIconBitmap.bmBitsPixel >= 24)
        {
          desktopDC = GetDC(HWND_DESKTOP);
          coloredIconSizeInBytes = ((coloredIconBitmap.bmWidth * coloredIconBitmap.bmBitsPixel + 31) / 32) * 4 * coloredIconBitmap.bmHeight;
          desiredBitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
          desiredBitmapInfo.bmiHeader.biWidth = coloredIconBitmap.bmWidth;
          desiredBitmapInfo.bmiHeader.biHeight = coloredIconBitmap.bmHeight;
          desiredBitmapInfo.bmiHeader.biPlanes = 1;
          desiredBitmapInfo.bmiHeader.biBitCount = coloredIconBitmap.bmBitsPixel;
          desiredBitmapInfo.bmiHeader.biCompression = BI_RGB;
          bytesPerPixel = coloredIconBitmap.bmBitsPixel / 8;
          bitsStride = coloredIconBitmap.bmWidth + (coloredIconBitmap.bmWidth * bytesPerPixel) % 4;
          bytesForBrighterIcon.reset(new BYTE[coloredIconSizeInBytes]);
          bytesForGrayscaleIcon.reset(new BYTE[coloredIconSizeInBytes]);
          GetDIBits(desktopDC, coloredIconInfo.hbmColor, 0, coloredIconBitmap.bmHeight, bytesForBrighterIcon.get(), &desiredBitmapInfo, DIB_RGB_COLORS);
          GetDIBits(desktopDC, coloredIconInfo.hbmColor, 0, coloredIconBitmap.bmHeight, bytesForGrayscaleIcon.get(), &desiredBitmapInfo, DIB_RGB_COLORS);
          for (INT y = 0; y < coloredIconBitmap.bmHeight; y++)
          {
            for (INT x = 0; x < bitsStride; x++)
            {
              bytesOffset = (x + y * bitsStride) * bytesPerPixel;
              brightnessIncreased = bytesForBrighterIcon[bytesOffset + 0] * brightnessIncreaseDegree;
              if (brightnessIncreased > 255)
                brightnessIncreased = 255;
              if (brightnessIncreased < 0)
                brightnessIncreased = 0;
              bytesForBrighterIcon[bytesOffset + 0] = brightnessIncreased;
              brightnessIncreased = bytesForBrighterIcon[bytesOffset + 1] * brightnessIncreaseDegree;
              if (brightnessIncreased > 255)
                brightnessIncreased = 255;
              if (brightnessIncreased < 0)
                brightnessIncreased = 0;
              bytesForBrighterIcon[bytesOffset + 1] = brightnessIncreased;
              brightnessIncreased = bytesForBrighterIcon[bytesOffset + 2] * brightnessIncreaseDegree;
              if (brightnessIncreased > 255)
                brightnessIncreased = 255;
              if (brightnessIncreased < 0)
                brightnessIncreased = 0;
              bytesForBrighterIcon[bytesOffset + 2] = brightnessIncreased;
            }
          }
          for (INT y = 0; y < coloredIconBitmap.bmHeight; y++)
          {
            for (INT x = 0; x < bitsStride; x++)
            {
              bytesOffset = (x + y * bitsStride) * bytesPerPixel;
              grayscale = (50 + 11 * bytesForGrayscaleIcon[bytesOffset + 0] + 59 * bytesForGrayscaleIcon[bytesOffset + 1] + 30 * bytesForGrayscaleIcon[bytesOffset + 2]) / 100;
              bytesForGrayscaleIcon[bytesOffset + 0] = bytesForGrayscaleIcon[bytesOffset + 1] = bytesForGrayscaleIcon[bytesOffset + 2] = grayscale;
            }
          }
          SetDIBits(desktopDC, coloredIconInfo.hbmColor, 0, coloredIconBitmap.bmHeight, bytesForBrighterIcon.get(), &desiredBitmapInfo, DIB_RGB_COLORS);
          item->brighterIcon = CreateIconIndirect(&coloredIconInfo);
          SetDIBits(desktopDC, coloredIconInfo.hbmColor, 0, coloredIconBitmap.bmHeight, bytesForGrayscaleIcon.get(), &desiredBitmapInfo, DIB_RGB_COLORS);
          item->grayscaleIcon = CreateIconIndirect(&coloredIconInfo);
          ReleaseDC(HWND_DESKTOP, desktopDC);
        }
      }
    }

     

     

     

     

  2. 24 минуты назад, youneuoy сказал:

    Из первой же ссылки в гугле:

    Это верно.

    Мне был интересен сам подход. Самый быстрый и лучший. Так как я полагал что существует несколько подходов. Через CompatibleDC например, или возможно напрямую как-нибудь.

  3. Привет всем, никто не сталкивался с такой задачей?

    Пользователь, для пункта меню, предоставляет только цветную версию иконки (для удобства). Метод "Добавить пункт" класса "Меню", автоматически создает ее черно-белую версию. Она будет рисоваться когда пункт меню будет выключен.

     

    DrawState из WinAPI хоть и умеет делать из цветной иконки черно-белую, но результат не всегда хороший.

    Поэтому сейчас ищу наиболее лучший и быстрый способ конвертации. Основная идею конвертации это изменение RGB значения каждого пикселя изображения на определенное значение.

    Как появится первый рабочий вариант, то обязательно его выложу.

  4. Из своего опыта могу сказать что через cmp сравнение float работает некорректно. Особенно если float имеет отрицательное значение. Я использовал fpu.

    Из старого трейнера -

    fld dword ptr [Camera_Original_Pitch]
    fcomp dword ptr [Mouse_Screen_Y_Min_Value]
    fstsw ax
    sahf
    jae @f

     

    • Плюс 1
  5. 1 час назад, Xipho сказал:

    Это не отдельная программа, это часть общего алгоритма. Определение зависит от внесенных данных. Если внести данные по всей планете, то определит везде. Сейчас пока только Россия, и часть стран СНГ. Прототип алгоритма определяет за ~300 милисекунд, но я еще работаю над его оптимизацией, потому что можно время уменьшить за счет оптимизации способа хранения данных о границах населенных пунктов.

    Тоже хотел попробовать себя в этой задаче. Есть даже своя нестандартная идея. Стандартная как понимаю это R-дерево.

    Но, мне нужно доделывать начатое.

  6. 14 минут назад, Xipho сказал:

    Я бы еще предложил прочитать Фень Юаня "Программирование графики для Windows", чтобы заложить основы понимания функционирования классического графического интерфейса Windows и его графической системы GDI, но, наверное, сейчас это предлагать уже поздно. Впрочем, можно почитать первые несколько глав для устаканивания фундаментального понимания.

    Я читал об этом в книге Щупака. Про сообщения, очередь, GDI. У него все это изложено в более краткой форме. Также сам Щупак пишет что он учился по книгам именно Юаня, Рихтера, и других.  Я часто замечал почти буквальное повторение частей текста из книг Юаня, Рихтера, в его книге.

     

    А у тебя программа может определить любой город на планете? И даже если это небольшая деревня? Сколько времени нужно программе чтобы определить название города?

  7. 4 часа назад, Xipho сказал:

    Опасный подход. Если вдруг понадобится использовать один класс в параллель (несколько потоков), словишь очень неприятные побочные эффекты.

    Пока что библиотека работает на одном основном потоке. И если этот поток будет занят, то обновления интерфейса не будет. Это минус конечно.

     

    4 часа назад, Xipho сказал:

    Ведь по ходу конвертации тебе надо будет давать возможность пользователю обновлять графический интерфейс, для отображения, например, прогресса конвертации.

    Ранее в книге Щупака я читал про способ с PeekMessage. Думаю буду использовать его.

     

    4 часа назад, Xipho сказал:

    Опасный подход. Если вдруг понадобится использовать один класс в параллель (несколько потоков), словишь очень неприятные побочные эффекты.

    Это да, тут нужно быть внимательным.

     

    4 часа назад, Xipho сказал:

    Решена ли в твоих классах проблема состояния гонки (race condition)?

    Особо я пока не задумывался над этим.

    Многопоточность я проходил, когда читал книгу Щупака и тестировал примеры. Но в библиотеке она пока не реализована. Но в планах и во внимании она есть.

     

    Мне кажется что на разработку этой библиотеки у меня уйдет не один год. Но в начале я полагал что это лишь на несколько месяцев. Постоянные переделки, улучшения. Но мне это нравится, поэтому не тягостно.

  8. 27 минут назад, Xipho сказал:

    Политика такая. Кейс, в котором это используется, должен работать максимально быстро, и не зависеть от внешних факторов.

    Максимально быстро это круто. Я тоже добиваюсь максимально быстрой работы библиотеки. Вчера разработал схему, при которой обновляются только те окна и разделители, которые изменили свои размер или положение. Проверяется это прежним и текущим положением, но без вызовов и запросов текущей позиции. Никаких GetClientRect/GetWindowRect для любого окна. Только один раз, для главного. Дальше простая математика.

    От этого теперь нет ни единого лага.

    Никаких лишних вызовов функций или методов, там где это возможно. Никаких лишних выделений и освобождений памяти под локальные переменные. Лучше добавлю в класс переменную, чем в метод.

     

    • Плюс 1
  9. 2 часа назад, Xipho сказал:

    На мой взгляд, куда интереснее задача с определением местоположения человека по его координатам. Только не точное до метра, а, например, определение города, в котором сейчас человек находится. По его координатам, конечно. Без использования внешних сервисов, разумеется )) Я сейчас над такой задачей работаю. Показать решение, увы, не вправе, но задачка оказалась довольно интересная.

    Звучит интересно. Но почему нельзя использовать внешние сервисы? Чтобы приложение могло работать без интернета?

    Первое что мне приходит на ум, для решения такой задачи нужна база данных.

    Это как с окнами, например, нужно определить находится ли курсор мыши в области какого-либо определенного окна. Или над каким окном находится сейчас курсор.

    Так как территория города не имеет четкой прямоугольной формы, то полагаю ее нужно разбить на множество небольших прямоугольников, и по ним сравнивать.

    Нужна база данных этих прямоугольников, и к каким городам они относятся.

  10. В библиотеке что я указал выше, есть возможность использовать MDI интерфейс. Я смотрел как-то ранее на MSDN его суть, - не приглянулся.

    Стоит ли вообще пытаться его реализовать? Если в той библиотеке какую я хочу написать, будет Dockind система, с опцией Tabbed document interface. Также возможно и Collapsible система. Словом если будет IDE-style interface.

    Не пойму зачем автор вышеупомянутой библиотеки его реализовал. Чтобы просто был, или и правда есть в нем какое-то удобство. Честно говоря, я прочитал все MSDN руководство по MDI, и не увидел каких-либо особых фишек. Все тоже самое, кажется, можно сделать и с SDI, не говоря уже о IDE стиле.

  11. Доделал перемещение полосок-разделителей. Все хорошо, но видны лаги, небольшие.

    Скачал DXDO, для WPF, для создания Docking, создал такое же количество окон, - лагов нет. Как понимаю, это потому что WPF отрисовывает через DirectX.

    Нашел интересную C++ GUI библиотеку, в которой есть Docking. Она не на DirectX. У нее тоже заметно небольшие лаги. Понятно, лучше уже не сделать значит.

    После того как увидел что кто-то уже сделал подобную моей библиотеку, желание доделывать свою, никудышную, немало так угасло. Но ничего, продолжу.

  12. 1 час назад, KRYPTOPUNK сказал:

    У меня есть система UI, которая считает свои размеры относительно родительского "окна" в процентах на каждом "эвенте" OnResize
    Правда там система скорее как в вебе, где отношения размеров "родитель"->"потомок" указывается в процентном соотношении. 
    Так вот, на каждый ресайз вызывается OnResize для каждого элемента системы и он пересчитывает свои размеры на основе размеров родителя и своих отступов от границ родительского окна. 
    Удобно, достаточно, и не сильно сложно по big-O. 

    И да, там уже можно не строить дерево руками, а у каждого элемента просто хранить вектор с детьми проходясь range-based циклом

    Это Anchor система? У тебя есть видео работы этой системы? Или может скрины, нескольких этапов.

  13. 5 часов назад, Xipho сказал:

    Вдобавок к паттернам обязательно читать дядюшку Боба (Роберт Мартин) "Чистый код" (примеры на Java, но суть уловить несложно), и его же "Чистая архитектура" (книга более-менее абстрактная без привязок к коду). 

    Прямо настоятельно рекомендую отложить код в сторону до полного прочтения книг, которые предлагал выше и предлагаю сейчас.

    У меня на очереди была книга Рихтера. Добавлю и эти к ней. Спасибо.

    Не могу отложить код, он не завершен. Доведу до рабочего состояния только Docking, и приступлю к книгам.

  14. 1 час назад, Xipho сказал:

    1. Ты размазываешь ответственности, то есть, у тебя "родители" делают работу, которую не должны делать (Нарушение принципа единственной ответственности)

    2. Ты открываешь детали объекта наружу - родительский объект ничего не должен знать о размера потомка. Размеры потомка - дело самого потомка  (Нарушение принципа инкапсуляции  + изменение состояния объекта извне - антипаттерн)

    3. Ты усложняешь систему там, где в этом нет необходимости (нарушение принципа KISS - keep it simple, stupid)

    4. Сложность твоего алгоритма в лучшем случае будет O(n), в худшем, даже без использования рекурсии может стремиться к O(n!). Тут, правда, это не сильно актуально, поскольку у дочерних окон будет все равно не больше 3-4 уровней вложенности (иначе это уже ошибка UI дизайна)

     

    Это то, что сходу в голову пришло. Уверен, если ты мне дашь свой код на ревью, я еще с десяток пунктов найду.

     

    Похоже я снова напутал. На самом деле у меня родитель только перебирает своих потомков, а они уже сами изменяют свои свойства, - размер и положение. Примерно так

    currChildCnt = parent->childT;
    while (currChildCnt)
    {
    	currChildCnt->bottomDiv->Top = ...
    	currChildCnt = currChildCnt->brotherAtRight;
    }

    Я часто путаюсь, извиняюсь.

  15. 3 часа назад, Xipho сказал:

    Еще раз. Обновление размеров дочерних окон - не ответственность главного (родительского для любого уровня) окна. Его ответственность - сгенерировать событие изменения размера. А те, кто в этом заинтересован - они должны это событие мониторить.

    Кажется теперь я понял, что ты имеешь ввиду.

    Действительно, у меня сейчас не так, как ты пишешь. У меня окно-родитель/контейнер-родитель, после изменения своего размера, начинает перебирать всех своих личных потомков (непосредственных), корректируя их размеры, а те, после корректировки, делают тоже самое со своими личными потомками, и так до самого последнего потомка, для этой ветви.

    Как мне кажется, по сути это ни чем не отличается от твоего варианта. У тебя сами личные потомки должны как-то отреагировать на событие, причем они должны соблюсти еще и последовательность, - вначале самый крайний изменяет свой размер, затем по очереди соседи, так как сосед справа равняется на соседа слева.

    При моем же способе, родитель просто перебирает своих личных потомком, начиная с крайнего, - никаких забот со стороны личных потомков о последовательности нет.

    Что-то вроде урока физкультуры, - учитель говорит "равняйсь", а ученики по очереди начинают равняться друг на друга.

    Что плохого в этом моей способе?

     

  16. 45 минут назад, Xipho сказал:

    При изменении одного окна (сплиттер, кстати, тоже можно считать окном, просто специфичным, что облегчит задачу)

    Вначале я делал сплиттер из статического текстового контрола, перехватывая WM_PAINT и заполняя цветом фон. Но, способ оказался глючным. Неполное закрашивание.

    Сейчас сплиттеры это простые окошки, как ты пишешь.

    45 минут назад, Xipho сказал:

     перебирать всё дерево - это стопроцентный оверинжиниринг.

    Вообще да, лишние телодвижения ни к чему. Сейчас полный перебор дерева происходит только при изменении размеров главного окна, когда в любом случае все окна и контейнеры нужно как-то корректировать. При смещениях полосок, - я пока не знаю как это все будет. Я только сейчас начал заниматься ими. Надеюсь будет достаточно изменить только размеры нижележащих потомков.

    Все упирается в эти полоски, точнее в эти относительные размеры потомков к их родителю.

    Кстати Imgui в этом плане не следит за оставшимися промежутками между окнами при полном их сжатии. Видны разные по ширине промежутки, это некрасиво. Также в Imgui неудобно изменять размеры окон при перетаскивании полосок. В VS при перемещении полоски родителя, вообще все потомки также смещаются, и это удобно, в Imgui же не так, в Imgu изменяется размер только соседнего окна, и при вставке нового окна с внешней стороны, то есть в главное окно, приходится каждое окно обратно возвращать на свое место, вместо того чтобы взять и один раз сместить полоску нового вставленного окна. Не знаю, зачем он так сделал.

  17. 2 часа назад, Xipho сказал:

    Ну вот это, уж извини, полная фигня. Родительское окно должно давать сигнал об изменении своих размеров только дочерним окнам первого уровня. А они уже, при изменении своих - своим дочерним при изменении своего размера.

    У меня почти так.

    Каждая полоса-разделитель имеет в своем классе указатель на то окно, с которым она ассоциирована. А окно в свою очередь, имеет указатель на контейнер, с которым оно ассоциировано.

    При смещении полосы, изменяется размер контейнера, а затем, по всем правилам, нужно было бы подкорректировать размеры всех нижележащих контейнеров-потомков, как ты пишешь. Но, у меня не так, в моем случае я беру самый корень дерева, и корректирую размеры вообще всех контейнеров. Это конечно не правильно, это излишне. Но я так делаю вот по какой причине.

    Спойлер

    spacer.png

    После сжатия, контейнер 2 будет сжат не до конца, так как он имеет больший шаг/относительную степень своего размера по отношению к родительскому. Для того чтобы этого не было, чтобы все контейнеры сжимались до конца, мне приходится вызывать функцию выравнивания для всего дерева вообще.

     

    Я нигде не смог найти алгоритма, по которому можно сделать Docking систему, вообще ноль информации. Есть уже готовая система и исходники, для Imgui, но во первых она работает коряво, мне не нравится поведение окон, во вторых я не могу разобраться в коде, что там и к чему.

    Есть еще своя родная Docking система в Imgui, но там я тоже не могу разобраться в коде, что там и к чему.

    Мне проще было придумать и написать свою, двигая и анализирую окна в VS.

    Про шаблон подписки я прочитал, видел и код примера.

     

    Что по поводу рекурсивной функции, для задачи поста? Есть ли возможность использовать ее, или оставить очередь?

     

  18. 2 часа назад, Xipho сказал:

    Мне кажется, у тебя неверное представление. Каждый узел дерева может иметь любое количество потомков, каждый из которых тоже является узлом дерева.

    Я опять извиняюсь за свою косноязычность. Я неправильно нарисовал схему. Я не написал что братья сами также являются потомками.

    Просто метод построения произвольного дерева, согласно этому источнику, - у узла-родителя есть один потомок, самый левый, а у этого самого левого потомка, братья. Эти браться являются также потомками этого родителя, но связь с родителем идет через самого левого потомка.

     

    Вот, а у меня, два потомка, "самых левых", у родителя, с братьями у каждого.

    А так, если вернуться к задаче этого поста, то нужно взять дерево, и для всех узлов-родителей, найти количество их потомков.

    Спойлер

    spacer.png

    Вот что в ответе должно быть:

    - Узел 1, имеет 4 узла М и 3 узла Ж

    - Узел 2, имеет 1 узел М и 1 узел Ж

    - Узел 3, имеет 1 узел М

    - Узел 4, имеет 1 узел М и 1 узел Ж

     

    2 часа назад, Xipho сказал:

    И я немного не понимаю, зачем тебе такая сложность с Docking системой. Закрадывается ощущение, что ты занимаешься каким-то диким оверинжинирингом. 

    Самому тяжело это все реализовывать, сам удивляюсь этой сложности. Но как я писал ранее, слишком много разных взаимодействий и влияний одних окон на другие, при вставке, изменении размера главного окна. Чтобы сохранялись/восстанавливались пропорции. Пока я не начинал писать код, мне тоже казалось что будет легко. Но на самом деле, там все по хитрому.

    На видео видно что вначале уменьшаются внутренние контейнеры. Причем нужно учитывать сколько потомков ниже, чтобы оставить место именно для стольких полос-разделителей, сколько их находится ниже, в дереве.

    Спойлер

     

     

  19. 1 час назад, Xipho сказал:

    Вообще не понял. В общем, распиши постановку задачи нормально, так будет куда проще понять. Например, чем отличается в твоем дереве потомок от брата. Имеется в виду, концептуально.

    Суть задачи в том, чтобы после добавления в дерево нового узла, обновить для каждого родителя в этом дереве количество его потомков.

    Вот схема обычного произвольного дерева, в моем представлении

    Спойлер

    spacer.png

    На этой схеме, у родителя может быть только один потомок. Родитель и потомок могут иметь своих братьев. Тот потомок или брат, у которого есть свой потомок, является также и родителем.

     

    Но в моем случае, мне пришлось сделать так, что у родителя может быть и два потомка (максимум). Более того, родитель у меня может быть двух видов, V и H.

    - У родителя V могут быть только потомки типа L и R.

    - У родителя H могут быть только потомки типа T и B.

    - У родителя L или R могут быть только потомки типа L.

    - У родителя T или B могут быть только потомки типа T.

    Это так я составил схему, алгоритм, по которым работает Docking система

    Спойлер

    spacer.png

     

    Когда я добавляю или удаляю окно в Docking систему, мне нужно пересчитывать количество потомков для всех узлов-родителей во всем дереве.

    Сейчас перерасчет работает при помощи очереди. Особо не видно чтобы тормозило, но не знаю, может с рекурсией будет и быстрее.

    А так, все работает как надо. Остались мелкие доработки. Рисовать заголовки у окон, кнопки на них, и т.д.

    Спойлер

    spacer.png

     

  20. 1 час назад, Xipho сказал:

    Males - это узлы типа М, females  - Ж. Код написан так, чтобы считать потомком у узла, у которого запрошен метод printCounts

    Получается что каждый узел-родитель должен вызвать для себя рекурсивную функцию подсчета собственных потомков? Каждый раз обходя дерево, начиная со своего личного уровня и до самого нижнего?

    Я никак не пойму тот код на Kotlin, потому не знаю даже того алгоритма. Но если так, как я написал выше, то не получится ли что некоторые узлы будут посещаться более одного раза?

    Спойлер

    spacer.png

    P - это узел-родитель. Ветвь на рисунке слева, где 35 сравнений, это при рекурсии. Где 10 сравнений, это при использовании очереди, без рекурсии.

    35 или 10 сравнений, это сравнения "if", на то какого типа данный узел.

     

    Помимо всего прочего, при рекурсии, нужно же еще обойти дерево для определения кто есть узел-потомок, чтобы вызвать для него/них рекурсивный подсчет?

     

    1 час назад, Xipho сказал:

    Не совсем понял, почему. Что, например, в коллекции из двух элементов не может выступать в качестве "один потомок и один брат"?

    Я не совсем понимаю что такое коллекция. Это контейнер с одним потомком и одним братом? Если так, то у меня ситуация немного иная, - у меня у родителя может быть два разных потомка, у которых имеются свои братья, числом более одного.

    Например, родитель имеет потомка childL и childR, но childL и childR между собой не братья, хотя у них и один родитель. У childL и у childR есть свои братья, много братьев. И childL и childR и их братья, сами могут быть для кого-то родителями. И так далее.

  21. 2 часа назад, Xipho сказал:

    А можешь привести ссылку на эту часть текста, где подобное утверждается? Потому что сомнительно, что вдруг какой-то вид дерева невозможно обойти с помощью рекурсии.

    Не могу найти, но помню, что натыкался на это. Я сразу стал искать примеры обхода в ширину с помощью очереди. Вот сайт, который я добавил в закладки.

    Спойлер

     

    2 часа назад, Xipho сказал:

    Тут, кстати, не сказано, сами узлы пропускаем, а их дочерние элементы считаем, или тоже пропускаем?

    Да, извиняюсь, тоже заметил, выразился неправильно.

    Нужно для всякого узла, который является родителем, получить количество его потомков, М и Ж. Узел типа ДЖ, под номером 4,

    является родителем, и для него, как и для других не ДЖ родителей, тоже нужно получить количество своих потомков.

    17 минут назад, Xipho сказал:

    Кстати, если дочерние узлы объединить в коллекцию, а не как сейчас (у ноды один потомок и один брат), то рекурсивный вызов будет один, а эффект не изменится. Это как вариант

    Один потомок и один брат в моем случае не сработает.

    2 часа назад, Xipho сказал:

    Не уверен, что правильно понял задачу, конечно, но набросал на коленке решение с рекурсией. Язык - котлин, но разобраться и перенести на плюсы не составит труда. Есть некоторая избыточность в классах, но это для читаемости, чтобы было понимание, почему тот или иной счетчик увеличивается. Код под спойлером, и будет ссылка на плейграунд, чтобы поиграться с кодом

    Спасибо, попробую сейчас разобраться.

    *********************************************************

     

    Сложно, незнакомый язык Kotlin.

    Там на сайте на выходе -Males: 3, females: 2. Я думал будет что-то вроде:

    - Узел 1, имеет 4 узла М и 3 узла Ж

    - Узел 2, имеет 1 узел М и 1 узел Ж

  22. 6 часов назад, Xipho сказал:

    Зачем такие сложности? Чем рекурсия не угодила?

    Рекурсия в этом случае не работает, как я понял. Здесь произвольное дерево, которое нужно обойти в ширину.

    С помощью рекурсии обход в ширину возможен только для бинарных деревьев. Так я читал в книге.

    А так, если и правда есть способ с рекурсией, я бы с большим интересом его посмотрел.

  23. Получилось таким способом

    Спойлер
    #include <windows.h>
    #include <iostream>
    #include <string>
    #include <queue>
    
    
    enum class Type : INT { Man, Woman, Pet };
    
    class Container 
    {
    public:
        Container* child{ nullptr };
        Container* brotherAtRight{ nullptr };
        INT index{ 0 };
        INT manCount{ 0 };
        Type type{ Type::Man };
        INT womanCount{ 0 };
    };
    
    int main()
    {
    	//Create a tree.
        //1
        Container* rootCnt = new Container();
        rootCnt->type = Type::Man;
        rootCnt->index = 1;
        //2
        rootCnt->child = new Container();;
        rootCnt->child->type = Type::Man;
        rootCnt->child->index = 2;
        //3
        rootCnt->child->brotherAtRight = new Container();
        rootCnt->child->brotherAtRight->type = Type::Woman;
        rootCnt->child->brotherAtRight->index = 3;
        //4
        rootCnt->child->brotherAtRight->brotherAtRight = new Container();
        rootCnt->child->brotherAtRight->brotherAtRight->type = Type::Pet;
        rootCnt->child->brotherAtRight->brotherAtRight->index = 4;
        //5
        rootCnt->child->child = new Container();
        rootCnt->child->child->type = Type::Man;
        rootCnt->child->child->index = 5;
        //6
        rootCnt->child->child->brotherAtRight = new Container();
        rootCnt->child->child->brotherAtRight->type = Type::Woman;
        rootCnt->child->child->brotherAtRight->index = 6;
        //7
        rootCnt->child->child->brotherAtRight->brotherAtRight = new Container();
        rootCnt->child->child->brotherAtRight->brotherAtRight->type = Type::Pet;
        rootCnt->child->child->brotherAtRight->brotherAtRight->index = 7;
        //8
        rootCnt->child->brotherAtRight->child = new Container();
        rootCnt->child->brotherAtRight->child->type = Type::Man;
        rootCnt->child->brotherAtRight->child->index = 8;
        //9
        rootCnt->child->brotherAtRight->brotherAtRight->child = new Container();
        rootCnt->child->brotherAtRight->brotherAtRight->child->type = Type::Man;
        rootCnt->child->brotherAtRight->brotherAtRight->child->index = 9;
        //10
        rootCnt->child->brotherAtRight->brotherAtRight->child->brotherAtRight = new Container();
        rootCnt->child->brotherAtRight->brotherAtRight->child->brotherAtRight->type = Type::Woman;
        rootCnt->child->brotherAtRight->brotherAtRight->child->brotherAtRight->index = 10;
    
    	Container* currCnt = nullptr;
        Container* currChild = nullptr;
        std::deque<Container*> queue;
        DWORD_PTR nextItem = 0;
        DWORD_PTR maxItems = 0;
    
        //Disassemble a tree.
    	queue.push_back(rootCnt);
        currCnt = queue.front();
        while (TRUE) 
        {
            if (currCnt->child)
            {
                currChild = currCnt->child;
                while (currChild)
                {
                    queue.push_back(currChild);
                    currChild = currChild->brotherAtRight;
                    maxItems++;
                }
            }
            currCnt = queue[++nextItem];
            if (nextItem == maxItems)
                break;
        }
    
        //Get counts.
        currCnt = queue.back();
        while (TRUE)
        {
            if (currCnt->child)
            {
                currChild = currCnt->child;
                while (currChild)
                {
                    if (currChild->type == Type::Man)
                        currCnt->manCount++;
                    if (currChild->type == Type::Woman)
                        currCnt->womanCount++;
                    currCnt->manCount += currChild->manCount;
                    currCnt->womanCount += currChild->womanCount;
                    currChild = currChild->brotherAtRight;
                }
                if (currCnt->type != Type::Pet)
                {
                    std::wcout << "index = " << currCnt->index << "\n";
                    std::wcout << "manCount = " << currCnt->manCount << "\n";
                    std::wcout << "womanCount = " << currCnt->womanCount << "\n";
                }
            }
            if (!maxItems)
                break;
            currCnt = queue[--maxItems];
        }
        return 0;
    }

     

    Вначале создается дерево, затем это дерево преобразуется в одну одномерную очередь, начиная с самого корня. Далее, начиная с конца очереди (то есть что тоже, с самого глубокого уровня дерева), и в обратном порядке, проверяется каждый узел на наличие потомков, то есть определяется является ли текущий узел родителем. Если является, то для себя самого он подсчитывает количество своих личных потомков, а после, складывает количество своих личных потомков, с количеством личных потомков его личных потомков.

     

    Второе условие задачи, которое конкретно относится ко мне, - есть два вида мужского и женского родов (например до 30 и после 30 лет). Но считать их нужно за одно. То есть, и до 30, и после 30 лет, это один род, хотя и разные типы.

    Вот пример решения для этого условия

    Спойлер
    #include <windows.h>
    #include <iostream>
    #include <string>
    #include <queue>
    
    
    enum class Type : INT { Man1, Man2, Woman1, Woman2, Pet };
    enum class Relation : INT { Child, Brother };
    
    class Container 
    {
    public:
        Container* child{ nullptr };
        Container* brotherAtRight{ nullptr };
        INT index{ 0 };
        INT manCount{ 0 };
        Type type{ Type::Man1 };
        INT womanCount{ 0 };
    };
    
    VOID AddRelative(Container* _rootCnt, Relation _relation, INT _cntIndex, INT _newCntIndex, Type _newType)
    {
        Container* currCnt = nullptr;
        Container* currChild = nullptr;
        std::deque<Container*> queue;
        //View a tree.
        queue.push_back(_rootCnt);
        while (!queue.empty())
        {
            currCnt = queue.front();
            //---------------------------------------
            if (currCnt->index == _cntIndex)
            {
                if (_relation == Relation::Child)
                {
                    currCnt->child = new Container();
                    currCnt->child->index = _newCntIndex;
                    currCnt->child->type = _newType;
                }
                else
                {
                    currCnt->brotherAtRight = new Container();
                    currCnt->brotherAtRight->index = _newCntIndex;
                    currCnt->brotherAtRight->type = _newType;
                }
                break;
            }
            //---------------------------------------
            queue.pop_front();
            if (currCnt->child)
            {
                currChild = currCnt->child;
                while (currChild)
                {
                    queue.push_back(currChild);
                    currChild = currChild->brotherAtRight;
                }
            }
        }
    }
    
    VOID GetCounts(Container* _rootCnt)
    {
        Container* currCnt = nullptr;
        Container* currChild = nullptr;
        std::deque<Container*> queue;
        DWORD_PTR nextItem = 0;
        DWORD_PTR maxItems = 0;
    
        //Disassemble a tree.
        queue.push_back(_rootCnt);
        currCnt = queue.front();
        while (TRUE)
        {
            if (currCnt->child)
            {
                currChild = currCnt->child;
                while (currChild)
                {
                    queue.push_back(currChild);
                    currChild = currChild->brotherAtRight;
                    maxItems++;
                }
            }
            currCnt = queue[++nextItem];
            if (nextItem == maxItems) //If the counter (nextItem) of the next element reaches the end of the dynamic queue.
                break;
        }
    
        //Get counts.
        currCnt = queue.back();
        while (TRUE)
        {
            if (currCnt->child)
            {
                currChild = currCnt->child;
                while (currChild)
                {
                    if (currChild->type == Type::Man1 || currChild->type == Type::Man2)
                        currCnt->manCount++;
                    if (currChild->type == Type::Woman1 || currChild->type == Type::Woman2)
                        currCnt->womanCount++;
                    currCnt->manCount += currChild->manCount;
                    currCnt->womanCount += currChild->womanCount;
                    currChild = currChild->brotherAtRight;
                }
                if (currCnt->type != Type::Pet)
                {
                    std::wcout << "index = " << currCnt->index << "\n";
                    std::wcout << "manCount = " << currCnt->manCount << "\n";
                    std::wcout << "womanCount = " << currCnt->womanCount << "\n\n";
                }
            }
            if (!maxItems)
                break;
            currCnt = queue[--maxItems]; //First reduce the maxItems, then get the queue element.
        }
    }
    
    int main()
    {
    	//Create a tree.
        //1
        Container* rootCnt = new Container();
        rootCnt->type = Type::Man1;
        rootCnt->index = 1;
        //2
        AddRelative(rootCnt, Relation::Child, 1, 2, Type::Woman2);
        //3
        AddRelative(rootCnt, Relation::Brother, 2, 3, Type::Woman1);
        //4
        AddRelative(rootCnt, Relation::Brother, 3, 4, Type::Man2);
        //5
        AddRelative(rootCnt, Relation::Child, 2, 5, Type::Woman1);
        //6
        AddRelative(rootCnt, Relation::Brother, 5, 6, Type::Pet);
        //7
        AddRelative(rootCnt, Relation::Brother, 6, 7, Type::Man1);
        //8
        AddRelative(rootCnt, Relation::Child, 3, 8, Type::Man1);
        //9
        AddRelative(rootCnt, Relation::Child, 4, 9, Type::Pet);
        //10
        AddRelative(rootCnt, Relation::Brother, 9, 10, Type::Man2);
        //11
        AddRelative(rootCnt, Relation::Brother, 10, 11, Type::Man2);
        //12
        AddRelative(rootCnt, Relation::Child, 6, 12, Type::Man2);
        //13
        AddRelative(rootCnt, Relation::Child, 8, 13, Type::Woman2);
        //14
        AddRelative(rootCnt, Relation::Brother, 13, 14, Type::Woman2);
        //15
        AddRelative(rootCnt, Relation::Child, 12, 15, Type::Woman1);
        //16
        AddRelative(rootCnt, Relation::Brother, 15, 16, Type::Pet);
        //17
        AddRelative(rootCnt, Relation::Child, 14, 17, Type::Man1);
        //18
        AddRelative(rootCnt, Relation::Brother, 17, 18, Type::Woman1);
        //19
        AddRelative(rootCnt, Relation::Brother, 18, 19, Type::Pet);
        //20
        AddRelative(rootCnt, Relation::Child, 16, 20, Type::Woman1);
        //21
        AddRelative(rootCnt, Relation::Child, 18, 21, Type::Pet);
        //22
        AddRelative(rootCnt, Relation::Brother, 21, 22, Type::Man1);
        //23
        AddRelative(rootCnt, Relation::Child, 20, 23, Type::Man2);
        //24
        AddRelative(rootCnt, Relation::Brother, 23, 24, Type::Man1);
    
        //Get counts.
        GetCounts(rootCnt);
        return 0;
    }

     

    Здесь добавлена примитивная функция для создания дерева. Сама суть подсчета прежняя.

    Это дерево для второго условия

    Спойлер

    spacer.png

     

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.