Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inverted end caps when using offsetStroke with (some) straight lines #10

Open
trun opened this issue Sep 27, 2021 · 3 comments
Open

Inverted end caps when using offsetStroke with (some) straight lines #10

trun opened this issue Sep 27, 2021 · 3 comments

Comments

@trun
Copy link

trun commented Sep 27, 2021

When applying offsetStroke to Paths consisting of only straight lines it will occasionally produce end caps on the outlined shape that are "inverted". In the example below, the black line is the input Path and the red fill is the outlined shape produced from the parameters PaperOffset.offsetStroke(st1, 5, { cap: 'round' })...

Image 2021-09-27 at 5 10 59 PM

The root cause of the issue appears to be the reliance on the clockwise attribute of the input Path, which in turn relies on the area to determine the direction of the Path. The area of Path containing only a straight line should, by definition, always be zero, but floating point math makes this fact unreliable. In turn, this makes this clockwise attribute unreliable and calling reverse() on this Path will likely produce a new Path with the same direction as the original. The end result is a Path that is counter-clockwise and un-reverse()able, which seems to mess with some of the logic in paperjs-offset that assumes calling reverse() on a counter-clockwise Path will always produce a clockwise Path. The Path in the example above has an area of -1.7763568394002505e-14.

My (incomplete) workaround is to outline straight lines manually with a much more simplistic algorithm that does not rely on on the path direction. Also currently assumes round caps (though could probably be generalized to support other caps) and does not need to support joins because the lines are assumed to be straight...

function simpleOutlineStroke(path, offset) {
  const normal = path.getNormalAt(0)
  const p0 = path.firstSegment.getPoint().add(normal.multiply(offset))
  const p1 = path.lastSegment.getPoint().add(normal.multiply(offset))
  const p2 = path.lastSegment.getPoint().add(normal.multiply(-offset))
  const p3 = path.firstSegment.getPoint().add(normal.multiply(-offset))

  // draw outlined path
  const offsetPath = new paper.Path({ insert: false })
  offsetPath.moveTo(p0)
  offsetPath.lineTo(p1)
  offsetPath.arcTo(p2, true)
  offsetPath.lineTo(p3)
  offsetPath.arcTo(p0, true)
  offsetPath.closePath()

  // copy path styles
  offsetPath.strokeWidth = 0;
  offsetPath.fillColor = path.strokeColor;
  offsetPath.shadowBlur = path.shadowBlur;
  offsetPath.shadowColor = path.shadowColor;
  offsetPath.shadowOffset = path.shadowOffset;

  return offsetPath
}

Example used to produce the screenshot...

let canvas = document.querySelector('canvas')
paper.setup(canvas)
paper.view.center = [-0.7260725539107398, -1.2302079087572793]
let angle = Math.atan(0.9266588281199828)
let x = Math.cos(angle) * 100
let y = Math.sin(angle) * 100
let xOffset = 0.7260725539107398 - x / 2
let yOffset = 1.2302079087572793 - y / 2
let st1 = new paper.Path.Line({ from: [xOffset, yOffset], to: [x + xOffset, y + yOffset], strokeColor: 'rgba(255, 0, 0, 0.5)', strokeWidth: 1 })
let st2 = new paper.Path.Line({ from: [xOffset, yOffset], to: [x + xOffset, y + yOffset], strokeColor: '#000', strokeWidth: 1 })
PaperOffset.offsetStroke(st1, 5, { cap: 'round' })
st1.bringToFront()
@ErikSom
Copy link

ErikSom commented Dec 10, 2023

I was running into the same issue, @glenzli , have you run into similar behaviour?

@ErikSom
Copy link

ErikSom commented Dec 10, 2023

Wouldn't it be much simpler @trun to fix this by having a very small epsilon which is considered 0 as well?
0 = x < epsilon && x > -epsilon

@ErikSom
Copy link

ErikSom commented Dec 10, 2023

Indeed it's fixed when patching paperjs with the following code:

	isClockwise: function() {
		return this.getArea() >= -0.000000000001;
	},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants