SVGLoader path issue - some of my paths are being filled unexpectedly

Hi there,

I am just getting to grips with Three.js and am struggling a bit with getting one of my SVG’s to load correctly. It seems that when I fill some of the paths, it’s adding a fill to parts I am not expecting.

I’m not sure if this is due to the SVG I am using or whether I am doing something wrong with the implementation - hoping someone could help me out!

Here is a screenshot of the expected SVG (I have added a black background so you can visually see it):

Here is it being rendered to the canvas:

You can see there’s something weird going on with the fill color.

Here is the code I am using to render the SVG:

const veinsSVGLoader = new SVGLoader(manager)
veinsSVGLoader.load(
  '<MY_SVG_URL>',
  (data) => {
    const paths = data.paths
    const group = new THREE.Group()
    group.scale.set(0.0015, -0.0015, 0.0015) // Scale down for large SVGs
    group.position.set(-0.72, 1.712, 0.15) // Center the SVG in the scene

    paths.forEach((path, index) => {
      const subGroup = new THREE.Group()

      const fillColor = path.userData.style.fill
      if (fillColor) {
        const material = new THREE.MeshBasicMaterial({
          color: new THREE.Color().setStyle(fillColor),
          side: THREE.DoubleSide,
          depthWrite: false,
          transparent: true,
        })

        const shapes = SVGLoader.createShapes(path)
        shapes.forEach((shape) => {
          const geometry = new THREE.ShapeGeometry(shape)
          const mesh = new THREE.Mesh(geometry, material)
          subGroup.add(mesh)
        })
      }

      const strokeColor = path.userData.style.stroke
      if (strokeColor) {
        const material = new THREE.MeshBasicMaterial({
          color: new THREE.Color().setStyle(strokeColor),
          side: THREE.DoubleSide,
          depthWrite: false,
          transparent: true,
        })

        const shapes = SVGLoader.createShapes(path)
        shapes.forEach((shape) => {
          const geometry = new THREE.ShapeGeometry(shape)
          const mesh = new THREE.Mesh(geometry, material)
          subGroup.add(mesh)
        })
      }

      subGroup.name = `group-${index}`
      group.add(subGroup)
    })

    group.renderOrder = 2
    scene.add(group)
  },
  function (xhr) {
    console.log(`Veins: ${(xhr.loaded / xhr.total) * 100}% loaded`)
  },
  function (error) {
    console.log('Veins: An error happened')
  },
)

I have also attached the SVG if that helps!
veins-all

Hoping someone can provide some info!

Thanks in advance.

Jack

1 Like

Can you attach the svg as a text file, so we can look into the svg code itself, instead of the rendered result?

Hey yes of course, the last image is the SVG file, but reattaching here as a .txt file:
veins.txt (102.2 KB)

Thanks!

Thank you.

At the core of the phenomenon you are observing seems to be some ambiguity as for the fill-rule to be applied.

Your svg does not specify any “fill-rule”, so the default applies which is “nonzero” (Source). The difference between fillrule “nonzero” and the other possible setting “evenodd” is shown in this illustration:
fillrule-nonzero
The effect of a nonzero fill rule on paths with self-intersections and enclosed subpaths.

By contrast, “even-odd” works like this:
fillrule-evenodd
The effect of an evenodd fill rule on paths with self-intersections and enclosed subpaths.

Source.

Note the sense of rotation (sequence of vertices), how each sub-path is being specified in the above examples.

Since your svg file doesn’t specify a fill-rule yet renders correctly, I’m afraid the ball is now in Three.js’ court. I couldn’t find any Path or Curve setting to specify which fill-rule is employed on the Three.js side. Maybe someone can take over from here.

Related: SVG extrude without filling the path?

1 Like

Thank you so much for the thorough response!

Really helpful to understand :pray:

I’ll keep tinkering in the meantime! Hopefully someone else has come across a similar issue here - it’s going well apart from this haha

There’s hope! :sunglasses:

The original SVGLoader example code shows your svg flawlessly, as far as I can tell.

I added an import map to the html and adjusted the svg-URL to point to my own webspace, from where it is retrieved.

Codepen

Your turn, @jackkemm

2 Likes

Hey, you’re right that looks perfect to me :smiley:

Thank you for the Codepen link, just compared to my code and yes, it looks like I wasn’t correctly rendering the strokes :man_facepalming:

Thank you so much for your help!!!