Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
Flatland
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
sfwatergit
Flatland
Commits
404d650a
Commit
404d650a
authored
5 years ago
by
hagrid67
Browse files
Options
Downloads
Patches
Plain Diff
Refactored editor as model-view-controller.
New notebook Editor2.ipynb.
parent
0b2b56f6
No related branches found
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
flatland/utils/editor.py
+359
-162
359 additions, 162 deletions
flatland/utils/editor.py
notebooks/Editor2.ipynb
+232
-0
232 additions, 0 deletions
notebooks/Editor2.ipynb
with
591 additions
and
162 deletions
flatland/utils/editor.py
+
359
−
162
View file @
404d650a
...
@@ -4,6 +4,8 @@ import time
...
@@ -4,6 +4,8 @@ import time
from
collections
import
deque
from
collections
import
deque
from
matplotlib
import
pyplot
as
plt
from
matplotlib
import
pyplot
as
plt
import
threading
import
threading
import
os
# from contextlib import redirect_stdout
# from contextlib import redirect_stdout
# import os
# import os
# import sys
# import sys
...
@@ -19,107 +21,207 @@ import flatland.utils.rendertools as rt
...
@@ -19,107 +21,207 @@ import flatland.utils.rendertools as rt
from
examples.play_model
import
Player
from
examples.play_model
import
Player
from
flatland.envs.env_utils
import
mirror
from
flatland.envs.env_utils
import
mirror
import
ipywidgets
from
ipywidgets
import
IntSlider
,
VBox
,
HBox
,
Checkbox
,
Output
,
Text
import
jpy_canvas
class
EditorMVC
(
object
):
def
__init__
(
self
,
env
=
None
):
if
env
is
None
:
env
=
RailEnv
(
width
=
10
,
height
=
10
,
rail_generator
=
random_rail_generator
(
cell_type_relative_proportion
=
[
1
,
1
]
+
[
0.5
]
*
6
),
number_of_agents
=
0
,
obs_builder_object
=
TreeObsForRailEnv
(
max_depth
=
2
))
env
.
reset
()
self
.
editor
=
EditorModel
(
env
)
self
.
editor
.
view
=
self
.
view
=
View
(
self
.
editor
)
self
.
view
.
controller
=
self
.
editor
.
controller
=
self
.
controller
=
Controller
(
self
.
editor
,
self
.
view
)
self
.
view
.
init_canvas
()
self
.
view
.
init_widgets
()
# has to be done after controller
class
View
(
object
):
class
View
(
object
):
def
__init__
(
self
,
editor
):
def
__init__
(
self
,
editor
):
self
.
editor
=
editor
self
.
editor
=
self
.
model
=
editor
def
display
(
self
):
self
.
wOutput
.
clear_output
()
return
self
.
wMain
def
init_canvas
(
self
):
self
.
oRT
=
rt
.
RenderTool
(
self
.
editor
.
env
)
self
.
oRT
=
rt
.
RenderTool
(
self
.
editor
.
env
)
plt
.
figure
(
figsize
=
(
10
,
10
))
plt
.
figure
(
figsize
=
(
10
,
10
))
self
.
oRT
.
renderEnv
(
spacing
=
False
,
arrows
=
False
,
sRailColor
=
"
gray
"
,
show
=
False
)
self
.
oRT
.
renderEnv
(
spacing
=
False
,
arrows
=
False
,
sRailColor
=
"
gray
"
,
show
=
False
)
img
=
self
.
oRT
.
getImage
()
img
=
self
.
oRT
.
getImage
()
plt
.
clf
()
plt
.
clf
()
import
jpy_canvas
self
.
wImage
=
jpy_canvas
.
Canvas
(
img
)
self
.
wid_img
=
jpy_canvas
.
Canvas
(
img
)
self
.
yxSize
=
self
.
wImage
.
data
.
shape
[:
2
]
self
.
writableData
=
np
.
copy
(
self
.
wImage
.
data
)
# writable copy of image - wid_img.data is somehow readonly
self
.
wImage
.
register_move
(
self
.
controller
.
on_mouse_move
)
class
JupEditor
(
object
):
self
.
wImage
.
register_click
(
self
.
controller
.
on_click
)
def
__init__
(
self
,
env
,
wid_img
):
print
(
"
Correct editor
"
)
self
.
env
=
env
self
.
wid_img
=
wid_img
self
.
qEvents
=
deque
()
self
.
regen_size
=
10
# TODO: These are currently estimated values
# TODO: These are currently estimated values
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
.
model
.
env
.
rail
.
width
# 35
def
init_widgets
(
self
):
# Radiobutton for drawmode - TODO: replace with shift/ctrl/alt keys
# self.wDrawMode = RadioButtons(options=["Draw", "Erase", "Origin", "Destination"])
# self.wDrawMode.observe(self.editor.setDrawMode, names="value")
# Debug checkbox - enable logging in the Output widget
self
.
wDebug
=
ipywidgets
.
Checkbox
(
description
=
"
Debug
"
)
self
.
wDebug
.
observe
(
self
.
controller
.
setDebug
,
names
=
"
value
"
)
# Separate checkbox for mouse move events - they are very verbose
self
.
wDebug_move
=
Checkbox
(
description
=
"
Debug mouse move
"
)
self
.
wDebug_move
.
observe
(
self
.
controller
.
setDebugMove
,
names
=
"
value
"
)
# This is like a cell widget where loggin goes
self
.
wOutput
=
Output
()
# Filename textbox
self
.
wFilename
=
Text
(
description
=
"
Filename
"
)
self
.
wFilename
.
value
=
self
.
model
.
env_filename
self
.
wFilename
.
observe
(
self
.
controller
.
setFilename
,
names
=
"
value
"
)
# Size of environment when regenerating
self
.
wSize
=
IntSlider
(
value
=
10
,
min
=
5
,
max
=
30
,
step
=
5
,
description
=
"
Regen Size
"
)
self
.
wSize
.
observe
(
self
.
controller
.
setRegenSize
,
names
=
"
value
"
)
# Progress bar intended for stepping in the background (not yet working)
self
.
wProg_steps
=
ipywidgets
.
IntProgress
(
value
=
0
,
min
=
0
,
max
=
20
,
step
=
1
,
description
=
"
Step
"
)
# abbreviated description of buttons and the methods they call
ldButtons
=
[
dict
(
name
=
"
Refresh
"
,
method
=
self
.
controller
.
refresh
),
dict
(
name
=
"
Clear
"
,
method
=
self
.
controller
.
clear
),
dict
(
name
=
"
Regenerate
"
,
method
=
self
.
controller
.
regenerate
),
dict
(
name
=
"
Load
"
,
method
=
self
.
controller
.
load
),
dict
(
name
=
"
Save
"
,
method
=
self
.
controller
.
save
),
dict
(
name
=
"
Step
"
,
method
=
self
.
controller
.
step
),
dict
(
name
=
"
Run Steps
"
,
method
=
self
.
controller
.
start_run
),
]
self
.
lwButtons
=
[]
for
dButton
in
ldButtons
:
wButton
=
ipywidgets
.
Button
(
description
=
dButton
[
"
name
"
])
wButton
.
on_click
(
dButton
[
"
method
"
])
self
.
lwButtons
.
append
(
wButton
)
self
.
wVbox_controls
=
VBox
([
self
.
wFilename
,
# self.wDrawMode,
*
self
.
lwButtons
,
self
.
wSize
,
self
.
wDebug
,
self
.
wDebug_move
,
self
.
wProg_steps
])
self
.
wMain
=
HBox
([
self
.
wImage
,
self
.
wVbox_controls
])
self
.
lrcStroke
=
[]
def
drawStroke
(
self
):
self
.
iTransLast
=
-
1
pass
self
.
gRCTrans
=
array
([[
-
1
,
0
],
[
0
,
1
],
[
1
,
0
],
[
0
,
-
1
]])
# NESW in RC
def
new_env
(
self
):
self
.
oRT
=
rt
.
RenderTool
(
self
.
editor
.
env
)
self
.
debug
=
False
def
redraw
(
self
):
self
.
debug_move
=
False
# TODO: bit of a hack - can we suppress the console messages from MPL at source?
self
.
wid_output
=
None
# with redirect_stdout(stdout_dest):
self
.
drawMode
=
"
Draw
"
with
self
.
wOutput
:
self
.
env_filename
=
"
temp.npy
"
plt
.
figure
(
figsize
=
(
10
,
10
))
self
.
set_env
(
env
)
self
.
oRT
.
renderEnv
(
spacing
=
False
,
arrows
=
False
,
sRailColor
=
"
gray
"
,
show
=
False
)
self
.
iAgent
=
None
img
=
self
.
oRT
.
getImage
()
self
.
player
=
None
plt
.
clf
()
self
.
thread
=
None
plt
.
close
()
self
.
wImage
.
data
=
img
self
.
writableData
=
np
.
copy
(
self
.
wImage
.
data
)
return
img
def
redisplayImage
(
self
):
if
self
.
writableData
is
not
None
:
# This updates the image in the browser to be the new edited version
self
.
wImage
.
data
=
self
.
writableData
def
set_env
(
self
,
env
):
def
drag_path_element
(
self
,
x
,
y
):
self
.
env
=
env
# Draw a black square on the in-memory copy of the image
self
.
yxBase
=
array
([
6
,
21
])
# pixel offset
if
x
>
10
and
x
<
self
.
yxSize
[
1
]
and
y
>
10
and
y
<
self
.
yxSize
[
0
]:
self
.
nPixCell
=
700
/
self
.
env
.
rail
.
width
# 35
self
.
writableData
[
y
-
2
:
y
+
2
,
x
-
2
:
x
+
2
,
:]
=
0
self
.
oRT
=
rt
.
RenderTool
(
env
)
def
setDebug
(
self
,
dEvent
):
def
xy_to_rc
(
self
,
x
,
y
):
self
.
debug
=
dEvent
[
"
new
"
]
rcCell
=
((
array
([
y
,
x
])
-
self
.
yxBase
)
/
self
.
nPixCell
).
astype
(
int
)
self
.
log
(
"
Set Debug:
"
,
self
.
debug
)
return
rcCell
def
setDebugMove
(
self
,
dEvent
):
def
log
(
self
,
*
args
,
**
kwargs
):
self
.
debug_move
=
dEvent
[
"
new
"
]
if
self
.
wOutput
:
self
.
log
(
"
Set DebugMove:
"
,
self
.
debug
)
with
self
.
wOutput
:
print
(
*
args
,
**
kwargs
)
else
:
print
(
*
args
,
**
kwargs
)
def
setOutput
(
self
,
wid_output
):
self
.
wid_output
=
wid_output
def
setDrawMode
(
self
,
dEvent
):
class
Controller
(
object
):
self
.
drawMode
=
dEvent
[
"
new
"
]
"""
Controller to handle incoming events from the ipywidgets
Updates the editor/model.
Calls the View directly for things which do not directly effect the model
(this means the mouse drag path before it is interpreted as transitions)
"""
def
__init__
(
self
,
model
,
view
):
self
.
editor
=
self
.
model
=
model
self
.
view
=
view
self
.
qEvents
=
deque
()
self
.
drawMode
=
"
Draw
"
def
setModel
(
self
,
model
):
self
.
model
=
model
def
on_click
(
self
,
wid
,
event
):
def
on_click
(
self
,
wid
,
event
):
x
=
event
[
'
canvasX
'
]
x
=
event
[
'
canvasX
'
]
y
=
event
[
'
canvasY
'
]
y
=
event
[
'
canvasY
'
]
rcCell
=
((
array
([
y
,
x
])
-
self
.
yxBase
)
/
self
.
nPixCell
).
astype
(
i
nt
)
self
.
debug
(
"
debug:
"
,
eve
nt
)
if
self
.
drawMode
==
"
Origin
"
:
rcCell
=
self
.
view
.
xy_to_rc
(
x
,
y
)
self
.
iAgent
=
self
.
env
.
add_agent
(
rcCell
,
rcCell
,
None
)
self
.
drawMode
=
"
Destination
"
self
.
player
=
None
# will need to start a new player
elif
self
.
drawMode
==
"
Destination
"
and
self
.
iAgent
is
not
None
:
bShift
=
event
[
"
shiftKey
"
]
self
.
env
.
agents_target
[
self
.
iAgent
]
=
rcCell
bCtrl
=
event
[
"
ctrlKey
"
]
self
.
drawMode
=
"
Origin
"
if
bCtrl
and
not
bShift
:
self
.
model
.
add_agent
(
rcCell
)
# self.log("agent", self.drawMode, self.iAgent, rcCell)
elif
bShift
and
bCtrl
:
self
.
model
.
add_target
(
rcCell
)
if
self
.
debug
:
self
.
debug
(
"
click in cell
"
,
rcCell
)
self
.
log
(
"
debug:
"
,
event
)
self
.
model
.
debug_cell
(
rcCell
)
binTrans
=
self
.
env
.
rail
.
get_transitions
(
rcCell
)
sbinTrans
=
format
(
binTrans
,
"
#018b
"
)[
2
:]
self
.
log
(
"
cell
"
,
rcCell
,
"
Transitions:
"
,
binTrans
,
sbinTrans
,
[
sbinTrans
[
i
:
i
+
4
]
for
i
in
range
(
0
,
len
(
sbinTrans
),
4
)])
self
.
redraw
()
def
setDebug
(
self
,
dEvent
):
self
.
model
.
setDebug
(
dEvent
[
"
new
"
])
def
setDebugMove
(
self
,
dEvent
):
self
.
model
.
setDebug_move
(
dEvent
[
"
new
"
])
def
setDrawMode
(
self
,
dEvent
):
self
.
drawMode
=
dEvent
[
"
new
"
]
def
setFilename
(
self
,
event
):
self
.
model
.
setFilename
(
event
[
"
new
"
])
def
event_handler
(
self
,
wid
,
event
):
def
on_mouse_move
(
self
,
wid
,
event
):
"""
Mouse motion event handler for drawing.
"""
Mouse motion event handler for drawing.
"""
"""
x
=
event
[
'
canvasX
'
]
x
=
event
[
'
canvasX
'
]
y
=
event
[
'
canvasY
'
]
y
=
event
[
'
canvasY
'
]
env
=
self
.
env
qEvents
=
self
.
qEvents
qEvents
=
self
.
qEvents
lrcStroke
=
self
.
lrcStroke
bRedrawn
=
False
writableData
=
None
if
self
.
d
ebug
and
(
event
[
"
buttons
"
]
>
0
or
self
.
d
ebug_move
):
if
self
.
model
.
bD
ebug
and
(
event
[
"
buttons
"
]
>
0
or
self
.
model
.
bD
ebug_move
):
self
.
lo
g
(
"
debug:
"
,
len
(
qEvents
),
len
(
lrcStroke
),
event
)
self
.
debu
g
(
"
debug:
"
,
len
(
qEvents
),
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.
# The intention was to avoid too many redraws.
...
@@ -134,66 +236,161 @@ class JupEditor(object):
...
@@ -134,66 +236,161 @@ class JupEditor(object):
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
height
,
width
=
wid
.
data
.
shape
[:
2
]
# height, width = wid.data.shape[:2]
writableData
=
np
.
copy
(
self
.
wid_img
.
data
)
# writable copy of image - wid_img.data is somehow readonly
# writableData = np.copy(self.wid_img.data) # writable copy of image - wid_img.data is somehow readonly
# with self.wid_img.hold_sync():
with
self
.
wid_img
.
hold_sync
():
while
len
(
qEvents
)
>
0
:
while
len
(
qEvents
)
>
0
:
t
,
x
,
y
=
qEvents
.
popleft
()
# get events from our queue
t
,
x
,
y
=
qEvents
.
popleft
()
# get events from our queue
self
.
view
.
drag_path_element
(
x
,
y
)
# Translate and scale from x,y to integer row,col (note order change)
# rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int)
rcCell
=
self
.
view
.
xy_to_rc
(
x
,
y
)
self
.
editor
.
drag_path_element
(
rcCell
)
# 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)
# lrcStroke.append(rcCell)
# else:
# # This is the first cell in a mouse stroke
# lrcStroke.append(rcCell)
self
.
view
.
redisplayImage
()
else
:
self
.
model
.
mod_path
(
not
event
[
"
shiftKey
"
])
def
refresh
(
self
,
event
):
self
.
debug
(
"
refresh
"
)
self
.
view
.
redraw
()
def
clear
(
self
,
event
):
self
.
model
.
clear
()
def
regenerate
(
self
,
event
):
self
.
model
.
regenerate
()
def
setRegenSize
(
self
,
event
):
self
.
model
.
setRegenSize
(
event
[
"
new
"
])
def
load
(
self
,
event
):
self
.
model
.
load
()
# Draw a black square
def
save
(
self
,
event
):
if
x
>
10
and
x
<
width
and
y
>
10
and
y
<
height
:
self
.
model
.
save
()
writableData
[
y
-
2
:
y
+
2
,
x
-
2
:
x
+
2
,
:]
=
0
def
step
(
self
,
event
):
self
.
model
.
step
()
def
start_run
(
self
,
event
):
self
.
model
.
start_run
()
def
log
(
self
,
*
args
,
**
kwargs
):
if
self
.
view
is
None
:
print
(
*
args
,
**
kwargs
)
else
:
self
.
view
.
log
(
*
args
,
**
kwargs
)
def
debug
(
self
,
*
args
,
**
kwargs
):
self
.
model
.
debug
(
*
args
,
**
kwargs
)
class
EditorModel
(
object
):
def
__init__
(
self
,
env
):
self
.
view
=
None
self
.
env
=
env
self
.
regen_size
=
10
self
.
lrcStroke
=
[]
self
.
iTransLast
=
-
1
self
.
gRCTrans
=
array
([[
-
1
,
0
],
[
0
,
1
],
[
1
,
0
],
[
0
,
-
1
]])
# NESW in RC
self
.
bDebug
=
False
self
.
bDebug_move
=
False
self
.
wid_output
=
None
self
.
drawMode
=
"
Draw
"
self
.
env_filename
=
"
temp.npy
"
self
.
set_env
(
env
)
self
.
iAgent
=
None
self
.
player
=
None
self
.
thread
=
None
def
set_env
(
self
,
env
):
"""
set a new env for the editor, used by load and regenerate.
"""
self
.
env
=
env
self
.
yxBase
=
array
([
6
,
21
])
# pixel offset
self
.
nPixCell
=
700
/
self
.
env
.
rail
.
width
# 35
self
.
oRT
=
rt
.
RenderTool
(
env
)
def
setDebug
(
self
,
bDebug
):
self
.
bDebug
=
bDebug
self
.
log
(
"
Set Debug:
"
,
self
.
bDebug
)
def
setDebugMove
(
self
,
bDebug
):
self
.
bDebug_move
=
bDebug
self
.
log
(
"
Set DebugMove:
"
,
self
.
bDebug_move
)
def
setDrawMode
(
self
,
sDrawMode
):
self
.
drawMode
=
sDrawMode
def
drag_path_element
(
self
,
rcCell
):
"""
Mouse motion event handler for drawing.
"""
lrcStroke
=
self
.
lrcStroke
# Translate and scale from x,y to integer row,col (note order change)
# Store the row,col location of the click, if we have entered a new cell
rcCell
=
((
array
([
y
,
x
])
-
self
.
yxBase
)
/
self
.
nPixCell
).
astype
(
int
)
if
len
(
lrcStroke
)
>
0
:
rcLast
=
lrcStroke
[
-
1
]
# Store the row,col location of the click, if we have entered a new cell
if
not
np
.
array_equal
(
rcLast
,
rcCell
):
# only save at transition
if
len
(
lrcStroke
)
>
0
:
lrcStroke
.
append
(
rcCell
)
rcLast
=
lrcStroke
[
-
1
]
self
.
debug
(
"
lrcStroke
"
,
len
(
lrcStroke
),
rcCell
)
if
not
np
.
array_equal
(
rcLast
,
rcCell
):
# only save at transition
# print(y, x, rcCell)
else
:
lrcStroke
.
append
(
rcCell
)
# This is the first cell in a mouse stroke
else
:
lrcStroke
.
append
(
rcCell
)
# This is the first cell in a mouse stroke
self
.
debug
(
"
lrcStroke
"
,
len
(
lrcStroke
),
rcCell
)
lrcStroke
.
append
(
rcCell
)
def
mod_path
(
self
,
bAddRemove
):
# This elif means we wait until all the mouse events have been processed (black square drawn)
# 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)
# 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:
# Equivalent to waiting for mouse button to be lifted (and a mouse event is necessary:
# the mouse may need to be moved)
# the mouse may need to be moved)
elif
len
(
lrcStroke
)
>=
3
:
lrcStroke
=
self
.
lrcStroke
# If we have already touched 3 cells
if
len
(
lrcStroke
)
>=
2
:
# We have a transition into a cell, and out of it.
self
.
mod_rail_cell_seq
(
lrcStroke
,
bAddRemove
)
self
.
redraw
()
if
self
.
drawMode
==
"
Draw
"
:
bAddRemove
=
True
elif
self
.
drawMode
==
"
Erase
"
:
bAddRemove
=
False
def
mod_rail_cell_seq
(
self
,
lrcStroke
,
bAddRemove
=
True
):
# If we have already touched 3 cells
# We have a transition into a cell, and out of it.
if
len
(
lrcStroke
)
>=
2
:
# If the first cell in a stroke is empty, add a deadend to cell 0
# If the first cell in a stroke is empty, add a deadend to cell 0
if
self
.
env
.
rail
.
get_transitions
(
lrcStroke
[
0
])
==
0
:
if
self
.
env
.
rail
.
get_transitions
(
lrcStroke
[
0
])
==
0
:
self
.
ad
d_rail_2cells
(
lrcStroke
,
bAddRemove
,
iCellToMod
=
0
)
self
.
mo
d_rail_2cells
(
lrcStroke
,
bAddRemove
,
iCellToMod
=
0
)
while
len
(
lrcStroke
)
>=
3
:
# Add transitions for groups of 3 cells
self
.
add_rail_3cells
(
lrcStroke
,
env
,
bAddRemove
)
# hence inbound and outbound transitions for middle cell
while
len
(
lrcStroke
)
>=
3
:
self
.
mod_rail_3cells
(
lrcStroke
,
bAddRemove
=
bAddRemove
)
# If final cell empty, insert deadend:
# If final cell empty, insert deadend:
if
len
(
lrcStroke
)
==
2
and
(
self
.
env
.
rail
.
get_transitions
(
lrcStroke
[
1
])
==
0
):
if
len
(
lrcStroke
)
==
2
:
self
.
add_rail_2cells
(
lrcStroke
,
bAddRemove
,
iCellToMod
=
1
)
if
self
.
env
.
rail
.
get_transitions
(
lrcStroke
[
1
])
==
0
:
self
.
mod_rail_2cells
(
lrcStroke
,
bAddRemove
,
iCellToMod
=
1
)
self
.
redraw
()
# now empty out the final two cells from the queue
bRedrawn
=
True
lrcStroke
.
clear
()
# only redraw with the dots/squares if necessary
def
mod_rail_3cells
(
self
,
lrcStroke
,
bAddRemove
=
True
,
bPop
=
True
):
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.
Add transitions for rail spanning three cells.
lrc
Cells
-- list
of 3 rc cells
lrc
Stroke
-- list
containing
"
stroke
"
of cells across grid
bAddRemove -- whether to add (True) or remove (False) the transition
bAddRemove -- whether to add (True) or remove (False) the transition
The transition is added to or removed from the 2nd cell, consistent with
The transition is added to or removed from the 2nd cell, consistent with
entering from the 1st cell, and exiting into the 3rd.
entering from the 1st cell, and exiting into the 3rd.
...
@@ -201,15 +398,15 @@ class JupEditor(object):
...
@@ -201,15 +398,15 @@ class JupEditor(object):
eg rcCells [(3,4), (2,4), (2,5)] would result in the transitions
eg rcCells [(3,4), (2,4), (2,5)] would result in the transitions
N->E and W->S in cell (2,4).
N->E and W->S in cell (2,4).
"""
"""
rc3Cells
=
array
(
rc
Cells
[:
3
])
# the 3 cells
rc3Cells
=
array
(
l
rc
Stroke
[:
3
])
# the 3 cells
rcMiddle
=
rc3Cells
[
1
]
# the middle cell which we will update
rcMiddle
=
rc3Cells
[
1
]
# the middle cell which we will update
bDeadend
=
np
.
all
(
rc
Cells
[
0
]
==
rc
Cells
[
2
])
# deadend means cell 0 == cell 2
bDeadend
=
np
.
all
(
l
rc
Stroke
[
0
]
==
l
rc
Stroke
[
2
])
# deadend means cell 0 == cell 2
# Save the original state of the cell
# Save the original state of the cell
# oTransrcMiddle = self.env.rail.get_transitions(rcMiddle)
# oTransrcMiddle = self.env.rail.get_transitions(rcMiddle)
# sTransrcMiddle = self.env.rail.cell_repr(rcMiddle)
# sTransrcMiddle = self.env.rail.cell_repr(rcMiddle)
# get the 2 row, col deltas between the 3 cells, eg [-1,0] = North
# get the 2 row, col deltas between the 3 cells, eg
[
[-1,0]
,[0,1]]
= North
, East
rc2Trans
=
np
.
diff
(
rc3Cells
,
axis
=
0
)
rc2Trans
=
np
.
diff
(
rc3Cells
,
axis
=
0
)
# get the direction index for the 2 transitions
# get the direction index for the 2 transitions
...
@@ -246,9 +443,9 @@ class JupEditor(object):
...
@@ -246,9 +443,9 @@ class JupEditor(object):
# self.log(rcMiddle, "Orig:", sTransrcMiddle, "Mod:", self.env.rail.cell_repr(rcMiddle))
# self.log(rcMiddle, "Orig:", sTransrcMiddle, "Mod:", self.env.rail.cell_repr(rcMiddle))
if
bPop
:
if
bPop
:
rc
Cells
.
pop
(
0
)
# remove the first cell in the stroke
l
rc
Stroke
.
pop
(
0
)
# remove the first cell in the stroke
def
ad
d_rail_2cells
(
self
,
lrcCells
,
bAddRemove
=
True
,
iCellToMod
=
0
,
bPop
=
False
):
def
mo
d_rail_2cells
(
self
,
lrcCells
,
bAddRemove
=
True
,
iCellToMod
=
0
,
bPop
=
False
):
"""
"""
Add transitions for rail between two cells
Add transitions for rail between two cells
lrcCells -- list of two rc cells
lrcCells -- list of two rc cells
...
@@ -283,31 +480,10 @@ class JupEditor(object):
...
@@ -283,31 +480,10 @@ class JupEditor(object):
if
bPop
:
if
bPop
:
lrcCells
.
pop
(
0
)
lrcCells
.
pop
(
0
)
def
redraw
(
self
,
hide_stdout
=
True
,
update
=
True
):
def
redraw
(
self
):
self
.
view
.
redraw
()
# if hide_stdout:
# stdout_dest = os.devnull
# else:
# stdout_dest = sys.stdout
# TODO: bit of a hack - can we suppress the console messages from MPL at source?
def
clear
(
self
):
# with redirect_stdout(stdout_dest):
with
self
.
wid_output
:
plt
.
figure
(
figsize
=
(
10
,
10
))
self
.
oRT
.
renderEnv
(
spacing
=
False
,
arrows
=
False
,
sRailColor
=
"
gray
"
,
show
=
False
)
img
=
self
.
oRT
.
getImage
()
plt
.
clf
()
plt
.
close
()
if
update
:
self
.
wid_img
.
data
=
img
return
img
def
redraw_event
(
self
,
event
):
img
=
self
.
redraw
()
self
.
wid_img
.
data
=
img
def
clear
(
self
,
event
):
self
.
env
.
rail
.
grid
[:,
:]
=
0
self
.
env
.
rail
.
grid
[:,
:]
=
0
self
.
env
.
number_of_agents
=
0
self
.
env
.
number_of_agents
=
0
self
.
env
.
agents_position
=
[]
self
.
env
.
agents_position
=
[]
...
@@ -315,48 +491,62 @@ class JupEditor(object):
...
@@ -315,48 +491,62 @@ class JupEditor(object):
self
.
env
.
agents_handles
=
[]
self
.
env
.
agents_handles
=
[]
self
.
env
.
agents_target
=
[]
self
.
env
.
agents_target
=
[]
self
.
player
=
None
self
.
player
=
None
self
.
redraw
_event
(
event
)
self
.
redraw
(
)
def
setFilename
(
self
,
filename
):
def
setFilename
(
self
,
filename
):
self
.
log
(
"
filename =
"
,
filename
,
type
(
filename
))
self
.
log
(
"
filename =
"
,
filename
,
type
(
filename
))
self
.
env_filename
=
filename
self
.
env_filename
=
filename
def
setFilename_event
(
self
,
event
):
def
load
(
self
):
self
.
setFilename
(
event
[
"
new
"
])
if
os
.
path
.
exists
(
self
.
env_filename
):
self
.
log
(
"
load file:
"
,
self
.
env_filename
)
def
load
(
self
,
event
):
self
.
env
.
rail
.
load_transition_map
(
self
.
env_filename
,
override_gridsize
=
True
)
self
.
env
.
rail
.
load_transition_map
(
self
.
env_filename
,
override_gridsize
=
True
)
self
.
fix_env
()
self
.
fix_env
()
self
.
set_env
(
self
.
env
)
self
.
set_env
(
self
.
env
)
self
.
redraw
()
self
.
wid_img
.
data
=
self
.
redraw
()
else
:
self
.
log
(
"
File does not exist:
"
,
self
.
env_filename
,
"
Working directory:
"
,
os
.
getcwd
())
def
save
(
self
,
event
):
def
save
(
self
):
self
.
log
(
"
save to
"
,
self
.
env_filename
)
self
.
log
(
"
save to
"
,
self
.
env_filename
,
"
working dir:
"
,
os
.
getcwd
()
)
self
.
env
.
rail
.
save_transition_map
(
self
.
env_filename
)
self
.
env
.
rail
.
save_transition_map
(
self
.
env_filename
)
def
regenerate_event
(
self
,
event
):
def
regenerate
(
self
):
self
.
log
(
"
Regenerate size
"
,
self
.
regen_size
)
self
.
env
=
RailEnv
(
width
=
self
.
regen_size
,
self
.
env
=
RailEnv
(
width
=
self
.
regen_size
,
height
=
self
.
regen_size
,
height
=
self
.
regen_size
,
rail_generator
=
random_rail_generator
(
cell_type_relative_proportion
=
[
1
,
1
]
+
[
0.5
]
*
6
),
rail_generator
=
random_rail_generator
(
cell_type_relative_proportion
=
[
1
,
1
]
+
[
0.5
]
*
6
),
number_of_agents
=
self
.
env
.
number_of_agents
,
number_of_agents
=
self
.
env
.
number_of_agents
,
obs_builder_object
=
TreeObsForRailEnv
(
max_depth
=
2
))
obs_builder_object
=
TreeObsForRailEnv
(
max_depth
=
2
))
self
.
env
.
reset
(
regen_rail
=
True
)
self
.
env
.
reset
(
regen_rail
=
True
)
self
.
fix_env
()
self
.
set_env
(
self
.
env
)
self
.
set_env
(
self
.
env
)
self
.
player
=
Player
(
self
.
env
)
self
.
player
=
Player
(
self
.
env
)
self
.
view
.
new_env
()
self
.
redraw
()
self
.
redraw
()
def
setRegenSize
_event
(
self
,
event
):
def
setRegenSize
(
self
,
size
):
self
.
regen_size
=
event
[
"
new
"
]
self
.
regen_size
=
size
def
step_event
(
self
,
event
=
None
):
def
add_agent
(
self
,
rcCell
):
self
.
iAgent
=
self
.
env
.
add_agent
(
rcCell
,
rcCell
,
None
)
self
.
player
=
None
# will need to start a new player
self
.
redraw
()
def
add_target
(
self
,
rcCell
):
if
self
.
iAgent
is
not
None
:
self
.
env
.
agents_target
[
self
.
iAgent
]
=
rcCell
self
.
redraw
()
def
step
(
self
):
if
self
.
player
is
None
:
if
self
.
player
is
None
:
self
.
player
=
Player
(
self
.
env
)
self
.
player
=
Player
(
self
.
env
)
self
.
env
.
reset
(
regen_rail
=
False
,
replace_agents
=
False
)
self
.
env
.
reset
(
regen_rail
=
False
,
replace_agents
=
False
)
self
.
player
.
step
()
self
.
player
.
step
()
self
.
redraw
()
self
.
redraw
()
def
start_run
_event
(
self
,
event
=
None
):
def
start_run
(
self
):
if
self
.
thread
is
None
:
if
self
.
thread
is
None
:
self
.
thread
=
threading
.
Thread
(
target
=
self
.
bg_updater
,
args
=
())
self
.
thread
=
threading
.
Thread
(
target
=
self
.
bg_updater
,
args
=
())
self
.
thread
.
start
()
self
.
thread
.
start
()
...
@@ -367,7 +557,7 @@ class JupEditor(object):
...
@@ -367,7 +557,7 @@ class JupEditor(object):
try
:
try
:
for
i
in
range
(
20
):
for
i
in
range
(
20
):
# self.log("step ", i)
# self.log("step ", i)
self
.
step
_event
()
self
.
step
()
time
.
sleep
(
0.2
)
time
.
sleep
(
0.2
)
finally
:
finally
:
self
.
thread
=
None
self
.
thread
=
None
...
@@ -377,10 +567,17 @@ class JupEditor(object):
...
@@ -377,10 +567,17 @@ class JupEditor(object):
self
.
env
.
height
=
self
.
env
.
rail
.
height
self
.
env
.
height
=
self
.
env
.
rail
.
height
def
log
(
self
,
*
args
,
**
kwargs
):
def
log
(
self
,
*
args
,
**
kwargs
):
if
self
.
view
is
None
:
if
self
.
wid_output
:
with
self
.
wid_output
:
print
(
*
args
,
**
kwargs
)
else
:
print
(
*
args
,
**
kwargs
)
print
(
*
args
,
**
kwargs
)
else
:
self
.
view
.
log
(
*
args
,
**
kwargs
)
def
debug
(
self
,
*
args
,
**
kwargs
):
if
self
.
bDebug
:
self
.
log
(
*
args
,
**
kwargs
)
def
debug_cell
(
self
,
rcCell
):
binTrans
=
self
.
env
.
rail
.
get_transitions
(
rcCell
)
sbinTrans
=
format
(
binTrans
,
"
#018b
"
)[
2
:]
self
.
debug
(
"
cell
"
,
rcCell
,
"
Transitions:
"
,
binTrans
,
sbinTrans
,
[
sbinTrans
[
i
:
i
+
4
]
for
i
in
range
(
0
,
len
(
sbinTrans
),
4
)])
This diff is collapsed.
Click to expand it.
notebooks/Editor2.ipynb
0 → 100644
+
232
−
0
View file @
404d650a
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Rail Editor v0.2"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from numpy import array\n",
"import ipywidgets\n",
"import IPython\n",
"from IPython.core.display import display, HTML"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<style>.container { width:90% !important; }</style>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"display(HTML(\"<style>.container { width:90% !important; }</style>\"))"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"cpu\n"
]
}
],
"source": [
"from flatland.utils.editor import EditorMVC, EditorModel, View, Controller"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Figure size 720x720 with 0 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"mvc = EditorMVC()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Instructions\n",
"- Drag to draw\n",
" - improved dead-ends\n",
"- Shift-Drag to erase rails\n",
" - erasing dead ends not yet automated - drag right across them\n",
"- ctrl-click to add agent\n",
" - direction chosen randomly to fit rail\n",
"- ctrl-shift-click to add target for last agent\n",
" - target can be moved by repeating "
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "a1884dec581441f4948f33ffa9cac53d",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(Canvas(), VBox(children=(Text(value='temp.npy', description='Filename'), Button(description='Re…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"mvc.view.display()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "647f8e9c1227404eb58393eca27af3c5",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Output()"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"mvc.view.wOutput.clear_output()\n",
"mvc.view.wOutput"
]
}
],
"metadata": {
"hide_input": false,
"kernelspec": {
"display_name": "ve367",
"language": "python",
"name": "ve367"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.7"
},
"latex_envs": {
"LaTeX_envs_menu_present": true,
"autoclose": false,
"autocomplete": true,
"bibliofile": "biblio.bib",
"cite_by": "apalike",
"current_citInitial": 1,
"eqLabelWithNumbers": true,
"eqNumInitial": 1,
"hotkeys": {
"equation": "Ctrl-E",
"itemize": "Ctrl-I"
},
"labels_anchors": false,
"latex_user_defs": false,
"report_style_numbering": false,
"user_envs_cfg": false
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 2
}
%% Cell type:markdown id: tags:
# Rail Editor v0.2
%% Cell type:code id: tags:
```
python
import
numpy
as
np
from
numpy
import
array
import
ipywidgets
import
IPython
from
IPython.core.display
import
display
,
HTML
```
%% Cell type:code id: tags:
```
python
display
(
HTML
(
"
<style>.container { width:90% !important; }</style>
"
))
```
%% Output
%% Cell type:code id: tags:
```
python
from
flatland.utils.editor
import
EditorMVC
,
EditorModel
,
View
,
Controller
```
%% Output
cpu
%% Cell type:code id: tags:
```
python
mvc
=
EditorMVC
()
```
%% Output
%% Cell type:markdown id: tags:
## Instructions
-
Drag to draw
-
improved dead-ends
-
Shift-Drag to erase rails
-
erasing dead ends not yet automated - drag right across them
-
ctrl-click to add agent
-
direction chosen randomly to fit rail
-
ctrl-shift-click to add target for last agent
-
target can be moved by repeating
%% Cell type:code id: tags:
```
python
mvc
.
view
.
display
()
```
%% Output
%% Cell type:code id: tags:
```
python
mvc
.
view
.
wOutput
.
clear_output
()
mvc
.
view
.
wOutput
```
%% Output
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment