BMINAIEV_BLOG Telegram 22
Площадь многоугольника

Недавно вычислял площадь выпуклого многоугольника. Для этого существует очень простой алгоритм. Нужно по всем ребрам просуммировать их "ориентированную площадь".

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

Получается простой код:

fn area(pts: &[Point]) -> f32 {
let mut res = 0.0;
for i in 0..pts.len() {
let p1 = pts[i];
let p2 = pts[(i + 1) % pts.len()];
res += p1.x * p2.y - p1.y * p2.x;
}
res.abs() / 2.0
}

Этот способ называют формулой Гаусса.

Иногда используют площади трапеций, но суть от этого не меняется.

Но не все так просто! Вчера я написал этот алгоритм, посчитал площадь квадрата 1х1 и получил 0.0. Проблема была в том, что хоть квадрат и был маленьким, его координаты были порядка нескольких тысяч и хранились в f32. Казалось бы у типа f32 есть 23 бита мантиссы и их должно хватать, чтобы сохранить число порядка нескольких тысяч. Но на самом деле проблема в том, что при вычислении векторного произведения нужно перемножить два числа порядка нескольких тысяч (результат уже нельзя абсолютно точно сохранить в f32), а потом вычесть такое же по порядку число. От этого накапливается погрешность, и получается 0.0 в результате.

Как исправить алгоритм? Довольно хорошо работающий способ — вместо точки (0, 0) взять первую точку многоугольника, и все векторные произведения считать от нее:

fn area(pts: &[Point]) -> f32 {
let mut res = 0.0;
for i in 0..pts.len() {
let p1 = pts[i] - pts[0];
let p2 = pts[(i + 1) % pts.len()] - pts[0];
res += p1.x * p2.y - p1.y * p2.x;
}
res.abs() / 2.0
}

Но такой трюк не всегда решает проблему (например, в случае с невыпуклыми многоугольниками). Так что по возможности лучше просто избегать чисел с плавающей точкой :)
👍19



tgoop.com/bminaiev_blog/22
Create:
Last Update:

Площадь многоугольника

Недавно вычислял площадь выпуклого многоугольника. Для этого существует очень простой алгоритм. Нужно по всем ребрам просуммировать их "ориентированную площадь".

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

Получается простой код:

fn area(pts: &[Point]) -> f32 {
let mut res = 0.0;
for i in 0..pts.len() {
let p1 = pts[i];
let p2 = pts[(i + 1) % pts.len()];
res += p1.x * p2.y - p1.y * p2.x;
}
res.abs() / 2.0
}

Этот способ называют формулой Гаусса.

Иногда используют площади трапеций, но суть от этого не меняется.

Но не все так просто! Вчера я написал этот алгоритм, посчитал площадь квадрата 1х1 и получил 0.0. Проблема была в том, что хоть квадрат и был маленьким, его координаты были порядка нескольких тысяч и хранились в f32. Казалось бы у типа f32 есть 23 бита мантиссы и их должно хватать, чтобы сохранить число порядка нескольких тысяч. Но на самом деле проблема в том, что при вычислении векторного произведения нужно перемножить два числа порядка нескольких тысяч (результат уже нельзя абсолютно точно сохранить в f32), а потом вычесть такое же по порядку число. От этого накапливается погрешность, и получается 0.0 в результате.

Как исправить алгоритм? Довольно хорошо работающий способ — вместо точки (0, 0) взять первую точку многоугольника, и все векторные произведения считать от нее:

fn area(pts: &[Point]) -> f32 {
let mut res = 0.0;
for i in 0..pts.len() {
let p1 = pts[i] - pts[0];
let p2 = pts[(i + 1) % pts.len()] - pts[0];
res += p1.x * p2.y - p1.y * p2.x;
}
res.abs() / 2.0
}

Но такой трюк не всегда решает проблему (например, в случае с невыпуклыми многоугольниками). Так что по возможности лучше просто избегать чисел с плавающей точкой :)

BY Боря программирует




Share with your friend now:
tgoop.com/bminaiev_blog/22

View MORE
Open in Telegram


Telegram News

Date: |

How to create a business channel on Telegram? (Tutorial) Judge Hui described Ng as inciting others to “commit a massacre” with three posts teaching people to make “toxic chlorine gas bombs,” target police stations, police quarters and the city’s metro stations. This offence was “rather serious,” the court said. Concise In 2018, Telegram’s audience reached 200 million people, with 500,000 new users joining the messenger every day. It was launched for iOS on 14 August 2013 and Android on 20 October 2013. While the character limit is 255, try to fit into 200 characters. This way, users will be able to take in your text fast and efficiently. Reveal the essence of your channel and provide contact information. For example, you can add a bot name, link to your pricing plans, etc.
from us


Telegram Боря программирует
FROM American