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"
|
||||
mturk_sandbox: true
|
||||
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_region_name: "eu-west-3"
|
||||
task_xml: "mt_task.xml"
|
||||
hit_db: store.db
|
||||
hour_rate_aim: 15
|
||||
hit_lifetime: 54000 ;15*60*60
|
||||
hit_assignment_duration: 300 ; 5*60
|
||||
hit_lifetime: 54000 #15*60*60
|
||||
hit_assignment_duration: 300 # 5*60
|
||||
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())
|
||||
|
||||
def getImageUrl(self):
|
||||
return f"scans/{self.id}.png"
|
||||
return f"scans/{self.id}.jpg"
|
||||
|
||||
def getStatus(self):
|
||||
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
|
||||
"""
|
||||
self.eventQueue.put(Signal('scan.start'))
|
||||
|
||||
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()
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
@ -313,6 +316,7 @@ class CentralManagement():
|
|||
|
||||
f = io.BytesIO(o)
|
||||
img = Image.open(f)
|
||||
img = img.transpose(Image.ROTATE_90)
|
||||
img.save(filename)
|
||||
|
||||
self.eventQueue.put(Signal('hit.scanned', {'hit_id':self.currentHit.id}))
|
||||
|
|
|
@ -6,17 +6,6 @@ from threading import Event
|
|||
from sorteerhoed.Signal import Signal
|
||||
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:
|
||||
def __init__(self, config, eventQ: Queue, runningEvent: Event):
|
||||
#TODO: scanningEvent -> CentralManagement.isScanning -> prevent plotter move during scan, failsafe
|
||||
|
@ -25,6 +14,13 @@ class Plotter:
|
|||
self.q = Queue()
|
||||
self.isRunning = runningEvent
|
||||
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):
|
||||
self.logger.info("Queue to park plotter")
|
||||
|
@ -37,69 +33,118 @@ class Plotter:
|
|||
self.q.put([0,0,0])
|
||||
|
||||
def start(self):
|
||||
self.axiDrawCueListener()
|
||||
try:
|
||||
if not self.config['dummy_plotter']:
|
||||
self.ad = axidraw.AxiDraw()
|
||||
|
||||
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:
|
||||
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:
|
||||
time.sleep(.05)
|
||||
self.logger.debug(f'Dummy plotter move: {move}')
|
||||
self.logger.info("Stopping dummy plotter")
|
||||
else:
|
||||
self.ad = axidraw.AxiDraw()
|
||||
self.ad.interactive()
|
||||
# self.ad.plot_path()
|
||||
|
||||
self.ad.interactive()
|
||||
# self.ad.plot_path()
|
||||
connected = self.ad.connect()
|
||||
if not connected:
|
||||
raise Exception("Cannot connect to Axidraw")
|
||||
|
||||
connected = self.ad.connect()
|
||||
if not connected:
|
||||
raise Exception("Cannot connect to Axidraw")
|
||||
try:
|
||||
self.ad.options.units = 1 # set to use centimeters instead of inches
|
||||
self.ad.options.accel = 100;
|
||||
self.ad.options.speed_penup = 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)
|
||||
|
||||
plotterWidth = 22
|
||||
plotterHeight = 18 # 16?
|
||||
# plotterWidth = 25
|
||||
# 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:
|
||||
self.ad.moveto(move[0]* plotterWidth, move[1]*plotterHeight)
|
||||
self.logger.debug(f'handler! {move}')
|
||||
except Exception as e:
|
||||
self.logger.exception(e)
|
||||
finally:
|
||||
self.logger.warning("Close Axidraw connection")
|
||||
else:
|
||||
self.ad = None
|
||||
self.axiDrawCueListener()
|
||||
except Exception as e:
|
||||
self.logger.exception(e)
|
||||
finally:
|
||||
self.logger.warning("Close Axidraw connection")
|
||||
if self.ad:
|
||||
self.ad.moveto(0,0)
|
||||
self.ad.disconnect()
|
||||
|
||||
# send shutdown signal (if not already set)
|
||||
self.isRunning.clear()
|
||||
# send shutdown signal (if not already set)
|
||||
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;
|
||||
|
||||
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.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):
|
||||
try:
|
||||
|
@ -258,7 +264,14 @@ class DrawPageHandler(tornado.web.RequestHandler):
|
|||
logger.info(f"Image url: {image}")
|
||||
|
||||
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)
|
||||
|
||||
class StatusPage():
|
||||
|
@ -342,7 +355,16 @@ class Server:
|
|||
}),
|
||||
(r"/status/ws", StatusWebSocketHandler),
|
||||
(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,
|
||||
{"path": self.web_root}),
|
||||
], debug=True, autoreload=False)
|
||||
|
|
232
www/index.html
232
www/index.html
|
@ -12,6 +12,7 @@
|
|||
right:0;
|
||||
width:100%;
|
||||
height:100%;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
path {
|
||||
|
@ -19,7 +20,6 @@
|
|||
stroke: red;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
body.submitted path{
|
||||
stroke:darkgray;
|
||||
}
|
||||
|
@ -28,38 +28,121 @@
|
|||
display:none;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
height: 600px;
|
||||
width: 600px;
|
||||
position: relative;
|
||||
background:#ccc;
|
||||
/*#wrapper {
|
||||
height: calc({DRAW_HEIGHT}/{HEIGHT} * 100%);
|
||||
width: calc({DRAW_WIDTH}/{WIDTH} * 100%);
|
||||
position: absolute;
|
||||
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;
|
||||
}*/
|
||||
#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{
|
||||
height: 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>
|
||||
</head>
|
||||
<body>
|
||||
<ul>
|
||||
<li>Drag the mouse to trace the line drawing</li>
|
||||
<li>Follow the lines as precise as possible</li>
|
||||
<li>Press submit when you're done.</li>
|
||||
<li>You'll receive a submission token, to fill in at Mechanical Turk</li>
|
||||
</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'>
|
||||
<button id='submit'>Submit</button>
|
||||
<button id='reset'>Reset</button>
|
||||
</div>
|
||||
<div id='message'></div>
|
||||
<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>
|
||||
<li>Drag the mouse to trace the <strong>clearest lines</strong> drawing above</li>
|
||||
<li>Follow the lines as precise as possible</li>
|
||||
<li>Press submit when you're done.</li>
|
||||
<li>You'll receive a submission token, to fill in at Mechanical Turk</li>
|
||||
</ul>
|
||||
<div class='buttons'>
|
||||
<button id='submit'>Submit</button>
|
||||
<!-- <button id='reset'>Reset</button>-->
|
||||
</div>
|
||||
<div id='message'></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">
|
||||
let url = window.location.origin.replace('http', 'ws') +'/ws?' + window.location.search.substring(1);
|
||||
|
@ -68,50 +151,77 @@
|
|||
let submitEl = document.getElementById('submit');
|
||||
let resetEl = document.getElementById('reset');
|
||||
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 isDrawing = false;
|
||||
let hasMouseDown = false;
|
||||
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 pos = svgEl.getBoundingClientRect()
|
||||
let x = e.x - pos['left'];
|
||||
let y = e.y - pos['top'];
|
||||
let pos = getCoordinates(e);
|
||||
|
||||
if(!isInsideBounds(pos)) {
|
||||
// outside of bounds
|
||||
return;
|
||||
}
|
||||
|
||||
if(isDrawing && !isInsideDrawingBounds(pos)){
|
||||
stopDrawing(pos);
|
||||
}
|
||||
if(!isDrawing && hasMouseDown && isInsideDrawingBounds(pos)){
|
||||
isDrawing = true;
|
||||
}
|
||||
|
||||
if(isDrawing) {
|
||||
strokes.push([x, y, 0]);
|
||||
strokes.push([pos['x'], pos['y'], 0]);
|
||||
let d = strokes2D(strokes);
|
||||
strokeEl.setAttribute('d', d);
|
||||
}
|
||||
|
||||
currentPoint = {
|
||||
console.log([pos['x'], pos['y']], isDrawing);
|
||||
socket.send(JSON.stringify({
|
||||
'action': 'move',
|
||||
'direction': [e.x/window.innerWidth, e.y/window.innerHeight],
|
||||
'direction': [pos['x'], pos['y']],
|
||||
'mouse': isDrawing,
|
||||
};
|
||||
console.log([x,y], isDrawing);
|
||||
}));
|
||||
};
|
||||
|
||||
let interval = window.setInterval(function(){
|
||||
if(currentPoint === null) {
|
||||
return
|
||||
}
|
||||
|
||||
socket.send(JSON.stringify(currentPoint));
|
||||
}, 200);
|
||||
|
||||
let penup = function(e) {
|
||||
if(!isDrawing) {
|
||||
return;
|
||||
let stopDrawing = function(pos) {
|
||||
if(!isDrawing) {
|
||||
return;
|
||||
}
|
||||
isDrawing = false;
|
||||
|
||||
//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){
|
||||
// mark point as last of stroke
|
||||
|
@ -119,9 +229,18 @@
|
|||
}
|
||||
socket.send(JSON.stringify({
|
||||
'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) {
|
||||
// strokes to a d attribute for a path
|
||||
|
@ -130,7 +249,7 @@
|
|||
let cmd = "";
|
||||
for (let stroke of strokes) {
|
||||
if(!last_stroke) {
|
||||
d += `M${stroke[0]},${stroke[1]} `;
|
||||
d += `M${stroke[0]*svgWidth*10},${stroke[1]*svgHeight*10} `;
|
||||
cmd = 'M';
|
||||
} else {
|
||||
if (last_stroke[2] == 1) {
|
||||
|
@ -141,7 +260,7 @@
|
|||
cmd = 'l';
|
||||
}
|
||||
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;
|
||||
|
||||
|
@ -150,18 +269,16 @@
|
|||
}
|
||||
|
||||
let startDrawing = function(e){
|
||||
isDrawing = true;
|
||||
// start drawing
|
||||
//document.body.addEventListener('mousemove', draw);
|
||||
|
||||
hasMouseDown = true;
|
||||
};
|
||||
let reset = function() {
|
||||
|
||||
/*let reset = function() {
|
||||
strokes = [];
|
||||
strokeEl.setAttribute('d', "");
|
||||
socket.send(JSON.stringify({
|
||||
'action': 'reset',
|
||||
}));
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
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){
|
||||
if(!strokes.length){
|
||||
alert('please draw before submitting');
|
||||
|
|
Loading…
Reference in a new issue