gonum.org/v1/gonum@v0.14.0/graph/path/internal/testgraphs/grid.go (about)

     1  // Copyright ©2014 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package testgraphs
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"math"
    11  
    12  	"gonum.org/v1/gonum/graph"
    13  	"gonum.org/v1/gonum/graph/iterator"
    14  	"gonum.org/v1/gonum/graph/simple"
    15  )
    16  
    17  const (
    18  	Closed  = '*' // Closed is the closed grid node representation.
    19  	Open    = '.' // Open is the open grid node representation.
    20  	Unknown = '?' // Unknown is the unknown grid node representation.
    21  )
    22  
    23  // Grid is a 2D grid planar undirected graph.
    24  type Grid struct {
    25  	// AllowDiagonal specifies whether
    26  	// diagonally adjacent nodes can
    27  	// be connected by an edge.
    28  	AllowDiagonal bool
    29  	// UnitEdgeWeight specifies whether
    30  	// finite edge weights are returned as
    31  	// the unit length. Otherwise edge
    32  	// weights are the Euclidean distance
    33  	// between connected nodes.
    34  	UnitEdgeWeight bool
    35  
    36  	// AllVisible specifies whether
    37  	// non-open nodes are visible
    38  	// in calls to Nodes and HasNode.
    39  	AllVisible bool
    40  
    41  	open []bool
    42  	r, c int
    43  }
    44  
    45  // NewGrid returns an r by c grid with all positions
    46  // set to the specified open state.
    47  func NewGrid(r, c int, open bool) *Grid {
    48  	states := make([]bool, r*c)
    49  	if open {
    50  		for i := range states {
    51  			states[i] = true
    52  		}
    53  	}
    54  	return &Grid{
    55  		open: states,
    56  		r:    r,
    57  		c:    c,
    58  	}
    59  }
    60  
    61  // NewGridFrom returns a grid specified by the rows strings. All rows must
    62  // be the same length and must only contain the Open or Closed characters,
    63  // NewGridFrom will panic otherwise.
    64  func NewGridFrom(rows ...string) *Grid {
    65  	if len(rows) == 0 {
    66  		return nil
    67  	}
    68  	for i, r := range rows[:len(rows)-1] {
    69  		if len(r) != len(rows[i+1]) {
    70  			panic("grid: unequal row lengths")
    71  		}
    72  	}
    73  	states := make([]bool, 0, len(rows)*len(rows[0]))
    74  	for _, r := range rows {
    75  		for _, b := range r {
    76  			switch b {
    77  			case Closed:
    78  				states = append(states, false)
    79  			case Open:
    80  				states = append(states, true)
    81  			default:
    82  				panic(fmt.Sprintf("grid: invalid state: %q", r))
    83  			}
    84  		}
    85  	}
    86  	return &Grid{
    87  		open: states,
    88  		r:    len(rows),
    89  		c:    len(rows[0]),
    90  	}
    91  }
    92  
    93  // Nodes returns all the open nodes in the grid if AllVisible is
    94  // false, otherwise all nodes are returned.
    95  func (g *Grid) Nodes() graph.Nodes {
    96  	var nodes []graph.Node
    97  	for id, ok := range g.open {
    98  		if ok || g.AllVisible {
    99  			nodes = append(nodes, simple.Node(id))
   100  		}
   101  	}
   102  	return iterator.NewOrderedNodes(nodes)
   103  }
   104  
   105  // Node returns the node with the given ID if it exists in the graph,
   106  // and nil otherwise.
   107  func (g *Grid) Node(id int64) graph.Node {
   108  	if g.has(id) {
   109  		return simple.Node(id)
   110  	}
   111  	return nil
   112  }
   113  
   114  // has returns whether id represents a node in the grid. The state of
   115  // the AllVisible field determines whether a non-open node is present.
   116  func (g *Grid) has(id int64) bool {
   117  	return 0 <= id && id < int64(len(g.open)) && (g.AllVisible || g.open[id])
   118  }
   119  
   120  // HasOpen returns whether n is an open node in the grid.
   121  func (g *Grid) HasOpen(id int64) bool {
   122  	return 0 <= id && id < int64(len(g.open)) && g.open[id]
   123  }
   124  
   125  // Set sets the node at position (r, c) to the specified open state.
   126  func (g *Grid) Set(r, c int, open bool) {
   127  	if r < 0 || r >= g.r {
   128  		panic("grid: illegal row index")
   129  	}
   130  	if c < 0 || c >= g.c {
   131  		panic("grid: illegal column index")
   132  	}
   133  	g.open[r*g.c+c] = open
   134  }
   135  
   136  // Dims returns the dimensions of the grid.
   137  func (g *Grid) Dims() (r, c int) {
   138  	return g.r, g.c
   139  }
   140  
   141  // RowCol returns the row and column of the id. RowCol will panic if the
   142  // node id is outside the range of the grid.
   143  func (g *Grid) RowCol(id int64) (r, c int) {
   144  	if id < 0 || int64(len(g.open)) <= id {
   145  		panic("grid: illegal node id")
   146  	}
   147  	return int(id) / g.c, int(id) % g.c
   148  }
   149  
   150  // XY returns the cartesian coordinates of n. If n is not a node
   151  // in the grid, (NaN, NaN) is returned.
   152  func (g *Grid) XY(id int64) (x, y float64) {
   153  	if !g.has(id) {
   154  		return math.NaN(), math.NaN()
   155  	}
   156  	r, c := g.RowCol(id)
   157  	return float64(c), float64(r)
   158  }
   159  
   160  // NodeAt returns the node at (r, c). The returned node may be open or closed.
   161  func (g *Grid) NodeAt(r, c int) graph.Node {
   162  	if r < 0 || r >= g.r || c < 0 || c >= g.c {
   163  		return nil
   164  	}
   165  	return simple.Node(r*g.c + c)
   166  }
   167  
   168  // From returns all the nodes reachable from u. Reachabilty requires that both
   169  // ends of an edge must be open.
   170  func (g *Grid) From(uid int64) graph.Nodes {
   171  	if !g.HasOpen(uid) {
   172  		return graph.Empty
   173  	}
   174  	nr, nc := g.RowCol(uid)
   175  	var to []graph.Node
   176  	for r := nr - 1; r <= nr+1; r++ {
   177  		for c := nc - 1; c <= nc+1; c++ {
   178  			if v := g.NodeAt(r, c); v != nil && g.HasEdgeBetween(uid, v.ID()) {
   179  				to = append(to, v)
   180  			}
   181  		}
   182  	}
   183  	if len(to) == 0 {
   184  		return graph.Empty
   185  	}
   186  	return iterator.NewOrderedNodes(to)
   187  }
   188  
   189  // HasEdgeBetween returns whether there is an edge between u and v.
   190  func (g *Grid) HasEdgeBetween(uid, vid int64) bool {
   191  	if !g.HasOpen(uid) || !g.HasOpen(vid) || uid == vid {
   192  		return false
   193  	}
   194  	ur, uc := g.RowCol(uid)
   195  	vr, vc := g.RowCol(vid)
   196  	if abs(ur-vr) > 1 || abs(uc-vc) > 1 {
   197  		return false
   198  	}
   199  	return g.AllowDiagonal || ur == vr || uc == vc
   200  }
   201  
   202  func abs(i int) int {
   203  	if i < 0 {
   204  		return -i
   205  	}
   206  	return i
   207  }
   208  
   209  // Edge returns the edge between u and v.
   210  func (g *Grid) Edge(uid, vid int64) graph.Edge {
   211  	return g.WeightedEdgeBetween(uid, vid)
   212  }
   213  
   214  // WeightedEdge returns the weighted edge between u and v.
   215  func (g *Grid) WeightedEdge(uid, vid int64) graph.WeightedEdge {
   216  	return g.WeightedEdgeBetween(uid, vid)
   217  }
   218  
   219  // EdgeBetween returns the edge between u and v.
   220  func (g *Grid) EdgeBetween(uid, vid int64) graph.Edge {
   221  	return g.WeightedEdgeBetween(uid, vid)
   222  }
   223  
   224  // WeightedEdgeBetween returns the weighted edge between u and v.
   225  func (g *Grid) WeightedEdgeBetween(uid, vid int64) graph.WeightedEdge {
   226  	if g.HasEdgeBetween(uid, vid) {
   227  		if !g.AllowDiagonal || g.UnitEdgeWeight {
   228  			return simple.WeightedEdge{F: simple.Node(uid), T: simple.Node(vid), W: 1}
   229  		}
   230  		ux, uy := g.XY(uid)
   231  		vx, vy := g.XY(vid)
   232  		return simple.WeightedEdge{F: simple.Node(uid), T: simple.Node(vid), W: math.Hypot(ux-vx, uy-vy)}
   233  	}
   234  	return nil
   235  }
   236  
   237  // Weight returns the weight of the given edge.
   238  func (g *Grid) Weight(xid, yid int64) (w float64, ok bool) {
   239  	if xid == yid {
   240  		return 0, true
   241  	}
   242  	if !g.HasEdgeBetween(xid, yid) {
   243  		return math.Inf(1), false
   244  	}
   245  	if e := g.EdgeBetween(xid, yid); e != nil {
   246  		if !g.AllowDiagonal || g.UnitEdgeWeight {
   247  			return 1, true
   248  		}
   249  		ux, uy := g.XY(e.From().ID())
   250  		vx, vy := g.XY(e.To().ID())
   251  		return math.Hypot(ux-vx, uy-vy), true
   252  	}
   253  	return math.Inf(1), true
   254  }
   255  
   256  // String returns a string representation of the grid.
   257  func (g *Grid) String() string {
   258  	b, _ := g.Render(nil)
   259  	return string(b)
   260  }
   261  
   262  // Render returns a text representation of the graph
   263  // with the given path included. If the path is not a path
   264  // in the grid Render returns a non-nil error and the
   265  // path up to that point.
   266  func (g *Grid) Render(path []graph.Node) ([]byte, error) {
   267  	b := make([]byte, g.r*(g.c+1)-1)
   268  	for r := 0; r < g.r; r++ {
   269  		for c := 0; c < g.c; c++ {
   270  			if g.open[r*g.c+c] {
   271  				b[r*(g.c+1)+c] = Open
   272  			} else {
   273  				b[r*(g.c+1)+c] = Closed
   274  			}
   275  		}
   276  		if r < g.r-1 {
   277  			b[r*(g.c+1)+g.c] = '\n'
   278  		}
   279  	}
   280  
   281  	// We don't use topo.IsPathIn at the outset because we
   282  	// want to draw as much as possible before failing.
   283  	for i, n := range path {
   284  		id := n.ID()
   285  		if !g.has(id) || (i != 0 && !g.HasEdgeBetween(path[i-1].ID(), id)) {
   286  			if 0 <= id && id < int64(len(g.open)) {
   287  				r, c := g.RowCol(id)
   288  				b[r*(g.c+1)+c] = '!'
   289  			}
   290  			return b, errors.New("grid: not a path in graph")
   291  		}
   292  		r, c := g.RowCol(id)
   293  		switch i {
   294  		case len(path) - 1:
   295  			b[r*(g.c+1)+c] = 'G'
   296  		case 0:
   297  			b[r*(g.c+1)+c] = 'S'
   298  		default:
   299  			b[r*(g.c+1)+c] = 'o'
   300  		}
   301  	}
   302  	return b, nil
   303  }