Skip to content

[p5.js 2.0 Bug Report]: Tessellation of beginShape/endShape with multiple contours sometimes produces weird results #8186

@davepagurek

Description

@davepagurek

Most appropriate sub-area of p5.js?

  • Accessibility
  • Color
  • Core/Environment/Rendering
  • Data
  • DOM
  • Events
  • Image
  • IO
  • Math
  • Typography
  • Utilities
  • WebGL
  • Build process
  • Unit testing
  • Internationalization
  • Friendly errors
  • Other (specify if possible)

p5.js version

2.0.5, 1.11.10

Web browser and version

Firefox, Chrome

Operating system

MacOS

Steps to reproduce this

Steps:

  1. Use WebGL mode
  2. Draw a shape with two intersecting contours
  3. In one of the contours, make a vertex have one coordinate aaalmost equal to that coordinate of the previous, e.g. have x=1 and then x=1.00000001

The output of libtess is glitchy in this case:

Without a similar coordinateWith a similar coordinate
Image Image

I debugged this and our vertices all look right going into libtess, and our GLU_TESS_COMBINE callback is working properly, so this is actually a bug within libtess. But libtess is a JS port of SGI C code from the 90s, I doubt they're actually going to fix this bug, and it's pretty subtle, so this feels like something we'll have to work around ourselves if we want to work around it at all.

Workaround for users

Basically if they're drawing a bunch of points, they have to add a check like this before drawing the points:

for (let i = 1; i < points.length; i++) {
  const prev = points[i-1]
  const next = points[i]
  if (Math.abs(prev.x - next.x) < 1e-6) next.x = prev.x
  if (Math.abs(prev.y - next.y) < 1e-6) next.y = prev.y
}

A fix for this probably would involve us doing that loop ourselves before tessellating a shape.

Should we fix this at all?

It's unlikely that a user would manually create problematic coordinates like this, but it's a lot more likely to happen when drawing contours automatically sampled from a font with font.textToContours() (which is how I ran into this just now!) So if we do a fix, we can maybe consider it a fix for textToContours usage.

Snippet:

function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  const contours = [
    [
      [-3.8642425537109375,-6.120738636363637,0],
      [3.2025188099254267,-6.120738636363637,0],
      [3.2025188099254267,-4.345170454545455,0],
      [-3.8642425537109375,-4.345170454545455,0],
      [-3.8642425537109375,-6.120738636363637,0],
    ],

    [
      [-1.8045834628018462,4.177556818181818,0],
      [-1.8045834628018462,-9.387784090909093,0],
      [0.29058699174360836,-9.387784090909093,0],
      ...(
        frameCount % 120 < 60
          ? [[0.2905869917436083,3.609374411367136,0]] // doesnt work
       // ? [[0.29058699174360836,3.609374411367136,0]] // works
          : []
      ),
      [0.31044303036623855,4.068235883781435,0],
      [0.38522861430307975,4.522728865422799,0],
      [0.548044378107245,4.941051136363637,0],
      [0.8364672032828204,5.2932224887960775,0],
      [1.2227602871981542,5.526988636363637,0],
      [1.6572258237923885,5.634502949876295,0],
      [2.101666537198154,5.669034090909091,0],
      [2.6695604948237173,5.633568761673102,0],
      [3.0249619917436084,5.5625,0],
      [3.4510983553799726,7.4446022727272725,0],
      [2.8568950819856695,7.613138889205699,0],
      [2.3751340936529037,7.676962586830456,0],
      [1.8892600236717598,7.693181792704519,0],
      [1.2922705720786674,7.649533731133848,0],
      [0.7080836288276859,7.519788939617751,0],
      [0.14854153719815422,7.311434659090909,0],
      [-0.38643934048179873,7.00959666478984,0],
      [-0.858113258144025,6.61653855366859,0],
      [-1.25415732643821,6.1484375,0],
      [-1.5108595282965422,5.697682732328092,0],
      [-1.6824918355513252,5.207533878495854,0],
      [-1.7762971052870198,4.695933154267308,0],
      [-1.8045834628018462,4.177556818181818,0],
    ]
  ]
  
  background('red')
  push()
  stroke(0)
  fill('#EEE')
  scale(15)
  beginShape()
  for (const contour of contours) {
    beginContour()
    for (const v of contour) {
      vertex(...v)
    }
    endContour()
  }
  endShape()
  
  stroke(0, 255, 0)
  strokeWeight(5)
  beginShape(POINTS)
  for (const contour of contours) {
    for (const v of contour) {
      vertex(...v)
    }
  }
  endShape()
  pop()
}

Live: https://editor.p5js.org/davepagurek/sketches/mvVHjXVjM

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions