Skip to content

Add incidentEdgeTracker and indexCellData types and tests #180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

rsned
Copy link
Collaborator

@rsned rsned commented May 15, 2025

Add incidentEdgeTracker and indexCellData types and tests.
Update LaxPolygon to support proper vertex ordering on holes and shells.
Fix comments and variable names in LaxPolygon.
Add more of the missing LaxPolygon tests to validate fixes.

These are ports of the corresponding C++ s2/internal/ types that are needed for ValidationQuery for Loops and Polygons.

This PR works towards issues #72, #108.

rsned added 3 commits May 15, 2025 13:07
These are ports of the C++ s2/internal/ types that are needed for ValidationQuery for Loops and Polygons.

Work for issues golang#72, golang#108.
Clean up some comments and variable names.
Update some of the methods to match current C++ logic.
Add more unit tests for multiloop polygons to validate that holes are inverted ordering.
@alan-strohm alan-strohm self-assigned this May 20, 2025
Copy link
Collaborator

@alan-strohm alan-strohm left a comment

Choose a reason for hiding this comment

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

Sending some initial thoughts since you've been waiting a while and some comments might need discussion. I'll look more tomorrow. Thanks for doing this!

@@ -0,0 +1,173 @@
// Copyright 2025 The S2 Geometry Project Authors. All rights reserved.
Copy link
Collaborator

@alan-strohm alan-strohm May 27, 2025

Choose a reason for hiding this comment

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

I assume we need to resolve this discussion about what to do with Copyrights before proceeding: https://github.com/golang/geo/pull/178/files#r2094005567


package s2

// incidentEdgeKey is a tuple of (shape id, vertex) that compares by shape id.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: id -> ID throughout.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

// shape's edges may be defined with multiple sequences of startShape(),
// addEdge()... , finishShape() calls.
//
// The reason for this is simple: most edges don't have more than two incident
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this supposed to read "most vertices don't have more than two incident edges"? I'm having a hard time following these two paragraphs and I don't think it' s just my lack of subject-matter experience...

If we're OK with more divergence with the C++, I think it'd be clearer to make the 3rd paragraph about how we only look for vertices with 3 or more incident edges and that we don't remember vertices with fewer incident edges after a call to finishShape. Then in the 4th (and 5th if necessary) paragraph, I'd talk about how "a single shape's edges may be defined with multiple sequences..." as long as the caller ensures that all the edges on a given vertex are handled in the same sequence of calls via something like ShapeIndex cells.

But if you want to stick to correcting obvious errors, that seems like a very reasonable choice.

// adds it second endpoint as well.
func (t *incidentEdgeTracker) addEdge(edgeID int32, e Edge) {
if t.currentShapeID < 0 {
return
Copy link
Collaborator

Choose a reason for hiding this comment

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

The error handling across these functions is pretty bad. Here, you silently drop edges. There's no checking to ensure that calls are made in the right sequence. Should we re-examine the API to make erroneous usage more difficult? All the internal calls to the C++ version look like:

      incident_edge_tracker_.StartShape(shape_id);
      for (const auto& edge : CurrentCell().shape_edges(shape_id)) {
        incident_edge_tracker_.AddEdge(edge.id, edge);
      }
      incident_edge_tracker_.FinishShape();

So we could have an interface that looks like:

// Add all edges to the tracker.   After calling, any vertices with multiple (> 2) incident
// edges will appear in the incident edge map.  Returns an error if any edges have a different
// shape ID than shapeID.
func (t *incidentEdgeTracker) addShapeEdges(shapeID int32, edges []ShapeEdge) error

This would also remove the need for a "nursery" and the associated type, resolving one of my other comments.

Copy link
Collaborator

@alan-strohm alan-strohm left a comment

Choose a reason for hiding this comment

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

OK, this should be a complete set of comments.

index string
want int
}{
// These shapeindex strings came from validation query's test
Copy link
Collaborator

Choose a reason for hiding this comment

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

// A collection of Loops is similar, but not the same as a Polygon, so
// from this creation method, we do not need to track Loop orientation
// as hole or shell like Polygon does.
func LaxPolygonFromLoops(loops []Loop) *LaxPolygon {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this function necessary? I'm not seeing a corresponding function in https://github.com/google/s2geometry/blob/master/src/s2/s2lax_polygon_shape.cc

if shape.Edge(edge.e) != (Edge{v0, v1}) {
t.Errorf("sfsdaa")
if got, want := shape.Edge(edge.e), (Edge{v0, v1}); got != want {
t.Errorf("Shape Edge(%d) = %v, want %v", edge.e, got, want)
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: shape.Edge

type shapeRegion struct {
id int32
region indexCellRegion
type shapeRange struct {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this one too generic? Would indexShapeRange be better?

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