Mturk Drawing with padding
This commit is contained in:
parent
a43437188b
commit
1926e2be24
13 changed files with 334 additions and 133 deletions
18
config.yml
18
config.yml
|
@ -3,11 +3,25 @@ amazon:
|
||||||
user_secret: "213j234/234sksjdfus83jd"
|
user_secret: "213j234/234sksjdfus83jd"
|
||||||
mturk_sandbox: true
|
mturk_sandbox: true
|
||||||
mturk_region: us-east-1
|
mturk_region: us-east-1
|
||||||
|
sqs_endpoint_url: "https://sqs.eu-west-3.amazonaws.com/"
|
||||||
sqs_url: "https://sqs.eu-west-3.amazonaws.com/60123456789/your_queue"
|
sqs_url: "https://sqs.eu-west-3.amazonaws.com/60123456789/your_queue"
|
||||||
sqs_region_name: "eu-west-3"
|
sqs_region_name: "eu-west-3"
|
||||||
task_xml: "mt_task.xml"
|
task_xml: "mt_task.xml"
|
||||||
hit_db: store.db
|
hit_db: store.db
|
||||||
hour_rate_aim: 15
|
hour_rate_aim: 15
|
||||||
hit_lifetime: 54000 ;15*60*60
|
hit_lifetime: 54000 #15*60*60
|
||||||
hit_assignment_duration: 300 ; 5*60
|
hit_assignment_duration: 300 # 5*60
|
||||||
hit_autoapprove_delay: 3600
|
hit_autoapprove_delay: 3600
|
||||||
|
dummy_plotter: false
|
||||||
|
server:
|
||||||
|
port: 8888
|
||||||
|
scanner: # size of scanarea in mm
|
||||||
|
# total visible glass (and size of the scan)
|
||||||
|
width: 255
|
||||||
|
height: 185
|
||||||
|
# area which can be removed
|
||||||
|
draw_width: 255
|
||||||
|
draw_height: 70
|
||||||
|
# part of scanner that is invissible left & top
|
||||||
|
left_padding: 0
|
||||||
|
top_padding: 45
|
||||||
|
|
|
@ -61,7 +61,7 @@ class HIT(Base):
|
||||||
return os.path.join('www', self.getImageUrl())
|
return os.path.join('www', self.getImageUrl())
|
||||||
|
|
||||||
def getImageUrl(self):
|
def getImageUrl(self):
|
||||||
return f"scans/{self.id}.png"
|
return f"scans/{self.id}.jpg"
|
||||||
|
|
||||||
def getStatus(self):
|
def getStatus(self):
|
||||||
if self.scanned_at:
|
if self.scanned_at:
|
||||||
|
|
BIN
sorteerhoed/__pycache__/HITStore.cpython-37.pyc
Normal file
BIN
sorteerhoed/__pycache__/HITStore.cpython-37.pyc
Normal file
Binary file not shown.
BIN
sorteerhoed/__pycache__/Signal.cpython-37.pyc
Normal file
BIN
sorteerhoed/__pycache__/Signal.cpython-37.pyc
Normal file
Binary file not shown.
BIN
sorteerhoed/__pycache__/__init__.cpython-37.pyc
Normal file
BIN
sorteerhoed/__pycache__/__init__.cpython-37.pyc
Normal file
Binary file not shown.
BIN
sorteerhoed/__pycache__/central_management.cpython-37.pyc
Normal file
BIN
sorteerhoed/__pycache__/central_management.cpython-37.pyc
Normal file
Binary file not shown.
BIN
sorteerhoed/__pycache__/plotter.cpython-37.pyc
Normal file
BIN
sorteerhoed/__pycache__/plotter.cpython-37.pyc
Normal file
Binary file not shown.
BIN
sorteerhoed/__pycache__/sqs.cpython-37.pyc
Normal file
BIN
sorteerhoed/__pycache__/sqs.cpython-37.pyc
Normal file
Binary file not shown.
BIN
sorteerhoed/__pycache__/webserver.cpython-37.pyc
Normal file
BIN
sorteerhoed/__pycache__/webserver.cpython-37.pyc
Normal file
Binary file not shown.
|
@ -300,8 +300,11 @@ class CentralManagement():
|
||||||
Run scanimage on scaner and returns a string with the filename
|
Run scanimage on scaner and returns a string with the filename
|
||||||
"""
|
"""
|
||||||
self.eventQueue.put(Signal('scan.start'))
|
self.eventQueue.put(Signal('scan.start'))
|
||||||
|
|
||||||
cmd = [
|
cmd = [
|
||||||
'sudo', 'scanimage', '-d', 'epkowa'
|
'sudo', 'scanimage', '-d', 'epkowa', '--format', 'jpeg',
|
||||||
|
'--resolution=100', '-l','20','-t','30','-x',(self.config['scanner']['height']),
|
||||||
|
'-y',str(self.config['scanner']['width'])
|
||||||
]
|
]
|
||||||
filename = self.currentHit.getImagePath()
|
filename = self.currentHit.getImagePath()
|
||||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
@ -313,6 +316,7 @@ class CentralManagement():
|
||||||
|
|
||||||
f = io.BytesIO(o)
|
f = io.BytesIO(o)
|
||||||
img = Image.open(f)
|
img = Image.open(f)
|
||||||
|
img = img.transpose(Image.ROTATE_90)
|
||||||
img.save(filename)
|
img.save(filename)
|
||||||
|
|
||||||
self.eventQueue.put(Signal('hit.scanned', {'hit_id':self.currentHit.id}))
|
self.eventQueue.put(Signal('hit.scanned', {'hit_id':self.currentHit.id}))
|
||||||
|
|
|
@ -6,17 +6,6 @@ from threading import Event
|
||||||
from sorteerhoed.Signal import Signal
|
from sorteerhoed.Signal import Signal
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class PathSegment:
|
|
||||||
def __init__(self):
|
|
||||||
self.d = []
|
|
||||||
|
|
||||||
def add(self, i):
|
|
||||||
# self.d.append(i)
|
|
||||||
|
|
||||||
def get(self, prop):
|
|
||||||
if prop =='d':
|
|
||||||
return self.d
|
|
||||||
|
|
||||||
class Plotter:
|
class Plotter:
|
||||||
def __init__(self, config, eventQ: Queue, runningEvent: Event):
|
def __init__(self, config, eventQ: Queue, runningEvent: Event):
|
||||||
#TODO: scanningEvent -> CentralManagement.isScanning -> prevent plotter move during scan, failsafe
|
#TODO: scanningEvent -> CentralManagement.isScanning -> prevent plotter move during scan, failsafe
|
||||||
|
@ -25,6 +14,13 @@ class Plotter:
|
||||||
self.q = Queue()
|
self.q = Queue()
|
||||||
self.isRunning = runningEvent
|
self.isRunning = runningEvent
|
||||||
self.logger = logging.getLogger("sorteerhoed").getChild("plotter")
|
self.logger = logging.getLogger("sorteerhoed").getChild("plotter")
|
||||||
|
self.pen_down = False
|
||||||
|
|
||||||
|
self.plotWidth = self.config['scanner']['width'] / 10 / 2.54
|
||||||
|
self.plotHeight = self.config['scanner']['height'] / 10 / 2.54
|
||||||
|
self.xPadding = self.config['scanner']['left_padding'] / 10 / 2.54;
|
||||||
|
self.yPadding = self.config['scanner']['top_padding'] / 10 / 2.54;
|
||||||
|
self.logger.info(f"Paddings x: {self.xPadding} inch y: {self.yPadding} inch")
|
||||||
|
|
||||||
def park(self):
|
def park(self):
|
||||||
self.logger.info("Queue to park plotter")
|
self.logger.info("Queue to park plotter")
|
||||||
|
@ -37,29 +33,8 @@ class Plotter:
|
||||||
self.q.put([0,0,0])
|
self.q.put([0,0,0])
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.axiDrawCueListener()
|
|
||||||
|
|
||||||
def axiDrawCueListener(self):
|
|
||||||
if self.config['dummy_plotter']:
|
|
||||||
while self.isRunning.is_set():
|
|
||||||
# TODO: queue that collects a part of the path data
|
|
||||||
# on a specific limit or on a specific time interval, do the plot
|
|
||||||
# also, changing ad.pen_raise() or ad.pen_lower() trigger a new segment
|
|
||||||
# Plot with ad.plan_trajectory() ??
|
|
||||||
plotterRan = False
|
|
||||||
try:
|
try:
|
||||||
move = self.q.get(True, 1)
|
if not self.config['dummy_plotter']:
|
||||||
plotterRan = True
|
|
||||||
except queue.Empty as e:
|
|
||||||
self.logger.log(5, "Empty queue.")
|
|
||||||
if plotterRan:
|
|
||||||
plotterRan = False
|
|
||||||
self.eventQ.put(Signal('plotter.finished'))
|
|
||||||
else:
|
|
||||||
time.sleep(.05)
|
|
||||||
self.logger.debug(f'Dummy plotter move: {move}')
|
|
||||||
self.logger.info("Stopping dummy plotter")
|
|
||||||
else:
|
|
||||||
self.ad = axidraw.AxiDraw()
|
self.ad = axidraw.AxiDraw()
|
||||||
|
|
||||||
self.ad.interactive()
|
self.ad.interactive()
|
||||||
|
@ -68,38 +43,108 @@ class Plotter:
|
||||||
connected = self.ad.connect()
|
connected = self.ad.connect()
|
||||||
if not connected:
|
if not connected:
|
||||||
raise Exception("Cannot connect to Axidraw")
|
raise Exception("Cannot connect to Axidraw")
|
||||||
try:
|
|
||||||
self.ad.options.units = 1 # set to use centimeters instead of inches
|
self.ad.options.units = 1 # set to use centimeters instead of inches
|
||||||
self.ad.options.accel = 100;
|
self.ad.options.accel = 100;
|
||||||
self.ad.options.speed_penup = 100
|
self.ad.options.speed_penup = 100
|
||||||
self.ad.options.speed_pendown = 100
|
self.ad.options.speed_pendown = 100
|
||||||
self.ad.options.model = 1 # A3, set to 1 for A4
|
self.ad.options.model = 1 # 2 for A3, 1 for A4
|
||||||
|
|
||||||
self.ad.moveto(0,0)
|
self.ad.moveto(0,0)
|
||||||
|
|
||||||
plotterWidth = 22
|
# plotterWidth = 25
|
||||||
plotterHeight = 18 # 16?
|
# plotterHeight = 21
|
||||||
|
|
||||||
plotterRan = False
|
|
||||||
while self.isRunning.is_set():
|
|
||||||
# TODO: set timeout on .get() with catch block, so we can escape if no moves come in
|
|
||||||
try:
|
|
||||||
move = self.q.get(True, 1)
|
|
||||||
plotterRan = True
|
|
||||||
except queue.Empty as e:
|
|
||||||
self.logger.log(5, "Empty queue.")
|
|
||||||
if plotterRan:
|
|
||||||
plotterRan = False
|
|
||||||
self.eventQ.put(Signal('plotter.finished'))
|
|
||||||
else:
|
else:
|
||||||
self.ad.moveto(move[0]* plotterWidth, move[1]*plotterHeight)
|
self.ad = None
|
||||||
self.logger.debug(f'handler! {move}')
|
self.axiDrawCueListener()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
finally:
|
finally:
|
||||||
self.logger.warning("Close Axidraw connection")
|
self.logger.warning("Close Axidraw connection")
|
||||||
|
if self.ad:
|
||||||
self.ad.moveto(0,0)
|
self.ad.moveto(0,0)
|
||||||
self.ad.disconnect()
|
self.ad.disconnect()
|
||||||
|
|
||||||
# send shutdown signal (if not already set)
|
# send shutdown signal (if not already set)
|
||||||
self.isRunning.clear()
|
self.isRunning.clear()
|
||||||
|
|
||||||
|
def draw_segments(self, segments = []):
|
||||||
|
coordinates = []
|
||||||
|
for segment in segments:
|
||||||
|
coordinate = [
|
||||||
|
# mm to cm to inches
|
||||||
|
(segment[0]) * self.plotWidth,
|
||||||
|
(1-segment[1]) * self.plotHeight
|
||||||
|
]
|
||||||
|
#prevent drawing when not in drwaing area
|
||||||
|
# this is a failsafe for a malicious working or glitching script, as this should also be done in the javascript
|
||||||
|
if self.pen_down:
|
||||||
|
if coordinate[0] < self.xPadding or coordinate[0] > self.xPadding+self.plotWidth or \
|
||||||
|
coordinate[1] < self.yPadding or coordinate[1] > self.yPadding + self.plotHeight:
|
||||||
|
self.logger.warn(f"Skip drawing for: {coordinates} out of bounds")
|
||||||
|
continue
|
||||||
|
|
||||||
|
coordinates.append(coordinate)
|
||||||
|
self.logger.info(f"Plot: {coordinates}")
|
||||||
|
if self.ad:
|
||||||
|
self.ad.plan_trajectory(coordinates)
|
||||||
|
# self.ad.moveto(move[0]* plotterWidth, move[1]*plotterHeight)
|
||||||
|
# self.logger.debug(f'handler! {move}')
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setPenDown(self, pen_state):
|
||||||
|
"""
|
||||||
|
False: pen raised, True: pen_lower
|
||||||
|
"""
|
||||||
|
if pen_state != self.pen_down:
|
||||||
|
self.pen_down = pen_state
|
||||||
|
self.logger.info("Changed pen: {}".format('down' if pen_state else 'up'))
|
||||||
|
if self.ad:
|
||||||
|
if pen_state:
|
||||||
|
self.ad.pen_lower()
|
||||||
|
else:
|
||||||
|
self.ad.pen_raise()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def axiDrawCueListener(self):
|
||||||
|
plotterRan = False
|
||||||
|
segments = []
|
||||||
|
while self.isRunning.is_set():
|
||||||
|
# TODO: queue that collects a part of the path data
|
||||||
|
# on a specific limit or on a specific time interval, do the plot
|
||||||
|
# also, changing ad.pen_raise() or ad.pen_lower() trigger a new segment
|
||||||
|
# Plot with ad.plan_trajectory() ??
|
||||||
|
try:
|
||||||
|
# if no info comes in for .5sec, plot that segment
|
||||||
|
segment = self.q.get(True, .5)
|
||||||
|
|
||||||
|
# change of pen state? draw previous segments!
|
||||||
|
if (segment[2] == 1 and not self.pen_down) or (segment[2] == 0 and self.pen_down) or len(segments) > 150:
|
||||||
|
if len(segments) > 1:
|
||||||
|
self.draw_segments(segments)
|
||||||
|
plotterRan = True
|
||||||
|
segments = [] #reset
|
||||||
|
|
||||||
|
# and change pen positions
|
||||||
|
self.setPenDown(segment[2] == 1)
|
||||||
|
|
||||||
|
|
||||||
|
segments.append(segment)
|
||||||
|
|
||||||
|
except queue.Empty as e:
|
||||||
|
self.logger.debug("Timeout queue.")
|
||||||
|
if len(segments):
|
||||||
|
# segments to plot!
|
||||||
|
self.draw_segments(segments)
|
||||||
|
plotterRan = True
|
||||||
|
segments = []
|
||||||
|
elif plotterRan:
|
||||||
|
plotterRan = False
|
||||||
|
self.eventQ.put(Signal('plotter.finished'))
|
||||||
|
# else:
|
||||||
|
# time.sleep(.05)
|
||||||
|
# self.logger.debug(f'Plotter move: {move}')
|
||||||
|
self.logger.info("Stopping plotter")
|
||||||
|
|
||||||
|
|
|
@ -233,9 +233,15 @@ def strokes2D(strokes):
|
||||||
return d;
|
return d;
|
||||||
|
|
||||||
class DrawPageHandler(tornado.web.RequestHandler):
|
class DrawPageHandler(tornado.web.RequestHandler):
|
||||||
def initialize(self, store: HITStore, path: str):
|
def initialize(self, store: HITStore, path: str, width: int, height: int, draw_width: int, draw_height: int, top_padding: int, left_padding: int):
|
||||||
self.store = store
|
self.store = store
|
||||||
self.path = path
|
self.path = path
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.draw_width = draw_width
|
||||||
|
self.draw_height = draw_height
|
||||||
|
self.top_padding = top_padding
|
||||||
|
self.left_padding = left_padding
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
try:
|
try:
|
||||||
|
@ -258,7 +264,14 @@ class DrawPageHandler(tornado.web.RequestHandler):
|
||||||
logger.info(f"Image url: {image}")
|
logger.info(f"Image url: {image}")
|
||||||
|
|
||||||
self.set_header("Access-Control-Allow-Origin", "*")
|
self.set_header("Access-Control-Allow-Origin", "*")
|
||||||
contents = open(os.path.join(self.path, 'index.html'), 'r').read().replace("{IMAGE_URL}", image)
|
contents = open(os.path.join(self.path, 'index.html'), 'r').read()
|
||||||
|
contents = contents.replace("{IMAGE_URL}", image)\
|
||||||
|
.replace("{WIDTH}", str(self.width))\
|
||||||
|
.replace("{HEIGHT}", str(self.height))\
|
||||||
|
.replace("{DRAW_WIDTH}", str(self.draw_width))\
|
||||||
|
.replace("{DRAW_HEIGHT}", str(self.draw_height))\
|
||||||
|
.replace("{TOP_PADDING}", str(self.left_padding))\
|
||||||
|
.replace("{LEFT_PADDING}", str(self.top_padding))
|
||||||
self.write(contents)
|
self.write(contents)
|
||||||
|
|
||||||
class StatusPage():
|
class StatusPage():
|
||||||
|
@ -342,7 +355,16 @@ class Server:
|
||||||
}),
|
}),
|
||||||
(r"/status/ws", StatusWebSocketHandler),
|
(r"/status/ws", StatusWebSocketHandler),
|
||||||
(r"/draw", DrawPageHandler,
|
(r"/draw", DrawPageHandler,
|
||||||
dict(store = self.store, path=self.web_root)),
|
dict(
|
||||||
|
store = self.store,
|
||||||
|
path=self.web_root,
|
||||||
|
width=self.config['scanner']['width'],
|
||||||
|
height=self.config['scanner']['height'],
|
||||||
|
draw_width=self.config['scanner']['draw_width'],
|
||||||
|
draw_height=self.config['scanner']['draw_height'],
|
||||||
|
top_padding=self.config['scanner']['top_padding'],
|
||||||
|
left_padding=self.config['scanner']['left_padding']
|
||||||
|
)),
|
||||||
(r"/(.*)", StaticFileWithHeaderHandler,
|
(r"/(.*)", StaticFileWithHeaderHandler,
|
||||||
{"path": self.web_root}),
|
{"path": self.web_root}),
|
||||||
], debug=True, autoreload=False)
|
], debug=True, autoreload=False)
|
||||||
|
|
210
www/index.html
210
www/index.html
|
@ -12,6 +12,7 @@
|
||||||
right:0;
|
right:0;
|
||||||
width:100%;
|
width:100%;
|
||||||
height:100%;
|
height:100%;
|
||||||
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
path {
|
path {
|
||||||
|
@ -19,7 +20,6 @@
|
||||||
stroke: red;
|
stroke: red;
|
||||||
stroke-width: 2px;
|
stroke-width: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.submitted path{
|
body.submitted path{
|
||||||
stroke:darkgray;
|
stroke:darkgray;
|
||||||
}
|
}
|
||||||
|
@ -28,39 +28,122 @@
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#wrapper {
|
/*#wrapper {
|
||||||
height: 600px;
|
height: calc({DRAW_HEIGHT}/{HEIGHT} * 100%);
|
||||||
width: 600px;
|
width: calc({DRAW_WIDTH}/{WIDTH} * 100%);
|
||||||
position: relative;
|
position: absolute;
|
||||||
background:#ccc;
|
left: calc(({WIDTH} - {DRAW_WIDTH})/2/{WIDTH} * 100%);
|
||||||
|
top: calc(({HEIGHT} - {DRAW_HEIGHT})/2/{HEIGHT} * 100%);
|
||||||
|
background:none;
|
||||||
cursor: url(cursor.png) 6 6, auto;
|
cursor: url(cursor.png) 6 6, auto;
|
||||||
|
}*/
|
||||||
|
#wrapper {
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
right:0;
|
||||||
|
bottom:0;
|
||||||
|
left:0;
|
||||||
|
background:none;
|
||||||
|
cursor: url(cursor.png) 6 6, auto;
|
||||||
|
}
|
||||||
|
.gray{
|
||||||
|
position:absolute;
|
||||||
|
background:rgba(255,255,255,0.7);
|
||||||
|
}
|
||||||
|
#gray_top{
|
||||||
|
left:0;
|
||||||
|
right:0;
|
||||||
|
top:0;
|
||||||
|
height:calc({TOP_PADDING}/{HEIGHT} * 100%);
|
||||||
|
}
|
||||||
|
#gray_bottom{
|
||||||
|
left:0;
|
||||||
|
right:0;
|
||||||
|
bottom:0;
|
||||||
|
height:calc(({HEIGHT} - {DRAW_HEIGHT} - {TOP_PADDING})/{HEIGHT} * 100%);
|
||||||
|
}
|
||||||
|
#gray_left{
|
||||||
|
left:0;
|
||||||
|
top:calc({TOP_PADDING}/{HEIGHT} * 100%);
|
||||||
|
height: calc({DRAW_HEIGHT}/{HEIGHT} * 100%);
|
||||||
|
width: calc({LEFT_PADDING}/{WIDTH} * 100%);
|
||||||
|
}
|
||||||
|
#gray_right{
|
||||||
|
right:0;
|
||||||
|
top:calc({TOP_PADDING}/{HEIGHT} * 100%);
|
||||||
|
height: calc({DRAW_HEIGHT}/{HEIGHT} * 100%);
|
||||||
|
width: calc(({WIDTH} - {DRAW_WIDTH} - {LEFT_PADDING})/{WIDTH} * 100%);
|
||||||
}
|
}
|
||||||
html, body{
|
html, body{
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
margin:0;
|
||||||
|
background:gray;
|
||||||
|
}
|
||||||
|
#interface{
|
||||||
|
background:white;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-top: calc({HEIGHT}/{WIDTH} * 100%);
|
||||||
|
background: white;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
}
|
||||||
|
#innerface{
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
@media (min-aspect-ratio: {WIDTH}/{HEIGHT}) {
|
||||||
|
#interface {
|
||||||
|
height: 100vh;
|
||||||
|
width: calc({WIDTH}/{HEIGHT} * 100vh);
|
||||||
|
padding-top:0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#info{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
width: 600px;
|
||||||
|
left: calc(50% - 250px);
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
.buttons{
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id='interface' style="background-image:url('{IMAGE_URL}')">
|
||||||
|
<div id='innerface'>
|
||||||
|
<div id='wrapper'>
|
||||||
|
<!-- <img src="{IMAGE_URL}" id='sample'>-->
|
||||||
|
<svg id="canvas" viewBox="0 0 {WIDTH}0 {HEIGHT}0" width="{WIDTH}mm" height="{HEIGHT}mm" preserveAspectRatio="none">
|
||||||
|
<path d="" id="stroke" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div id='info'>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Drag the mouse to trace the line drawing</li>
|
<li>Drag the mouse to trace the <strong>clearest lines</strong> drawing above</li>
|
||||||
<li>Follow the lines as precise as possible</li>
|
<li>Follow the lines as precise as possible</li>
|
||||||
<li>Press submit when you're done.</li>
|
<li>Press submit when you're done.</li>
|
||||||
<li>You'll receive a submission token, to fill in at Mechanical Turk</li>
|
<li>You'll receive a submission token, to fill in at Mechanical Turk</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div id='interface'>
|
|
||||||
<div id='wrapper'>
|
|
||||||
<img src="{IMAGE_URL}" id='sample'>
|
|
||||||
<svg id="canvas">
|
|
||||||
<path d="" id="stroke" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class='buttons'>
|
<div class='buttons'>
|
||||||
<button id='submit'>Submit</button>
|
<button id='submit'>Submit</button>
|
||||||
<button id='reset'>Reset</button>
|
<!-- <button id='reset'>Reset</button>-->
|
||||||
</div>
|
</div>
|
||||||
<div id='message'></div>
|
<div id='message'></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class='gray' id='gray_top'></div>
|
||||||
|
<div class='gray' id='gray_bottom'></div>
|
||||||
|
<div class='gray' id='gray_left'></div>
|
||||||
|
<div class='gray' id='gray_right'></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
let url = window.location.origin.replace('http', 'ws') +'/ws?' + window.location.search.substring(1);
|
let url = window.location.origin.replace('http', 'ws') +'/ws?' + window.location.search.substring(1);
|
||||||
let svgEl = document.getElementById("canvas");
|
let svgEl = document.getElementById("canvas");
|
||||||
|
@ -68,50 +151,77 @@
|
||||||
let submitEl = document.getElementById('submit');
|
let submitEl = document.getElementById('submit');
|
||||||
let resetEl = document.getElementById('reset');
|
let resetEl = document.getElementById('reset');
|
||||||
let messageEl = document.getElementById('message');
|
let messageEl = document.getElementById('message');
|
||||||
|
let innerFaceEl = document.getElementById('innerface'); // wrapper within the interface
|
||||||
|
|
||||||
|
let svgWidth = {WIDTH};
|
||||||
|
let svgHeight = {HEIGHT};
|
||||||
|
let drawWidth = {DRAW_WIDTH};
|
||||||
|
let drawHeight = {DRAW_HEIGHT};
|
||||||
|
|
||||||
|
let xPadding = {LEFT_PADDING} / svgWidth;
|
||||||
|
let yPadding = {TOP_PADDING} / svgHeight;
|
||||||
|
let drawWidthFactor = drawWidth / svgWidth;
|
||||||
|
let drawHeightFactor = drawHeight / svgHeight;
|
||||||
|
|
||||||
let strokes = [];
|
let strokes = [];
|
||||||
let isDrawing = false;
|
let isDrawing = false;
|
||||||
|
let hasMouseDown = false;
|
||||||
let currentPoint = null;
|
let currentPoint = null;
|
||||||
|
|
||||||
|
let getCoordinates = function(e) {
|
||||||
|
// convert event coordinates into relative positions on x & y axis
|
||||||
|
let box = innerFaceEl.getBoundingClientRect();
|
||||||
|
let x = (e.x - box['left']) / box['width'];
|
||||||
|
let y = (e.y - box['top']) / box['height'];
|
||||||
|
return {'x': x, 'y': y};
|
||||||
|
}
|
||||||
|
|
||||||
|
let isInsideBounds = function(pos) {
|
||||||
|
return !(pos['x'] < 0 || pos['y'] < 0 || pos['x'] > 1 || pos['y'] > 1);
|
||||||
|
}
|
||||||
|
let isInsideDrawingBounds = function(pos) {
|
||||||
|
if(pos['x'] > xPadding && pos['x'] < (xPadding+drawWidthFactor) && pos['y'] > yPadding && pos['y'] < yPadding+drawHeightFactor) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let draw = function(e) {
|
let draw = function(e) {
|
||||||
let pos = svgEl.getBoundingClientRect()
|
let pos = getCoordinates(e);
|
||||||
let x = e.x - pos['left'];
|
|
||||||
let y = e.y - pos['top'];
|
if(!isInsideBounds(pos)) {
|
||||||
|
// outside of bounds
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isDrawing && !isInsideDrawingBounds(pos)){
|
||||||
|
stopDrawing(pos);
|
||||||
|
}
|
||||||
|
if(!isDrawing && hasMouseDown && isInsideDrawingBounds(pos)){
|
||||||
|
isDrawing = true;
|
||||||
|
}
|
||||||
|
|
||||||
if(isDrawing) {
|
if(isDrawing) {
|
||||||
strokes.push([x, y, 0]);
|
strokes.push([pos['x'], pos['y'], 0]);
|
||||||
let d = strokes2D(strokes);
|
let d = strokes2D(strokes);
|
||||||
strokeEl.setAttribute('d', d);
|
strokeEl.setAttribute('d', d);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPoint = {
|
console.log([pos['x'], pos['y']], isDrawing);
|
||||||
|
socket.send(JSON.stringify({
|
||||||
'action': 'move',
|
'action': 'move',
|
||||||
'direction': [e.x/window.innerWidth, e.y/window.innerHeight],
|
'direction': [pos['x'], pos['y']],
|
||||||
'mouse': isDrawing,
|
'mouse': isDrawing,
|
||||||
};
|
}));
|
||||||
console.log([x,y], isDrawing);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let interval = window.setInterval(function(){
|
let stopDrawing = function(pos) {
|
||||||
if(currentPoint === null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.send(JSON.stringify(currentPoint));
|
|
||||||
}, 200);
|
|
||||||
|
|
||||||
let penup = function(e) {
|
|
||||||
if(!isDrawing) {
|
if(!isDrawing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isDrawing = false;
|
isDrawing = false;
|
||||||
|
|
||||||
//document.body.removeEventListener('mousemove', draw);
|
//document.body.removeEventListener('mousemove', draw);
|
||||||
|
|
||||||
let pos = svgEl.getBoundingClientRect()
|
|
||||||
let x = e.x - pos['left'];
|
|
||||||
let y = e.y - pos['top'];
|
|
||||||
|
|
||||||
if(strokes.length > 0){
|
if(strokes.length > 0){
|
||||||
// mark point as last of stroke
|
// mark point as last of stroke
|
||||||
|
@ -119,9 +229,18 @@
|
||||||
}
|
}
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
'action': 'up',
|
'action': 'up',
|
||||||
'direction': [x, y]
|
'direction': [pos['x'], pos['y']]
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let penup = function(e) {
|
||||||
|
if(!hasMouseDown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hasMouseDown = false;
|
||||||
|
|
||||||
|
let pos = getCoordinates(e);
|
||||||
|
stopDrawing(pos);
|
||||||
};
|
};
|
||||||
let strokes2D = function(strokes) {
|
let strokes2D = function(strokes) {
|
||||||
// strokes to a d attribute for a path
|
// strokes to a d attribute for a path
|
||||||
|
@ -130,7 +249,7 @@
|
||||||
let cmd = "";
|
let cmd = "";
|
||||||
for (let stroke of strokes) {
|
for (let stroke of strokes) {
|
||||||
if(!last_stroke) {
|
if(!last_stroke) {
|
||||||
d += `M${stroke[0]},${stroke[1]} `;
|
d += `M${stroke[0]*svgWidth*10},${stroke[1]*svgHeight*10} `;
|
||||||
cmd = 'M';
|
cmd = 'M';
|
||||||
} else {
|
} else {
|
||||||
if (last_stroke[2] == 1) {
|
if (last_stroke[2] == 1) {
|
||||||
|
@ -141,7 +260,7 @@
|
||||||
cmd = 'l';
|
cmd = 'l';
|
||||||
}
|
}
|
||||||
let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]];
|
let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]];
|
||||||
d += `${rel_stroke[0]},${rel_stroke[1]} `;
|
d += `${rel_stroke[0]*svgWidth*10},${rel_stroke[1]*svgHeight*10} `;
|
||||||
}
|
}
|
||||||
last_stroke = stroke;
|
last_stroke = stroke;
|
||||||
|
|
||||||
|
@ -150,18 +269,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let startDrawing = function(e){
|
let startDrawing = function(e){
|
||||||
isDrawing = true;
|
hasMouseDown = true;
|
||||||
// start drawing
|
|
||||||
//document.body.addEventListener('mousemove', draw);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
let reset = function() {
|
|
||||||
|
/*let reset = function() {
|
||||||
strokes = [];
|
strokes = [];
|
||||||
strokeEl.setAttribute('d', "");
|
strokeEl.setAttribute('d', "");
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
'action': 'reset',
|
'action': 'reset',
|
||||||
}));
|
}));
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
let socket = new WebSocket(url);
|
let socket = new WebSocket(url);
|
||||||
|
@ -179,8 +296,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//svgEl.addEventListener('mousedown', startDrawing);
|
//resetEl.addEventListener('click', reset);
|
||||||
resetEl.addEventListener('click', reset);
|
|
||||||
submitEl.addEventListener('click', function(e){
|
submitEl.addEventListener('click', function(e){
|
||||||
if(!strokes.length){
|
if(!strokes.length){
|
||||||
alert('please draw before submitting');
|
alert('please draw before submitting');
|
||||||
|
|
Loading…
Reference in a new issue