Skip to content

Commit

Permalink
Fix selection of arrowheads (fix #225)
Browse files Browse the repository at this point in the history
  • Loading branch information
edemaine committed Nov 3, 2023
1 parent f21ab3f commit 427e1cc
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 26 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ instead of version numbers.

## 2023-11-03

* Fix arrowheads getting clipped when downloading SVG/PDF
* Fix arrowheads getting clipped when downloading SVG/PDF,
and rectangular selection of arrowheads.
[[#225](https://github.com/edemaine/cocreate/issues/225)]

## 2023-11-01
Expand Down
14 changes: 14 additions & 0 deletions client/BBox.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ export class BBox
@fromRect: (rect) ->
new BBox rect.x, rect.y, rect.x + rect.width, rect.y + rect.height

#corners: ->
# [
# x: @minX
# y: @minY
# ,
# x: @maxX
# y: @minY
# ,
# x: @maxX
# y: @maxY
# ,
# x: @minX
# y: @maxY
# ]
center: ->
x: (@maxX + @minX) / 2
y: (@maxY + @minY) / 2
Expand Down
52 changes: 49 additions & 3 deletions client/Collision.coffee
Original file line number Diff line number Diff line change
@@ -1,7 +1,40 @@
import {BBox} from './BBox'
import {svgArrowCoords} from './lib/dom'

## Arrowheads of pen tool are rendered on unit line segments in the average
## direction of the first/last 20 points of the stroke.
export penArrowAverage = 20
export averageDirection = (pts) ->
{x: ox, y: oy} = pts[0]
dx = dy = 0
for i in [1...pts.length]
{x, y} = pts[i]
dx += x - ox
dy += y - oy
return {dx, dy} if pts.length <= 2
#dx /= pts.length - 1
#dy /= pts.length - 1
length = Math.sqrt dx * dx + dy * dy
if length > 0.01
dx /= length
dy /= length
else
dx = dy = 0
{dx, dy}

intersectsTriangle = (query, a, b, c) ->
intersectsSpecific.poly query,
pts: [a, b, c]
width: 0
, null, 2
## Check whether any corner of the triangle is in a query rectangle
#for p in [a, b, c]
# return true if query.containsPoint p
## Check whether any edge of the triangle intersects the query rectangle
#...

intersectsSpecific =
pen: (query, obj) ->
pen: (query, obj, bbox, average = penArrowAverage) ->
## Untranslate query box if object is translated.
if obj.tx or obj.ty
query = query.translate -(obj.tx ? 0), -(obj.ty ? 0)
Expand Down Expand Up @@ -65,11 +98,24 @@ intersectsSpecific =
return true if intersectsSpecific.rect query, {width: obj.width}, \
(new BBox pt.x, pt.y, pt.x, pt.y).fattened (obj.width / 2)

# Arrow heads.
# Note: v0 is slightly off because of the rounded tip of the arrowhead.
if obj.arrowStart
[v1, v2] = svgArrowCoords v0 = obj.pts[0],
averageDirection(obj.pts[...average]),
obj.width
return true if intersectsTriangle query, v0, v1, v2
if obj.arrowEnd
[v1, v2] = svgArrowCoords v0 = obj.pts[obj.pts.length-1],
averageDirection(obj.pts[-average..].reverse()),
obj.width
return true if intersectsTriangle query, v0, v1, v2

false

poly: (query, obj) ->
poly: (query, obj, bbox) ->
# Currently no fill support
intersectsSpecific.pen query, obj
intersectsSpecific.pen query, obj, bbox, 2

rect: (query, obj, bbox) ->
# Minkowski sum to make outer test simpler
Expand Down
21 changes: 1 addition & 20 deletions client/RenderObjects.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,9 @@ import {pointers} from './tools/modes'
import {tools} from './tools/defineTool'
import {anchorObjectTypes, anchorsOf, anchorRadius, anchorStroke} from './Anchor'
import {BBox, minSvgSize} from './BBox'
import {penArrowAverage, averageDirection} from './Collision'
#import {DBVT} from './DBVT'

## Arrowheads of pen tool are rendered on unit line segments in the average
## direction of the first/last 20 points of the stroke.
penArrowAverage = 20
averageDirection = (pts) ->
{x: ox, y: oy} = pts[0]
dx = dy = 0
for i in [1...pts.length]
{x, y} = pts[i]
dx += x - ox
dy += y - oy
#dx /= pts.length - 1
#dy /= pts.length - 1
length = Math.sqrt dx * dx + dy * dy
if length > 0.01
dx /= length
dy /= length
else
dx = dy = 0
{dx, dy}

export class RenderObjects
constructor: (@board) ->
@root = @board.root
Expand Down
8 changes: 6 additions & 2 deletions client/lib/dom.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,14 @@ export svgArrowCoords = (pEnd, pAdj, width) ->
Computes the coordinates of the two corners of an arrow head ending at pEnd,
coming from pAdj, given a specified line width (which determines scale)
and assuming #arrow heads.
If pAdj has {dx, dy}, it is instead treated like a vector starting at pEnd.
###
{x, y} = pEnd
dx = pAdj.x - x
dy = pAdj.y - y
if pAdj.dx?
{dx, dy} = pAdj
else
dx = pAdj.x - x
dy = pAdj.y - y
scale = width / Math.sqrt dx*dx + dy*dy
dx *= scale
dy *= scale
Expand Down

0 comments on commit 427e1cc

Please sign in to comment.