Смещение линии вытягивания на обоих концах

#c# #winforms #drawing

#c# #winforms #рисование

Вопрос:

Я пытаюсь нарисовать пунктирные линии, используя этот DrawLine метод. Однако это добавляет небольшое смещение к концам, что делает линии уродливыми. Как можно предотвратить добавление смещения к концам?

 protected override void OnPaint (PaintEventArgs e) {
   Graphics g = e.Graphics;
   g.DrawLine (Dot, new Point (10, 50), new Point (70, 50));
   g.DrawLine (Dot, new Point (70, 50), new Point (70, 100));
}

Pen Dot = new Pen (Brushes.Black, 2) { DashStyle = DashStyle.Dot };
  

Вывод

введите описание изображения здесь

Предполагаемый результат

введите описание изображения здесь

Комментарии:

1. Вот увеличенный результат рисования . Что не так с чертежом и чего вы ожидаете? (Пунктирная линия — это исходный пунктирный рисунок, а красная линия показывает поле, которое вы использовали для рисования.

2. @RezaAghaei Почему существует смещение? Технически линии должны совпадать, поскольку конечная точка первой строки и начальная точка второй строки одинаковы.

3. Вы можете использовать связанное изображение и показать неожиданную часть на изображении. Также покажите, каков ожидаемый результат. Я не мог понять, какое смещение вы упомянули. Это пунктирная линия шириной 2, как она может быть другой.

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

5. Я думаю, что я, наконец, справился с этим, пожалуйста, попробуйте улучшенную версию!

Ответ №1:

Цель проста и правдоподобна:

  • Мы хотим, чтобы наши строки начинались с точки.

И правила игры также довольно просты:

  • И точки, и пробелы по умолчанию являются квадратами.
  • Все линии рисуются с PenAlignment.Center помощью .

К сожалению, последствия комбинации довольно сложны, так что потерпите меня…

Давайте сначала посмотрим на 1-е правило; давайте проигнорируем другое DashStyles и останемся DashStyle.Dot . По умолчанию каждая точка и каждый пробел имеют Pen.Width пиксели для обеих сторон. Это приводит прямо к первым проблемам:

  • Если ширина наших линий не может быть разделена на, у Pen.Width нас проблемы.
  • Чтобы начинаться и заканчиваться точкой, мы хотим иметь n точки и n-1 пробелы.

Это еще не все, но давайте рассмотрим 2-е правило; чтобы проиллюстрировать это, я нарисовал это увеличенное в 10 раз изображение:

введите описание изображения здесь

Это код, который создал цветные части:

 g.FillRectangle(Brushes.Yellow, 15, 15, 10, 10);
g.DrawRectangle(Pens.Orange, 10, 10, 10, 10);
g.DrawLine(Pens.OrangeRed, 10, 5, 40, 5);
using (Pen pen = new Pen(Color.Red, 2f) { DashStyle = DashStyle.Dot })
    g.DrawLine(pen, 10, 30, 48, 30);
using (Pen pen = new Pen(Color.Crimson, 2f))
    g.DrawLine(pen, 10, 40, 48, 40);
using (Pen pen = new Pen(Color.DarkMagenta, 3f))
    g.DrawLine(pen, 10, 50, 48, 50);
  

Посмотрите внимательно, чтобы увидеть, как рисуются линии!

(В сторону: вы также можете посмотреть разницу DrawRectangle и FillRectangle !)

  • Горизонтальные линии начинаются и заканчиваются в правильных координатах, но они расширяются либо вниз (если их перо.Ширина = 1) или выше и ниже y-координаты.
  • Конечно, вертикальные линии будут делать то же самое.

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

Итак, что мы можем сделать? Я не думаю, что a DashOffset может помочь. Но есть еще один вариант настройки a Pen : мы можем настроить его DashPattern на использование пользовательских значений.

Нам нужны два значения floats , содержащие масштабирование для точек и пробелов. По умолчанию оба значения равны 1f . Я решил сохранить квадратные точки и изменить только пробелы. Вот функция, которая решает проблему с помощью

  • увеличение ширины линии на половину ширины пера с обеих сторон, чтобы внешние края встретились
  • расширение пробелов по мере необходимости, чтобы соответствовать длине линии

Вот функция рисования линии; она принимает Graphics объект, a Pen , два конца Points и a byte , которые сообщают нам, предназначена ли линия отдельно или будет соединяться с другими линиями, как в наших примерах.

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

Значения пропуска — 0 пропустить none, 1 or 2 пропустить 1-ю или последнюю точку и 3 пропустить оба. Конечно, вы можете enumeration вместо этого использовать.

 void DrawDottedLine(Graphics g, Pen pen_, Point pa_, Point pb, byte skipDots)
{
    float pw = pen_.Width;
    float pw2 = pen_.Width / 2f;
    Pen pen = (Pen)pen_.Clone();
    // find out directions:
    int sigX = Math.Sign(pb_.X - pa_.X);
    int sigY = Math.Sign(pb_.Y - pa_.Y);
    // move the end points out a bit:
    PointF pa = new PointF(pa_.X - pw2 * sigX, pa_.Y - pw2 * sigY);
    PointF pb = new PointF(pb_.X   pw2 * sigX, pb_.Y   pw2 * sigY);
    // find line new length:
    float lw = (float)(Math.Abs(pb.X - pa.X));
    float lh = (float)(Math.Abs(pb.Y - pa.Y));
    float ll = (float)(Math.Sqrt(lw * lw   lh * lh));
    // dot length:
    float dl = ll / pw;
    // dot gap count: round to nearest odd int:
    int dc = (int)(2 * Math.Round((dl   1) / 2) - 1);
    //  gap count:
    int gc = dc / 2 ;
    // dot count:
    dc = gc   1;
    // gap scaling
    float gs = (ll - dc * pw) / (pw * gc);
    // our custom dashpattern 
    pen.DashPattern = new float[] { 1, gs };
    // maybe skip 1st and/or last dots:
    if (skipDots % 2 == 1) pa = new PointF(pa_.X   pw * sigX, pa_.Y   pw * sigY);
    if (skipDots > 1) pb = new PointF(pb_.X - pw * sigX, pb_.Y - pw * sigY);
    // finally we can draw the line:
    g.DrawLine(pen, pa, pb);
    // dispose of pen clone
    pen.Dispose();
}
  

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

Вот результат, увеличенный в 4 раза, рисования четырех линий в форме прямоугольника с различной шириной пера, начиная с 1/3 - 10/3 :

введите описание изображения здесь

Это тестовый стенд, который я использовал; обратите внимание на использование полупрозрачного черного цвета, чтобы проиллюстрировать, как правильно прорисовываются углы, т. Е. Не перекрываются:

 Pen Dot = new Pen(Color.Black, 1f);
Point pa = new Point(10, 50);
Point pb = new Point(70, 50);
Point pc = new Point(70, 100);
Point pd = new Point(10, 100);

for (int i = 1; i < 10; i   )
{
    Dot = new Pen(Color.FromArgb(128, Color.Black), i / 3f){ DashStyle = DashStyle.Dot };
    g.TranslateTransform(10, 10);
    DrawDottedLine(g, Dot, pa, pb, 2);
    DrawDottedLine(g, Dot, pb, pc, 2);
    DrawDottedLine(g, Dot, pc, pd, 2);
    DrawDottedLine(g, Dot, pd, pa, 2);
    DrawDottedLine(g, Dot, pd, pb, 3);
}
  

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

Ответ №2:

смещение линии по ширине линии

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

В этом случае вы хотели бы немного сместить вертикальную линию влево и вверх ( borderwith если быть точным).

Итак, путем добавления (или вычитания) смещения, равного ширине линии, результат таков:

 protected override void OnPaint (PaintEventArgs e) {
   Graphics g = e.Graphics;
   g.DrawLine (Dot, new Point (10, 50), new Point (70, 50));
   g.DrawLine (Dot, new Point (69, 49), new Point (69, 100));
}

Pen Dot = new Pen (Brushes.Black, 2) { DashStyle = DashStyle.Dot };
  

Комментарии:

1. Я не мог понять, что вы говорите. Может помочь немного кода.

2. 67,49 и 67,100 выглядят идеально

3. @kakkarot, в этом случае вам нужно будет учесть тот факт, что точки имеют пробелы, поэтому смещения могут быть больше или меньше в зависимости от точки, где точка будет начинаться или заканчиваться.

4. На самом деле все нарисованные линии нарисованы пером. Выравнивание по центру, что усложняет дело. Я думаю, что я, наконец, прибил это..