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
......@@ -46,7 +46,7 @@ class JupEditor(object):
self.yxBase = array([6, 21]) # pixel offset
self.nPixCell = 700 / self.env.rail.width # 35
self.rcHistory = []
self.lrcStroke = []
self.iTransLast = -1
self.gRCTrans = array([[-1, 0], [0, 1], [1, 0], [0, -1]]) # NESW in RC
......@@ -112,19 +112,25 @@ class JupEditor(object):
y = event['canvasY']
env = self.env
qEvents = self.qEvents
rcHistory = self.rcHistory
lrcStroke = self.lrcStroke
bRedrawn = False
writableData = None
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"
# 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:
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:
tNow = time.time()
if tNow - qEvents[0][0] > 0.1: # wait before trying to draw
......@@ -142,93 +148,39 @@ class JupEditor(object):
# Translate and scale from x,y to integer row,col (note order change)
rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int)
if len(rcHistory) > 1:
rcLast = rcHistory[-1]
# Store the row,col location of the click, if we have entered a new cell
if len(lrcStroke) > 0:
rcLast = lrcStroke[-1]
if not np.array_equal(rcLast, rcCell): # only save at transition
# print(y, x, rcCell)
rcHistory.append(rcCell)
lrcStroke.append(rcCell)
else:
rcHistory.append(rcCell)
elif len(rcHistory) >= 3:
# This is the first cell in a mouse stroke
lrcStroke.append(rcCell)
# 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
# We have a transition into a cell, and out of it.
if self.drawMode == "Draw":
bTransition = True
bAddRemove = True
elif self.drawMode == "Erase":
bTransition = False
while len(rcHistory) >= 3:
rc3Cells = array(rcHistory[:3]) # the 3 cells
rcMiddle = rc3Cells[1] # the middle cell which we will update
bAddRemove = False
# Save the original state of the cell
oTransrcMiddle = self.env.rail.get_transitions(rcMiddle)
sTransrcMiddle = self.env.rail.cell_repr(rcMiddle)
# If the first cell in a stroke is empty, add a deadend to cell 0
if self.env.rail.get_transitions(lrcStroke[0]) == 0:
self.add_rail_2cells(lrcStroke, bAddRemove, iCellToMod=0)
# 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
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
while len(lrcStroke) >= 3:
self.add_rail_3cells(lrcStroke, env, bAddRemove)
# If final cell empty, insert deadend:
if len(rcHistory) == 2 and (self.env.rail.get_transitions(rcHistory[1]) == 0):
rc2Cells = array(rcHistory[:2]) # the 2 cells
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
if len(lrcStroke) == 2 and (self.env.rail.get_transitions(lrcStroke[1]) == 0):
self.add_rail_2cells(lrcStroke, bAddRemove, iCellToMod=1)
self.redraw()
bRedrawn = True
......@@ -237,7 +189,100 @@ class JupEditor(object):
if not bRedrawn and writableData is not None:
# This updates the image in the browser to be the new edited version
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):
# if hide_stdout:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment