1 Востаннє редагувалося taburyak (11.04.2017 11:30:01)

Тема: Мишкою оконтурити коло на PictureBox

Вітання.

Питання алгоритму, а не якихось особливостей C#, але проект на C#, тому вже тут спитаю.

Є вже наперед намальоване коло на picturebox. Потрібно оконтурити його іншим колом, яке малюється за допомоги руху миші. Не можу допетрати, як зробити, щоб коло, яке малюється мишкою, доторкалось своїм краєм до точки де початково натиснув кнопку миші (зараз точка початку це один з кутів квадрату в яке вписується коло). З-за цього не можна точно оконтурить. І ще одна проблема, щоб коло можна було починати малювати в будь якому напрямку, а не тільки в напрямку праворуч-вниз. Зараз більш-менш можна точно оконтурити коли тягнеш мишою праворуч-вниз, а рухом вверх доводити співпадіння кіл. Але думаю що я вигадую лісапєта і є якийсь вже відпрацьований алгоритм.

using System.Drawing;
using System.Windows.Forms;

namespace DrawRectangle1
{
    public partial class Form1 : Form
    {

        bool isClicked = false; // прапорець натискання кнопки миші

        int X = 0;
        int Y = 0;

        int X1 = 0;
        int Y1 = 0;

        public Form1()
        {
            InitializeComponent();
            pictureBox1.Cursor = Cursors.Cross; // в межах pictureBox курсор у вигляді перехрестя
            // Малюємо коло яке треба оконтурити
            Bitmap bitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            Graphics g = Graphics.FromImage(bitmap);
            g.FillEllipse(Brushes.Gray, 70, 70, 150, 150);
            pictureBox1.Image = bitmap;
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {            
            isClicked = true;

            X = e.X;
            Y = e.Y;

        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (isClicked)
            {
                X1 = e.X;
                Y1 = e.Y;
                pictureBox1.Invalidate(); //якщо кнопка затиснута то перемальовуємо 
            }            
        }

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            isClicked = false;            
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {            
            Pen pen = new Pen(Color.Red, 4);

            if (X > X1 && Y < Y1) // Рух ліворуч вниз
            {              
                e.Graphics.DrawEllipse(pen, X1, Y, System.Math.Abs(X1 - X), System.Math.Abs(X1 - X));
            } else if(X > X1 && Y > Y1) // Рух ліворуч вверх
            {              
                e.Graphics.DrawEllipse(pen, X1, Y1, System.Math.Abs(Y1 - Y), System.Math.Abs(Y1 - Y));
            } else if(X < X1 && Y < Y1) // Рух праворуч вниз
            {               
                e.Graphics.DrawEllipse(pen, X, Y, System.Math.Abs(X - X1), System.Math.Abs(X - X1));
            } else if(X < X1 && Y > Y1) // Рух праворуч вверх
            {
                e.Graphics.DrawEllipse(pen, X, Y1, System.Math.Abs(X - X1), System.Math.Abs(X - X1));
            }          
        }
    }
}

http://replace.org.ua/misc.php?action=pun_attachment&amp;item=1533

Post's attachments

приклад.png 24.56 kb, 245 downloads since 2017-04-11 

Подякували: 0xDADA11C71

2

Re: Мишкою оконтурити коло на PictureBox

Кілька разів перечитав Ваш опис і нічого не зрозумів, Ви б може додали картинку якусь?
Ще пропоную перемістити тему в розділ "Алгоритми і структури данних"

3

Re: Мишкою оконтурити коло на PictureBox

Перечитав теж. Все зрозуміло. Додав світлину в топ.

Подякували: 0xDADA11C71

4

Re: Мишкою оконтурити коло на PictureBox

Нащо тягнути? Достатньо одного кліку за межами кола, щоб визначити ширину, хіба ні?

5

Re: Мишкою оконтурити коло на PictureBox

Переформулюю: ви зараз будуєте "коло" (насправді еліпс) по двох вершинах описаного прямокутника зі сторонами, паралельними осям координат; а хочете будувати по таких самих вершинах вписаного (вони лежать якраз на краях еліпса) - але таким чином, щоб виходило коло, концентричне тому, що вже існує. Тобто програма має якимось чином знати, що там є коло з певними характеристиками, інакше вона не зможе зрозуміти, що синій прямокутник на моєму малюнку насправді треба підправити до чорного квадрата. Для цього потрібен векторний редактор, а не растровий.
https://replace.org.ua/extensions/om_images/img/59411c0e31344/2mgtr8l.png

Подякували: 0xDADA11C7, leofun012

6 Востаннє редагувалося taburyak (12.04.2017 07:04:38)

Re: Мишкою оконтурити коло на PictureBox

Так панове, трішки геометрії і алгебри і все порішав як треба:

private void PictureBox1_Paint(object sender, PaintEventArgs e)
        {            
            Pen pen = new Pen(Color.Red, 4);
            
            D = (int) System.Math.Sqrt(System.Math.Pow((X - X1), 2)+System.Math.Pow((Y - Y1), 2));
                                    
            e.Graphics.DrawEllipse(pen, (X + X1 - D) / 2, (Y + Y1 - D) / 2, D, D);            
        }

Малює саме так як треба. Тицяєш мишкою в будь яке місце краю сірого кола, тягнеш мишу до іншого краю і все чітко оконтурюється :)

Можна було б без змінної D, але все то перераховувати аж чотири рази, та й рядок малювання еліпсу виглядав би кошмарним.

7 Востаннє редагувалося taburyak (12.04.2017 16:23:37)

Re: Мишкою оконтурити коло на PictureBox

Так застряг на іншому. Цілий день не можу порішати. Навіть не розумію що у гугла питати.

Програма з топіку малює на bitmap сіре коло:

// Малюємо коло яке треба оконтурити
            Bitmap bitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            Graphics g = Graphics.FromImage(bitmap);
            g.FillEllipse(Brushes.Gray, 70, 70, 150, 150);
            pictureBox1.Image = bitmap;

А поверх зображення на графіці picturebox мишкою "натягую" червоне коло:

private void PictureBox1_Paint(object sender, PaintEventArgs e)
        {            
            Pen pen = new Pen(Color.Red, 4);
            
            D = (int) System.Math.Sqrt(System.Math.Pow((X - X1), 2)+System.Math.Pow((Y - Y1), 2));
                                    
            e.Graphics.DrawEllipse(pen, (X + X1 - D) / 2, (Y + Y1 - D) / 2, D, D);            
        }

А в події коли відпускаю кнопку хочу зберегти результат:

private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            isClicked = false;            
            PictureBox1.Image.Save("test.bmp");
        }

Але зберігається тільки сіре коло, а червоне пропадає. Воно то зрозуміло чого, бо в різних місцях мальовано. Але як мені при збереженні поєднати два кола щоб збережена картинка була такою як я бачу очима на екрані? Я вже дим з вух пускаю. Допоможіть.

На екрані так, сіре коло окунтурено червоним:
https://lh3.googleusercontent.com/GUm0aAT0igDNQ7B24HF8v-dGpB8XmT-5wDqbBs_eest7Iy7HAsBjx4VR10dRJRuFvwZVkUWUKiLnSEyaXMb1_n9XnmF1rY_h85bGpJDqScZPj-pEEdFllflKqOipnIYmN2mp8bsNUumdZ2YCPnbbXkpoy6qPF4qRApXFAX9GoQ4-de79Z2FR6L5dtwCrSVvmdJvg79DDvaA9k1v-lw8NvYW5oLdP4halsWk3a42S6y9XNul7NrGG_JKIgNrXkZYnxFCMcEiY8qdwqLDyGABg3qcLW1a5_VQmdAHvoVaKzs-QEMIg6V8ezhSgi56zqafX7z69xZZZ_nRRFIfCwnxF3-eC527OGxHJMqQVjLQt7v5xLrlyudDWWUjklEC3MjG_b-KValWqTGHzCMpKci4xsNApcmI0wLBnXQSYFXe-nywIITIJC_zbPSe960xxygUEof0zUZtjnuibHU5_o3Red4usK3nGHmLSfqPD0i9dQ3ilA1b9yRe-O8ymoaCyLqfGIzxWuJjapKis0-kgeQ64xWweIRGxCIj_SJK9Bgd1-iL5qo4FrEiHvDgS-C-ySBI6InssM5YIa_LBbmlR8J6vpuUcJBJOF1uhbcVdP9ezzgXOLehbaw=w451-h436-no

А збережений файл вже не має червоного кола, а треба щоб мав:
https://lh3.googleusercontent.com/xcYB8bojGsltts6PyXy6ciAXzlSFKuI6IUwzwkI9SP64NY5FWKvQAlmYtTcqXzyM3SVMhTjUejDaZnx4y6o2L90vzPj13Nx3V5x16w54xCzgfKEADrqItlBE5OYFHBt1H6CxFPMlLnqhBwD8_SGwndEOcpwudHHp7mOZ7FLNx5G1RRxvZWEmFx8fk5CKKa6sgTe5a7WIo1jr2BXQWTlaOLgR18xrdVztHqtu9EO-xK16cP828yV6VM7GbzWfJD2GtZhbPUM6xpfLgfC7J-BZoSE690M3m5tNEXA-1UFXtYHNijuQyBJ2hTSmHZn8_O6995525KwnukhjW9-C6P-4kFGtyM-T8XgTjl6mOkYfdUIllfmxx7T-PZW-AAzZveNuVCUdgtYy8LUFnN61X9VhPylayo_aQ2Nr83dvcGZwdG9ofqNhViYF7Y0rVjfjWzUAcDROBt_9gslCu1ct31beZJRTsK7P-M2Ftfc2-lGMwYgqeec7u4GXM_tRFiIXzGgkB9l0z-ns3NnelI2EUJKLaldNB1RumtD-_QQ23bLXH-Iy0oktCGGnF0kdoFaZn8eLJ801gqx0hG50B6LxHwwJMI2xbkYtk9CC9dtmRDobfhRzMqaUSw=w425-h380-no

8 Востаннє редагувалося koala (12.04.2017 16:44:28)

Re: Мишкою оконтурити коло на PictureBox

taburyak написав:

Так панове, трішки геометрії і алгебри і все порішав як треба:

Головне - що воно вас задовільняє. Може, і дійсно шукати протилежну точку зручно.

taburyak написав:

Так застряг на іншому.

Сіре коло ви малюєте на Bitmap-і, який записуєте в pictureBox1.Image. А червоне - на контексті, переданому в PaintEventArgs.Graphics, який ніде не зберігається. Просто малюйте у тому ж Bitmap-і, десь так:

Graphics g = Graphics.FromImage(pictureBox1.Image);
g.DrawEllipse(pen, (X + X1 - D) / 2, (Y + Y1 - D) / 2, D, D);  

Звісно, це треба робити тільки при відпусканні мишки, інакше попередні кола не будуть видалені. Коли тягнете - малюйте по переданому контексту.

9 Востаннє редагувалося taburyak (12.04.2017 20:37:39)

Re: Мишкою оконтурити коло на PictureBox

koala, дякую за цікавість до теми.
Так, як ви пропонуєте, я пробував. Все б підійшло б, але, як завжди, є одне але. Image що на picturebox'і, наприклад має розмір 1600×1200 pixel і властивості zoom, а розмір того ж picturebox 800×600. І тут починається проблеми з маштабуванням. Які я так і  не зміг  подолати. Велика похибка виходить і коло при відпусканні кнопки миші не зовсім співпадає.
Мабуть підійшов би такий варіант, щоб типу скріншота з екрану знімалось саме з області picturebox'у. Але я не знаю як це робиться.

10

Re: Мишкою оконтурити коло на PictureBox

Там все рівно 2:1, в чому ж проблема з масштабуванням? І чому не можна робити його одразу 800x600?
Зображення контролу можна отримати за допомогою DrawToBitmap. Але тоді не розумію, в чому сенс того PictureBox-у. І взагалі у чому сенс цієї діяльності (програмно намалювати коло, дати користувачеві намалювати якомога точніше інше коло, а потім це разом зберегти)? Чому не можна все малювати програмно чи намалювати тільки один раз?

Подякували: leofun011

11 Востаннє редагувалося taburyak (13.04.2017 13:57:17)

Re: Мишкою оконтурити коло на PictureBox

Та ну це ж тіко приклад для розібратись.

Сам проект з мікроскопа ловить відос. Направляєш на відбиток від кулі (не та шо з пістоля  *STOP* ). Робиш снапшот і прога вимірює діаметр відбитку. З цим проблем немає, бо визначення діаметру відбитку відбувається з оригіналом кадру з мікроскопа, а потім до зображення відбитку домальовується коло, шкала маштабу і вже оброблене зображення кидаю на picturebox з режимом zoom, де при будь якому розмірі вікна зберігаються пропорції і все таке. Та ця ж картинка додається до таблиці з даними про виміри.

А от в ручному режимі не все так просто, бо оригінал зображення кидається на picturebox, а мишою соваю (оконтурюю), для визначення диаметру кулі, вже поверх зображення яке я бачу очима на моніторі. Не над оригіналом зображення ходить курсор миші, а над вже ресайзаним зображенням яке знаходиться на пікчуребоксі. А розмір форми і відповідно picturebox може бути будь яким тому мальоване мишою коло не співпадає з колом на оригіналі. Маштабувати пробував - похибка велика (не співпадає).

Зараз знімаю прінтскрін з picturebox:

 Bitmap image = new Bitmap(snapshotPictureBox.ClientSize.Width, snapshotPictureBox.ClientSize.Height);

            System.Drawing.Point location = snapshotPictureBox.PointToScreen(System.Drawing.Point.Empty);

            using (Graphics g = Graphics.FromImage(image))
            {
                g.CopyFromScreen(location, System.Drawing.Point.Empty, image.Size);
            }

            snapshotPictureBox.Image = image;

Все добре коли пропорції картинки співпають з пропорціями picturebox. Як не співпадає, то скрін picturebox виходить з пустими полями по горизонталі чи вертикалі (image в режимі zoom для збереження пропорцій), бо прінтскріниться весь picturebox а не виключно image.

І що робить поки не придумав. picturebox лежить на одній з cell tablelayoutpanel може якось можна примусити цю ячейку тримати пропорції?

12

Re: Мишкою оконтурити коло на PictureBox

Є думка, що перед тим, як оригінал картинки кидати на picturebox, його ресайзонути до розмірів того ж picturebox, щоб співпадало все. А вже коли мишою на контексті намалювати червоне коло, то по завершенню просто домалювати на бітмапі вже поверх ресайзеного зображення коло з такими ж параметрами як на контексті. Має ж співпасти? Але тоді при кожній зміні розміру вікна потрібно знову брати оригінал, ресайзити під нові габарити picturebox.

13

Re: Мишкою оконтурити коло на PictureBox

Поговорю ще сам з собою  *CRAZY*

В мене на формі лежить tablelayoutpanel ( dock = fill) з двома колонками і одним рядком. В одній з комірок лежить той picturebox, який має зберігати пропорцію 4:3. А інша комірка має фіксовану ширину. Мабуть би мені підійшла така магія, що коли міняєш розмір форми то вона міняється таким чином щоб комірка з picturebox мала завжди пропорцію 4:3. Міняєш висоту форми - ширина підлаштовується, міняєш ширину форми - висота підлаштовується.