Skip to content

feat: Custom viewshed polygon reconstructor - #67

Merged
tombh merged 7 commits into
mainfrom
tombh/adjacent-polar-viewshed-builder
Jul 23, 2026
Merged

feat: Custom viewshed polygon reconstructor#67
tombh merged 7 commits into
mainfrom
tombh/adjacent-polar-viewshed-builder

Conversation

@tombh

@tombh tombh commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

At higher angle counts a traditional polygon unioning of all the polar segments suffers from O(N²). For example, even in Rust a 3600 angled viewshed takes ~20s to create its GeoJSON. And many minutes in browser JS.

Therefore we have to design an entirely new algorithm that takes advantage of the fact that we are only ever adding new pieces to the growing viewshed in a very specific way. Namely we only ever add polar segments, that are always contained within the arc of a single angle, onto the side of a polygon. This returns polygon reconstruction to 200ms 80ms for 3600 angled viewsheds. There's actually huuuuge amount of room for speeding up this approach, but seeing as its already so much faster than the old method, this is good enough for now.

TODO:

  • Handle polygons that wrap all the way around.
  • Handle segments that touch more than one polygon.

Apologies, this is rather big PR to review. Most of the changes are hidden in collapsed file views in the Github Diff view.

@tombh

tombh commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator Author

Current state of bencmarks:

Correct:
image

This PR:
image

@tombh
tombh force-pushed the tombh/adjacent-polar-viewshed-builder branch from 60f7aac to d343bc7 Compare June 19, 2026 09:45
At higher angle counts a traditional polygon unioning of all the polar
segments suffers from O(N²). For example, even in Rust a 3600 angled
viewshed takes ~20s to create its GeoJSON. And many minutes in browser JS.

Therefore we have to design an entirely new algorithm that takes
advantage of the fact that we are only ever adding new pieces to the
growing viewshed in a very specific way. Namely we only ever add polar
segments, that are always contained within the arc of a single angle,
onto the _side_ of a polygon. This returns polygon reconstruction to a
~200ms for 3600 angled viewsheds.
@tombh
tombh force-pushed the tombh/adjacent-polar-viewshed-builder branch from d343bc7 to ac0d147 Compare June 28, 2026 09:28
@tombh
tombh marked this pull request as ready for review June 28, 2026 09:31
@tombh

tombh commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator Author

Benchmark viewshed is now pixel-perfect with the existing one. Though note that the file size is slightly smaller due to a tidier algorithm.

@tombh
tombh requested a review from ryan-berger June 28, 2026 09:32

@ryan-berger ryan-berger left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to leave this for some changes responses, and dig into the algorithm a lot heavier tomorrow

Comment thread crates/viewshed-reconstructor/src/growable_polygon.rs
Comment thread crates/total-viewsheds/src/output/viewsheds/joiner_common.rs Outdated
Comment thread crates/viewshed-reconstructor/src/joiner/last.rs
Comment thread crates/viewshed-reconstructor/src/joiner/last.rs
Comment thread crates/total-viewsheds/src/output/viewsheds/joiner/normal.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/growable_polygon.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/growable_polygon.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/growable_polygon.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/joiner/last.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/joiner/normal.rs Outdated
Adds the following lints:

  unreachable_pub = "warn"
  redundant_pub_crate = "allow"

This enforces the practice of explicitly defining visibility. Therefore,
if a symbol isn't truly public to the outside world then it should be
qualified with `pub(crate)`, `pub(super)`, etc.

This is slightly more verbose but helps reasoning about API surfaces and
also safeguards against unexpected symbols becoming public when parent
visibility is changed.
@tombh
tombh force-pushed the tombh/adjacent-polar-viewshed-builder branch from aab2a56 to 7db051b Compare July 4, 2026 07:36
Comment thread crates/total-viewsheds/src/output/viewsheds/joiner/normal.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/joiner/normal.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/joiner/normal.rs Outdated
}

/// Join a final polygon to either itself or a starting polygon.
fn join_final_polygon(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is nearly identical to join_segment in the next.rs.

The only difference I can see is how maybe_joining_polygon is constructed, and maybe base_polygon?

To me this says that there is some incorrect abstraction here. There should be a function that does finds the start/end pair you are looking for, only referencing its parameters and not behind a &mut self

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we have the openings iterator, these functions look quite different now.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things have changed and I think this isn't a problem anymore

Comment thread crates/total-viewsheds/src/output/viewsheds/joiner/last.rs Outdated
Comment thread crates/viewshed-reconstructor/src/joiner/last.rs
Comment thread crates/viewshed-reconstructor/src/joiner/last.rs
Comment thread crates/total-viewsheds/src/output/viewsheds/segment_polygon.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/segment_polygon.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/joiner.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/joiner/last.rs Outdated
tombh added 2 commits July 5, 2026 16:19
And other improvements from review.
@tombh
tombh force-pushed the tombh/adjacent-polar-viewshed-builder branch from 7db051b to dfec260 Compare July 5, 2026 09:20

@ryan-berger ryan-berger left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Less comments, but getting close

Comment thread crates/viewshed-reconstructor/src/vertices.rs
Comment thread crates/total-viewsheds/src/output/viewsheds/vertices.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/joiner.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/joiner/last.rs Outdated
Comment thread crates/total-viewsheds/src/output/viewsheds/vertices.rs Outdated
Comment thread crates/viewshed-reconstructor/src/vertices.rs
tombh added 2 commits July 19, 2026 08:42
This prepares for compiling this code to WASM.
Successfully used in the browser.
@tombh
tombh merged commit 0931f66 into main Jul 23, 2026
4 checks passed
@tombh
tombh deleted the tombh/adjacent-polar-viewshed-builder branch July 23, 2026 05:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants