Positioning an svg on import

Hey ! I’m trying to understand why when I import an svg it’s position is completely offset from the center (0,0,0) of my scene.

For exemple in Vectary when i’m importing an SVG it is directly well positioned :
image

But in three js when i’m trying to import my SVG he has a position that is not correct.

Maybe the way I import my svg is not correct?

There is my code import with SVGLoader

  useEffect( () => {
    const svgGroup = new THREE.Group();
    const material = new THREE.MeshNormalMaterial();

    // svgGroup.scale.y *= -1;

    svgData.paths.forEach( ( path ) => {
      const shapes = path.toShapes( true );
      shapes.forEach( ( shape ) => {
        const geometry = new THREE.ExtrudeGeometry( shape, {
          depth: 1,
          bevelEnabled: false,
        } );

        const mesh = new THREE.Mesh( geometry, material );

        svgGroup.add( mesh );
      } );
    } );

    scene.add( svgGroup );

If you have an idea of how to do it, I’m interested, thank you!

In SVG, the positive y axis is from top to bottom SVG Coordinate Systems and Units - SVG Viewport and Viewbox (aspose.com).

In threejs, its from bottom to top Transformations and Coordinate Systems | Discover three.js (discoverthreejs.com)

As a result, the SVG appears upside down and backwards. The simplest fix after SVGLoader is to change the scale of the svgGroup.set(1, -1, 1) to flip it.

To center the svgGroup, use Box3

    const box = new Box3()
    box.setFromObject(this)

    const center = new Vector3()
    box.getCenter(center)

    this.translateX(-center.x)
    this.translateY(-center.y)

Hope this helps

Hi, thanks for the fixed svg!

So my problem is that I can center it, but I don’t want it to be in the center, I’d like it to take into account the viewbox to position it. Like this :
image

Because I have the impression that on import it no longer takes the viewbox into account?

I recall having this same problem before when dealing with SVG in that the origin/dimensions of the document isn’t very well defined.

You can get the bounding box of something by using
let bounds = new THREE.Box3().setFromObject( the Thing )
and use that bounds.min/max/size/center to figure out how to place your svg scene.
But this will only get you the bounds of the actual content… not the bounds of the “document” that contained the content.

Correct. The loader doesn’t expose the width, height or viewbox values either.

I’ve been tinkering with some SVG tools and demo app that improve on SVGLoader. It still a work in progress

After loading, it exposes this info after loading

    const svgshape = new SVGShape()
    const schema = svgshape.loadSVG(svg)
    console.log(schema.options?.viewBox)
1 Like

Okay, I’ve just tested with your librairy and it works much better, but the problem is that now I have a blackbox. If i want to remove this black box , but keeping the ‘Loremlpsumusus’ at the same position, it is possible ?

Hey, yes, with the bouding box I can place it wherever I want, but I’d like it to already be in the right position when it loads according to the viewbox, as in the example I showed above with Vectary, i don’t know if it is possible to achieve that

Its hard to see, but there is text in the black box. Black text on a black background is hard to see.

Perhaps change your scene background color so its easy to see the black text.

Does your SVG have a viewbox with negative left and right? Might be helpful to share your example SVG.

I will create a sandbox with the svg files , it will be easier to work with : three-svg-js-starter (forked) - CodeSandbox.

Yes there is a black text, and it’s that text that I want to keep in the same position.

My viewbox is : viewBox="0 0 306.945 219.071">

But i’m getting a negative height and width :

The short answer is that the SVGShape doesn’t support clipPath and is incorrectly drawing what’s in the <def> section. If you remove the <path> from within clipPath, the black box goes away.

I added axis at origin, changed background to white and removed centering so its easier to see

image

I’ll make a few corrections and post an update tomorrow.

Note that SVGShape doesn’t support clipPath. I’ll update readme with SVG elements not supported.

Yesterday I managed to delete the black thanks box. So since the SVGShape doesn’t support clipPath there’s no way of knowing why it starts in this position?

Each path is a text character. The matrix within the transform gives the x and y coordinate

transform=“matrix(1,0,0,-1,139.2339,59.577806)”

The transform in each path is set so each character is positioned perfectly relative to the next.

Try changing the fill color of each path to see this

Yes svg use matrix like that to know each character position.

  [a c e]    [1, 0 ,0 ]
  [b d f] => [-1, 139.23339, 59.5]

Where:

a is the horizontal scaling coefficient (scaleX). In our case, it's 1, so there's no horizontal scaling.

b is the vertical skewing coefficient (skewY). In our case, it's 0, so there's no vertical skewing.

c is the horizontal skewing coefficient (skewX). In our case, it's 0, so there's no horizontal skewing.

d is the vertical scaling coefficient (scaleY). In our case, it's -1, which means the element is flipped vertically (it's mirrored over the x-axis).

e is the horizontal translation amount (translateX). In our case, it's 139.2339, so the element is translated 139.2339 units horizontally.

f is the vertical translation amount (translateY). In our case, it's 59.577806, so the element is translated 59.577806 units vertically.

But i’m not sure how to interpreted these units in three js.

Yes i can just change the fill=“#e62051”/> attribut to change characters colors i guess.

But what I don’t understand is why vectary and blender, for example, position the svg like that, whereas three js doesn’t, even though it’s the same svg.

I wish i could position it like that.

Looks like those other tools center on the viewBox. That’s a design choice I guess.

When the SVG is opened in the browser, 0,0 is top left. There’s no concept of centering the viewbox in the browser.

Okay, so I couldn’t achieve this result by centering the viewbox because 0,0 is top left?

I’m not sure to understand.

I published an updated NPM package. There’s an new method on SVGShape to centerOnViewBox

I modified your original example to show how to use it.

three-svg-js-starter (forked) - CodeSandbox

image

Amazing, it’s working like a charm! Can i have some insight to understand how did you achieve this ?

Anyway thanks you!

Once I fixed the problem with width and height being incorrect, its easy to center. Just create a bounding box from origin to viewbox size, find the center and translate object to center.

  centerOnViewBox(): Vector3 {
    const box = new Box3()
    box.expandByPoint(new Vector3())
    box.expandByPoint(new Vector3(this.svg.width, this.svg.height, 0).multiply(this.scale))

    const center = new Vector3()
    box.getCenter(center)

    this.translateX(-center.x)
    this.translateY(-center.y)

    return box.getSize(center)
  }
1 Like