Skip to content

Commit

Permalink
Add line segment drawing (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
edemaine committed Jun 10, 2020
1 parent 0bcf5f0 commit 9c77ef1
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 16 deletions.
6 changes: 6 additions & 0 deletions client/lib/.iconsrc/segment.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion client/lib/dom.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ export SVGNS = 'http://www.w3.org/2000/svg'

svgTags =
svg: true
g: true
line: true
polyline: true
circle: true
g: true

export create = (tag, attrs, props, events, children) ->
if tag of svgTags
Expand Down
9 changes: 6 additions & 3 deletions client/lib/icons.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@ icons =
undo:
'<path d="M212.333 224.333H12c-6.627 0-12-5.373-12-12V12C0 5.373 5.373 0 12 0h48c6.627 0 12 5.373 12 12v78.112C117.773 39.279 184.26 7.47 258.175 8.007c136.906.994 246.448 111.623 246.157 248.532C504.041 393.258 393.12 504 256.333 504c-64.089 0-122.496-24.313-166.51-64.215-5.099-4.622-5.334-12.554-.467-17.42l33.967-33.967c4.474-4.474 11.662-4.717 16.401-.525C170.76 415.336 211.58 432 256.333 432c97.268 0 176-78.716 176-176 0-97.267-78.716-176-176-176-58.496 0-110.28 28.476-142.274 72.333h98.274c6.627 0 12 5.373 12 12v48c0 6.627-5.373 12-12 12z"/>'

## Significantly modified icons, with source SVG in subdirectory `.iconsrc`:
## Significantly modified icons, with source SVG (before converting to
## outlines) in subdirectory `.iconsrc`:

'download-svg': # heavily edited file-download-solid plus "SVG" in Impact font
'<path d="M424,0h-97.9H320h-32H88C74.7,0,64,10.7,64,24v159v305c0,13.3,10.7,24,24,24h336c13.3,0,24-10.7,24-24V183v-23v-32v-6.1V24 C448,10.7,437.3,0,424,0z M364.5,377.4L268,473.1c-6.7,6.6-17.4,6.6-24,0l-96.4-95.7c-10.2-10.1-3-27.4,11.3-27.4H224v-80 c0-8.8,7.2-16,16-16h32c8.8,0,16,7.2,16,16v80h65.2C367.5,350,374.6,367.3,364.5,377.4z M192,101h-38.3V88.2c0-6-0.5-9.8-1.5-11.4 c-1-1.6-2.6-2.5-4.9-2.5c-2.5,0-4.4,1.1-5.6,3.3c-1.3,2.2-1.9,5.6-1.9,10.1c0,5.8,0.7,10.1,2.1,13.1c1.4,2.9,5.2,6.5,11.6,10.6 c18.3,11.9,29.9,21.7,34.7,29.3c4.8,7.6,7.1,19.9,7.1,36.9c0,12.3-1.3,21.4-4,27.3c-2.6,5.8-7.7,10.7-15.3,14.7 c-7.6,4-16.4,5.9-26.4,5.9c-11,0-20.4-2.3-28.2-6.8c-7.8-4.6-12.9-10.4-15.3-17.4c-2.4-7.1-3.6-17.1-3.6-30v-11.3h38.3v21.1 c0,6.5,0.5,10.7,1.6,12.5c1.1,1.9,3,2.8,5.7,2.8c2.7,0,4.8-1.2,6.1-3.5c1.3-2.4,2-5.8,2-10.5c0-10.2-1.3-16.9-3.8-20 c-2.6-3.1-9-8.4-19.3-15.7c-10.2-7.4-17-12.8-20.4-16.1c-3.3-3.4-6.1-8-8.3-13.9c-2.2-5.9-3.3-13.5-3.3-22.7c0-13.3,1.5-23,4.6-29.1 c3.1-6.1,8.1-10.9,15-14.4c6.9-3.5,15.3-5.2,25.1-5.2c10.7,0,19.8,1.9,27.4,5.7c7.5,3.8,12.5,8.5,15,14.3c2.4,5.7,3.7,15.5,3.7,29.2 V101z M306.3,48.6l-21,173.1h-62.6L198.9,48.6h43.5c5.1,47.7,8.7,88,10.8,121c2.1-33.4,4.4-63,6.8-88.9l2.9-32.2H306.3z M407.8,112.3h-41.2V96.6c0-9.9-0.4-16.1-1.2-18.6c-0.8-2.5-2.6-3.7-5.6-3.7c-2.5,0-4.3,1.1-5.2,3.2c-0.9,2.1-1.4,7.6-1.4,16.5V177 c0,7.8,0.5,12.9,1.4,15.3c0.9,2.5,2.7,3.7,5.5,3.7c3,0,5-1.4,6.1-4.2c1.1-2.8,1.6-8.2,1.6-16.3v-20.5h-8.3v-26.3h48.3v92.9h-25.9 l-3.8-12.4c-2.8,5.3-6.4,9.4-10.6,12c-4.3,2.7-9.3,4-15.1,4c-6.9,0-13.4-1.8-19.4-5.5c-6-3.7-10.6-8.2-13.8-13.6 c-3.1-5.4-5.1-11.1-5.9-17.1c-0.8-6-1.2-14.9-1.2-26.8v-51.4c0-16.5,0.8-28.5,2.4-36c1.6-7.5,6.3-14.3,14-20.6 c7.7-6.2,17.7-9.4,30-9.4c12.1,0,22.1,2.7,30,8.1c8,5.4,13.1,11.9,15.6,19.3c2.4,7.4,3.6,18.3,3.6,32.5V112.3z"/>'
grid: # edited border-all to extend (more) beyond square
#'<rect x="32" width="64" height="512"/><rect x="224" width="64" height="512"/><rect x="416" width="64" height="512"/><rect y="32" width="512" height="64"/><rect y="224" width="512" height="64"/><rect y="416" width="512" height="64"/>'
'<rect x="52" width="64" height="512"/><rect x="224" width="64" height="512"/><rect x="396" width="64" height="512"/><rect y="52" width="512" height="64"/><rect y="224" width="512" height="64"/><rect y="396" width="512" height="64"/>'
'download-svg': # heavily edited file-download-solid plus "SVG" in Impact font
'<path d="M424,0h-97.9H320h-32H88C74.7,0,64,10.7,64,24v159v305c0,13.3,10.7,24,24,24h336c13.3,0,24-10.7,24-24V183v-23v-32v-6.1V24 C448,10.7,437.3,0,424,0z M364.5,377.4L268,473.1c-6.7,6.6-17.4,6.6-24,0l-96.4-95.7c-10.2-10.1-3-27.4,11.3-27.4H224v-80 c0-8.8,7.2-16,16-16h32c8.8,0,16,7.2,16,16v80h65.2C367.5,350,374.6,367.3,364.5,377.4z M192,101h-38.3V88.2c0-6-0.5-9.8-1.5-11.4 c-1-1.6-2.6-2.5-4.9-2.5c-2.5,0-4.4,1.1-5.6,3.3c-1.3,2.2-1.9,5.6-1.9,10.1c0,5.8,0.7,10.1,2.1,13.1c1.4,2.9,5.2,6.5,11.6,10.6 c18.3,11.9,29.9,21.7,34.7,29.3c4.8,7.6,7.1,19.9,7.1,36.9c0,12.3-1.3,21.4-4,27.3c-2.6,5.8-7.7,10.7-15.3,14.7 c-7.6,4-16.4,5.9-26.4,5.9c-11,0-20.4-2.3-28.2-6.8c-7.8-4.6-12.9-10.4-15.3-17.4c-2.4-7.1-3.6-17.1-3.6-30v-11.3h38.3v21.1 c0,6.5,0.5,10.7,1.6,12.5c1.1,1.9,3,2.8,5.7,2.8c2.7,0,4.8-1.2,6.1-3.5c1.3-2.4,2-5.8,2-10.5c0-10.2-1.3-16.9-3.8-20 c-2.6-3.1-9-8.4-19.3-15.7c-10.2-7.4-17-12.8-20.4-16.1c-3.3-3.4-6.1-8-8.3-13.9c-2.2-5.9-3.3-13.5-3.3-22.7c0-13.3,1.5-23,4.6-29.1 c3.1-6.1,8.1-10.9,15-14.4c6.9-3.5,15.3-5.2,25.1-5.2c10.7,0,19.8,1.9,27.4,5.7c7.5,3.8,12.5,8.5,15,14.3c2.4,5.7,3.7,15.5,3.7,29.2 V101z M306.3,48.6l-21,173.1h-62.6L198.9,48.6h43.5c5.1,47.7,8.7,88,10.8,121c2.1-33.4,4.4-63,6.8-88.9l2.9-32.2H306.3z M407.8,112.3h-41.2V96.6c0-9.9-0.4-16.1-1.2-18.6c-0.8-2.5-2.6-3.7-5.6-3.7c-2.5,0-4.3,1.1-5.2,3.2c-0.9,2.1-1.4,7.6-1.4,16.5V177 c0,7.8,0.5,12.9,1.4,15.3c0.9,2.5,2.7,3.7,5.5,3.7c3,0,5-1.4,6.1-4.2c1.1-2.8,1.6-8.2,1.6-16.3v-20.5h-8.3v-26.3h48.3v92.9h-25.9 l-3.8-12.4c-2.8,5.3-6.4,9.4-10.6,12c-4.3,2.7-9.3,4-15.1,4c-6.9,0-13.4-1.8-19.4-5.5c-6-3.7-10.6-8.2-13.8-13.6 c-3.1-5.4-5.1-11.1-5.9-17.1c-0.8-6-1.2-14.9-1.2-26.8v-51.4c0-16.5,0.8-28.5,2.4-36c1.6-7.5,6.3-14.3,14-20.6 c7.7-6.2,17.7-9.4,30-9.4c12.1,0,22.1,2.7,30,8.1c8,5.4,13.1,11.9,15.6,19.3c2.4,7.4,3.6,18.3,3.6,32.5V112.3z"/>'
segment: # based on line thickness from grid / border-all
'<path d="M32,512c-8.2,0-16.4-3.1-22.6-9.4c-12.5-12.5-12.5-32.8,0-45.3l448-448c12.5-12.5,32.8-12.5,45.3,0c12.5,12.5,12.5,32.8,0,45.3l-448,448C48.4,508.9,40.2,512,32,512z"/>'

export viewSize = 512

Expand Down
68 changes: 57 additions & 11 deletions client/main.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ tools =
pen:
icon: 'pencil-alt'
hotspot: [0, 1]
title: 'Freehand drawing'
title: 'Freehand drawing (with pen pressure adjusting width)'
down: (e) ->
return if pointers[e.pointerId]
pointers[e.pointerId] = Meteor.apply 'objectNew', [
room: currentRoom
type: 'pen'
pts: [eventToPoint e]
pts: [eventToPointW e]
color: currentColor
], returnStubValue: true
up: (e) ->
Expand All @@ -83,7 +83,32 @@ tools =
#else
Meteor.call 'objectPush',
id: pointers[e.pointerId]
pts: eventToPoint e
pts: eventToPointW e
segment:
icon: 'segment'
hotspot: [0.0625, 0.9375]
title: 'Draw straight line segments between endpoints (drag)'
down: (e) ->
return if pointers[e.pointerId]
pt = eventToPoint e
pointers[e.pointerId] = Meteor.apply 'objectNew', [
room: currentRoom
type: 'poly'
pts: [pt, pt]
color: currentColor
width: currentWidth
], returnStubValue: true
up: (e) ->
return unless pointers[e.pointerId]
undoableOp
type: 'new'
obj: Objects.findOne pointers[e.pointerId]
delete pointers[e.pointerId]
move: (e) ->
return unless pointers[e.pointerId]
Meteor.call 'objectEdit',
id: pointers[e.pointerId]
pts: 1: eventToPoint e
eraser:
icon: 'eraser'
hotspot: [0.35, 1]
Expand Down Expand Up @@ -289,16 +314,19 @@ pressureWidth = (e) -> (0.5 + e.pressure) * currentWidth

eventToPoint = (e) ->
{x, y} = dom.svgPoint board, e.clientX, e.clientY, boardRoot
x: x
y: y
w:
{x, y}

eventToPointW = (e) ->
pt = eventToPoint e
pt.w =
## iPhone (iOS 13.4, Safari 13.1) sends pressure 0 for touch events.
## Android Chrome (Samsung Note 8) sends pressure 1 for touch events.
## Just ignore pressure on touch and mouse events; could they make sense?
if e.pointerType == 'pen'
w = pressureWidth e
else
w = currentWidth
pt

eventToRawPoint = (e) ->
x: e.clientX
Expand Down Expand Up @@ -329,7 +357,7 @@ pointerEvents = ->
room: currentRoom
tool: currentTool
color: currentColor
cursor: eventToPoint e
cursor: eventToPointW e

class Highlighter
findGroup: (e) ->
Expand Down Expand Up @@ -432,13 +460,31 @@ class Render
renderPen: (obj, start = 0) ->
id = @id obj
unless (g = @dom[id])?
@dom[id] = g = dom.create 'g', null, dataset: id: id
@root.appendChild @dom[id]
@root.appendChild @dom[id] = g =
dom.create 'g', null, dataset: id: id
for i in [start...obj.pts.length]
pt = obj.pts[i]
g.appendChild edge obj, obj.pts[i-1], pt if i > 0
g.appendChild dot obj, pt
g
renderPoly: (obj) ->
id = @id obj
unless (poly = @dom[id])?
@root.appendChild @dom[id] = poly =
dom.create 'polyline', null, dataset: id: id
dom.attr poly,
points: ("#{x},#{y}" for {x, y} in obj.pts).join ' '
stroke: obj.color
'stroke-width': obj.width
'stroke-linecap': 'round'
'stroke-linejoin': 'round'
fill: 'none'
render: (obj, ...args) ->
switch obj.type
when 'pen'
@renderPen obj, ...args
when 'poly'
@renderPoly obj, ...args
delete: (obj) ->
id = @id obj
unless @dom[id]?
Expand All @@ -465,10 +511,10 @@ observeRender = (room) ->
# Currently assuming all objects are of type 'pen'
added: (obj) ->
render.shouldNotExist obj
render.renderPen obj
render.render obj
changed: (obj, old) ->
# Assumes that pen changes only append to `pts` field
render.renderPen obj, old.pts.length
render.render obj, old.pts.length
removed: (obj) ->
render.delete obj

Expand Down
43 changes: 42 additions & 1 deletion lib/objects.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import {checkRoom} from './rooms.coffee'
@Objects = new Mongo.Collection 'objects'
@ObjectsDiff = new Mongo.Collection 'objects.diff'

xyType =
x: Number
y: Number
xywType =
x: Number
y: Number
Expand All @@ -27,6 +30,16 @@ Meteor.methods
type: 'pen'
pts: [xywType]
color: String
when 'poly'
check obj,
_id: Match.Optional String
created: Match.Optional Date
updated: Match.Optional Date
room: String
type: 'poly'
pts: [xyType]
color: String
width: Number
else
throw new Error "Invalid type #{obj?.type} for object"
unless @isSimulation
Expand All @@ -53,13 +66,41 @@ Meteor.methods
diff.type = 'push'
diff.updated = new Date
ObjectsDiff.insert diff
Objects.update diff.id,
Objects.update id,
$push: pts: diff.pts
$set:
unless @isSimulation
updated: diff.updated
else
{}
objectEdit: (diff) ->
check diff,
id: String
color: Match.Optional String
width: Match.Optional Number
pts: Match.Optional Match.Where (pts) ->
return false unless typeof pts == 'object'
for key, value of pts
return false unless /^\d+$/.test key
check value, xyType
true
id = diff.id
set = {}
for key, value of diff
switch key
when 'color', 'width'
set[key] = value
when 'pts'
for subkey, subvalue of value
set["#{key}.#{subkey}"] = subvalue
unless @isSimulation
obj = checkObject id
diff.room = obj.room
diff.type = 'edit'
diff.updated = set.updated = new Date
ObjectsDiff.insert diff
Objects.update id,
$set: set
objectDel: (id) ->
check id, String
unless @isSimulation
Expand Down

0 comments on commit 9c77ef1

Please sign in to comment.