Skip to content
Snippets Groups Projects
Commit 7637ca18 authored by hagrid67's avatar hagrid67
Browse files

extract methods add_rail_2cells, add_rail_3cells

Add deadend logic for start, end of mouse stroke.
Use remove_deadends in set_transition for 3 cell transitions.
parent bbf582e6
No related branches found
No related tags found
No related merge requests found
...@@ -46,7 +46,7 @@ class JupEditor(object): ...@@ -46,7 +46,7 @@ class JupEditor(object):
self.yxBase = array([6, 21]) # pixel offset self.yxBase = array([6, 21]) # pixel offset
self.nPixCell = 700 / self.env.rail.width # 35 self.nPixCell = 700 / self.env.rail.width # 35
self.rcHistory = [] self.lrcStroke = []
self.iTransLast = -1 self.iTransLast = -1
self.gRCTrans = array([[-1, 0], [0, 1], [1, 0], [0, -1]]) # NESW in RC self.gRCTrans = array([[-1, 0], [0, 1], [1, 0], [0, -1]]) # NESW in RC
...@@ -112,19 +112,25 @@ class JupEditor(object): ...@@ -112,19 +112,25 @@ class JupEditor(object):
y = event['canvasY'] y = event['canvasY']
env = self.env env = self.env
qEvents = self.qEvents qEvents = self.qEvents
rcHistory = self.rcHistory lrcStroke = self.lrcStroke
bRedrawn = False bRedrawn = False
writableData = None writableData = None
if self.debug and (event["buttons"] > 0 or self.debug_move): if self.debug and (event["buttons"] > 0 or self.debug_move):
self.log("debug:", len(qEvents), len(rcHistory), event) self.log("debug:", len(qEvents), len(lrcStroke), event)
assert wid == self.wid_img, "wid not same as wid_img" assert wid == self.wid_img, "wid not same as wid_img"
# If the mouse is held down, enqueue an event in our own queue # If the mouse is held down, enqueue an event in our own queue
# The intention was to avoid too many redraws.
if event["buttons"] > 0: if event["buttons"] > 0:
qEvents.append((time.time(), x, y)) qEvents.append((time.time(), x, y))
# Process the events in our queue:
# Draw a black square to indicate a trail
# TODO: infer a vector of moves between these squares to avoid gaps
# Convert the xy position to a cell rc
# Enqueue transitions across cells in another queue
if len(qEvents) > 0: if len(qEvents) > 0:
tNow = time.time() tNow = time.time()
if tNow - qEvents[0][0] > 0.1: # wait before trying to draw if tNow - qEvents[0][0] > 0.1: # wait before trying to draw
...@@ -142,93 +148,39 @@ class JupEditor(object): ...@@ -142,93 +148,39 @@ class JupEditor(object):
# Translate and scale from x,y to integer row,col (note order change) # Translate and scale from x,y to integer row,col (note order change)
rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int) rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int)
if len(rcHistory) > 1: # Store the row,col location of the click, if we have entered a new cell
rcLast = rcHistory[-1] if len(lrcStroke) > 0:
rcLast = lrcStroke[-1]
if not np.array_equal(rcLast, rcCell): # only save at transition if not np.array_equal(rcLast, rcCell): # only save at transition
# print(y, x, rcCell) # print(y, x, rcCell)
rcHistory.append(rcCell) lrcStroke.append(rcCell)
else: else:
rcHistory.append(rcCell) # This is the first cell in a mouse stroke
lrcStroke.append(rcCell)
elif len(rcHistory) >= 3:
# This elif means we wait until all the mouse events have been processed (black square drawn)
# before trying to draw rails. (We could change this behaviour)
# Equivalent to waiting for mouse button to be lifted (and a mouse event is necessary:
# the mouse may need to be moved)
elif len(lrcStroke) >= 3:
# If we have already touched 3 cells # If we have already touched 3 cells
# We have a transition into a cell, and out of it. # We have a transition into a cell, and out of it.
if self.drawMode == "Draw": if self.drawMode == "Draw":
bTransition = True bAddRemove = True
elif self.drawMode == "Erase": elif self.drawMode == "Erase":
bTransition = False bAddRemove = False
while len(rcHistory) >= 3:
rc3Cells = array(rcHistory[:3]) # the 3 cells
rcMiddle = rc3Cells[1] # the middle cell which we will update
# Save the original state of the cell # If the first cell in a stroke is empty, add a deadend to cell 0
oTransrcMiddle = self.env.rail.get_transitions(rcMiddle) if self.env.rail.get_transitions(lrcStroke[0]) == 0:
sTransrcMiddle = self.env.rail.cell_repr(rcMiddle) self.add_rail_2cells(lrcStroke, bAddRemove, iCellToMod=0)
# get the 2 row, col deltas between the 3 cells, eg [-1,0] = North while len(lrcStroke) >= 3:
rc2Trans = np.diff(rc3Cells, axis=0) self.add_rail_3cells(lrcStroke, env, bAddRemove)
# get the direction index for the 2 transitions
liTrans = []
for rcTrans in rc2Trans:
# gRCTrans - rcTrans gives an array of vector differences between our rcTrans
# and the 4 directions stored in gRCTrans.
# Where the vector difference is zero, we have a match...
# np.all detects where the whole row,col vector is zero.
# argwhere gives the index of the zero vector, ie the direction index
iTrans = np.argwhere(np.all(self.gRCTrans - rcTrans == 0, axis=1))
if len(iTrans) > 0:
iTrans = iTrans[0][0]
liTrans.append(iTrans)
# check that we have two transitions
if len(liTrans) == 2:
# Set the transition
env.rail.set_transition((*rcMiddle, liTrans[0]), liTrans[1], bTransition)
# Also set the reverse transition
# use the reversed outbound transition for inbound
# and the reversed inbound transition for outbound
env.rail.set_transition((*rcMiddle, mirror(liTrans[1])), mirror(liTrans[0]), bTransition)
bValid = env.rail.is_cell_valid(rcMiddle)
if not bValid:
# Reset cell transition values
env.rail.grid[tuple(rcMiddle)] = oTransrcMiddle
self.log(rcMiddle, "Orig:", sTransrcMiddle, "Mod:", self.env.rail.cell_repr(rcMiddle))
rcHistory.pop(0) # remove the last-but-one
# If final cell empty, insert deadend: # If final cell empty, insert deadend:
if len(rcHistory) == 2 and (self.env.rail.get_transitions(rcHistory[1]) == 0): if len(lrcStroke) == 2 and (self.env.rail.get_transitions(lrcStroke[1]) == 0):
rc2Cells = array(rcHistory[:2]) # the 2 cells self.add_rail_2cells(lrcStroke, bAddRemove, iCellToMod=1)
rcFinal = rc2Cells[1] # the final cell which we will update
# get the row, col delta between the 2 cells, eg [-1,0] = North
rc2Trans = np.diff(rc2Cells, axis=0)
# get the direction index for the 2 transitions
liTrans = []
for rcTrans in rc2Trans:
iTrans = np.argwhere(np.all(self.gRCTrans - rcTrans == 0, axis=1))
if len(iTrans) > 0:
iTrans = iTrans[0][0]
liTrans.append(iTrans)
# check that we have one transition
if len(liTrans) == 1:
# Set the transition as a deadend
env.rail.set_transition((*rcFinal, liTrans[0]), mirror(liTrans[0]), bTransition)
bValid = env.rail.is_cell_valid(rcMiddle)
if not bValid:
# Reset cell transition values
env.rail.grid[tuple(rcMiddle)] = oTransrcMiddle
self.log(rcMiddle, "Orig:", sTransrcMiddle, "Mod:", self.env.rail.cell_repr(rcMiddle))
rcHistory.pop(0) # remove the last-but-one
self.redraw() self.redraw()
bRedrawn = True bRedrawn = True
...@@ -237,7 +189,100 @@ class JupEditor(object): ...@@ -237,7 +189,100 @@ class JupEditor(object):
if not bRedrawn and writableData is not None: if not bRedrawn and writableData is not None:
# This updates the image in the browser to be the new edited version # This updates the image in the browser to be the new edited version
self.wid_img.data = writableData self.wid_img.data = writableData
def add_rail_3cells(self, rcCells, bAddRemove=True, bPop=True):
"""
Add transitions for rail spanning three cells.
lrcCells -- list of 3 rc cells
bAddRemove -- whether to add (True) or remove (False) the transition
The transition is added to or removed from the 2nd cell, consistent with
entering from the 1st cell, and exiting into the 3rd.
Both the forward and backward transitions are added,
eg rcCells [(3,4), (2,4), (2,5)] would result in the transitions
N->E and W->S in cell (2,4).
"""
rc3Cells = array(rcCells[:3]) # the 3 cells
rcMiddle = rc3Cells[1] # the middle cell which we will update
bDeadend = np.all(rcCells[0] == rcCells[2]) # deadend means cell 0 == cell 2
# Save the original state of the cell
# oTransrcMiddle = self.env.rail.get_transitions(rcMiddle)
# sTransrcMiddle = self.env.rail.cell_repr(rcMiddle)
# get the 2 row, col deltas between the 3 cells, eg [-1,0] = North
rc2Trans = np.diff(rc3Cells, axis=0)
# get the direction index for the 2 transitions
liTrans = []
for rcTrans in rc2Trans:
# gRCTrans - rcTrans gives an array of vector differences between our rcTrans
# and the 4 directions stored in gRCTrans.
# Where the vector difference is zero, we have a match...
# np.all detects where the whole row,col vector is zero.
# argwhere gives the index of the zero vector, ie the direction index
iTrans = np.argwhere(np.all(self.gRCTrans - rcTrans == 0, axis=1))
if len(iTrans) > 0:
iTrans = iTrans[0][0]
liTrans.append(iTrans)
# check that we have two transitions
if len(liTrans) == 2:
# Set the transition
# If this transition spans 3 cells, it is not a deadend, so remove any deadends.
# The user will need to resolve any conflicts.
self.env.rail.set_transition((*rcMiddle, liTrans[0]), liTrans[1], bAddRemove,
remove_deadends=not bDeadend)
# Also set the reverse transition
# use the reversed outbound transition for inbound
# and the reversed inbound transition for outbound
self.env.rail.set_transition((*rcMiddle, mirror(liTrans[1])),
mirror(liTrans[0]), bAddRemove, remove_deadends=not bDeadend)
# bValid = self.env.rail.is_cell_valid(rcMiddle)
# if not bValid:
# # Reset cell transition values
# self.env.rail.grid[tuple(rcMiddle)] = oTransrcMiddle
# self.log(rcMiddle, "Orig:", sTransrcMiddle, "Mod:", self.env.rail.cell_repr(rcMiddle))
if bPop:
rcCells.pop(0) # remove the first cell in the stroke
def add_rail_2cells(self, lrcCells, bAddRemove=True, iCellToMod=0, bPop=False):
"""
Add transitions for rail between two cells
lrcCells -- list of two rc cells
bAddRemove -- whether to add (True) or remove (False) the transition
iCellToMod -- the index of the cell to modify: either 0 or 1
"""
rc2Cells = array(lrcCells[:2]) # the 2 cells
rcMod = rc2Cells[iCellToMod] # the cell which we will update
# get the row, col delta between the 2 cells, eg [-1,0] = North
rc1Trans = np.diff(rc2Cells, axis=0)
# get the direction index for the transition
liTrans = []
for rcTrans in rc1Trans:
iTrans = np.argwhere(np.all(self.gRCTrans - rcTrans == 0, axis=1))
if len(iTrans) > 0:
iTrans = iTrans[0][0]
liTrans.append(iTrans)
# check that we have one transition
if len(liTrans) == 1:
# Set the transition as a deadend
# The transition is going from cell 0 to cell 1.
if iCellToMod == 0:
# if 0, reverse the transition, we need to be entering cell 0
self.env.rail.set_transition((*rcMod, mirror(liTrans[0])), liTrans[0], bAddRemove)
else:
# if 1, the transition is entering cell 1
self.env.rail.set_transition((*rcMod, liTrans[0]), mirror(liTrans[0]), bAddRemove)
if bPop:
lrcCells.pop(0)
def redraw(self, hide_stdout=True, update=True): def redraw(self, hide_stdout=True, update=True):
# if hide_stdout: # if hide_stdout:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment