95

How do I most easily first scale an object, say 2 * times it's current size and then flip it vertically and horizontally, or both?

As of now, I can either set "scale(2,2)" for it to become 2 times as big as it's width and height but I can't flip it at the same with scale(-1, 1) for vertical flip.

I'm creating SVG objects programmatically, as a format to export to.

ZachB
  • 13,051
  • 4
  • 61
  • 89
Deukalion
  • 2,516
  • 9
  • 32
  • 50

5 Answers5

156

To apply both scale and flip, just list both in your transform:

transform="scale(2,2) scale(-1,1)"

Or simply combine the values:

transform="scale(-2,2)"

Of course, the issue you have with negative scales is that the objects get flipped across the origin (top left) of the SVG, so they can go off the edge of the document. You need to correct this by adding a translate as well.

So, for example, imagine we had a document that is 100×100.

<svg width="100" height="100">
    <polygon points="100,0,100,100,0,100"/>
</svg>

To flip this vertically, you do:

<polygon points="100,0,100,100,0,100" transform="scale(2,-2)"/>

And to correct the movement off-screen, you can either...

(option 1) Shift it negative before the flip (so it gets flipped back on screen):

<polygon points="100,0,100,100,0,100" transform="scale(2,-2) translate(0,-100)"/>

(The translate is listed second here because transform lists are effectively applied right to left)

(option 2) Or, you can shift it positive (by the scaled size) afterwards:

<polygon points="100,0,100,100,0,100" transform="translate(0,200) scale(-2,2)"/>

Here is a demo showing vertical flip, horizontal flip and both flips

Update

To flip (in position) an already existing object that is somewhere on screen. First determine its bounding box (minX, minY, maxX, maxY), or centreX,centreY if you already know that instead.

Then prepend the following to its transform:

translate(<minX+maxX>,0) scale(-1, 1)   // for flip X
translate(0,<minY+maxY>) scale(1, -1)   // for flip Y

or if you have the centre you can use

translate(<2 * centreX>,0) scale(-1, 1)   // for flip X

So in your example:

<rect x="75" y="75" width="50" height="50"  transform="translate(-100, -100) scale(2, 2) scale(1, 1) rotate(45, 100, 100)" />

The minX+maxX comes to 200. So to flip horizontally, we prepend:

translate(200,0) scale(-1, 1)

So the final object becomes:

<rect x="75" y="75" width="50" height="50"  transform="translate(200,0) scale(-1, 1) translate(-100, -100) scale(2, 2) scale(1, 1) rotate(45, 100, 100)" />

Demo here

johannchopin
  • 13,720
  • 10
  • 55
  • 101
Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
  • This helps alot. But I still have trouble to figure out how to properly save them in SVG format. On each object you can: Vertical Mirror or Horizontal Mirror. Where as the Vertical line is half the width, and Horizontal is at half the height. Drawn something on the left side, it should appear on the right side. Simple enough, until each object gets scaled or not scaled, moved from it's original "x" and "y" = Translate or Rotated. I'm not sure make an SVG transform attribute of it. I know the objects center, the size of the document and such and Transform, Scale, Rotate values + V or H mirror – Deukalion May 29 '14 at 09:12
  • You've of course answered the question, this is just an extended question to put it all out. I can't seem to put all the other values into the equation now when this is figured out. – Deukalion May 29 '14 at 09:14
  • This is an attempt to show what I mean and include such things as rotate, scale into the mix: http://jsfiddle.net/kj78D/59/ - as can be seen the gradient is also reverse, which indicate a mirroring... now I'm not sure where I'm suppose to put the "transform" - say if the object was placed at x=75, y=75 but moved 25 in each direction. Where that goes, and I'm not entirely sure why the mirrored rectangle needs "translate(-300, -100) since they don't point to anything specific, while the first objects translate at least point's to the objects negative center? Or maybe they both do. – Deukalion May 29 '14 at 09:54
  • My experiment will probably not work if I put the first "reversed" rectangle on the opposite side (where the mirrored one currently is) and retain the values. Probably not gonna look the same, so I'm probably just wasting my time understanding this. I tried implementing some of it, but it doesn't work everytime which is why I need a good template where I can put all of the stuff in, get it reversed no matter what values are put in. Matrices have always been a pain in the ass. – Deukalion May 29 '14 at 10:48
  • Translate Scale Flip Rotate Translate? In no specific order. Create a rectangle at a given coordinate within the document, rotate it some degrees, move it some coordinates, scale it to something other than it's original size and finally flip it all so it gets vertically flipped or horizontally flipped or both. Only problem is converting my values to SVG format. Of course, as I probably stated before one of these values does not need to be included and perhaps there's a different scenario for each one of them. Say I exclude rotate or scale; probably gonna look different in SVG Format as well. – Deukalion May 29 '14 at 11:54
  • I'm not sure I understand. If you give an example of an object you are having trouble flipping, I will do my best to show you. – Paul LeBeau May 29 '14 at 13:54
  • My question was for scaling an object 2 times or any times and then be able to flip it. This works fine. But when I try to work in rotation on an object, the movement of an object and then save it as SVG - it gets really messy. I'm using SVG as an exported format for my Android app, a drawing app. Paint something on the screen, rectangle, path, circle or whatever and then you can scale it through tools, move it. So when it's drawn, the X and Y values are set. From there on out I use a "Transform X Y" coordinate, so the user is able to reset the transform. – Deukalion May 29 '14 at 18:28
  • In short, make an object scaled, rotated and translated and at the same time flipped vertically. When I draw it, I just flip it. Works great. When this is exported to SVG, I first add the "normal" object (rectangle say) and then add another object (the vertical mirror) but the values to put in transform="" never works out as I wanted them too. So a scaled object 2 times, rotated 45 degrees and moved 50 points in X and Y from it's original X and Y coordinate (they don't change - to be able to reset it). Then put all this into a SVG transform, but not sure how. – Deukalion May 29 '14 at 18:32
  • Like I said, just an extension of the question. It's already been answered, just trying to understand the rest of it. Since I thought the only problem I had was flipped the object but it seems that I need more to be able to save it properly in SVG format. Not sure if you know any programming language, but this is my variant in Android or Java: http://pastebin.com/gA9MSNSP, where I draw each object flipped. Not sure how to translate that into SVG even though I know all required values. If I export it to SVG without the flipping, it works great. When I add the flipping it no longer works. – Deukalion May 29 '14 at 18:41
  • My head hurts trying to understand what you want. :/ Please make a fiddle with an example SVG that has the object you want to flip. Then tell me how you want it flipped. For example do you want it to be flipped in place, or do you want the whole SVG flipped? – Paul LeBeau May 29 '14 at 19:40
  • The objects are created by an Android app. Not all objects should be flipped, only the one that in the app is set as a Vertical or Horizontal Mirror. And each object can be rotated, moved, scaled and finally flipped. I've experimented with all values, like the fiddle I posted before - where I scaled it, rotated it and flipped it = correctly. But when I apply that method to my Export method to SVG it doesn't exactly work the same way. http://jsfiddle.net/kj78D/59/ Only thing I didn't do in that fiddle is move the object. Save it as SVG = Save normal object, if "VMirror" set, add flipped version – Deukalion May 29 '14 at 20:11
  • I think I solved it. Mapping it all out. I've made some algorithms from it. Perhaps this explains it better: http://pastebin.com/z7PDbusN – Deukalion May 30 '14 at 10:45
  • Thanks. I missed that you made an update on the post - which the site doesn't tell you about. Anyhow, it seems to work now. Only problem remaining is texts, since each TextSpan seems to need it's own transformation individually. – Deukalion Jun 07 '14 at 12:54
47

simply add below attributes into path tag in svg

transform="scale (-1, 1)" transform-origin="center"

Eg: <path transform="scale (-1, 1)" transform-origin="center" ......./>

Rayees Pk
  • 2,503
  • 1
  • 23
  • 19
16

Meet "Tux" the pinguin. For the sake of this exercise I painted the letters "L" and "R" on his feet.

Tux the pinguin

For starters, let's paint Tux in the center of our canvas. If the canvas is size 500x500, and if Tux has a size of 100x100 we have to position him at (200,200). (i.e. the center minus half its size.)

  <svg width="500" height="500">
    <!-- marking our border and a cross through the center -->
    <rect x="0" y="0" width="500" height="500" stroke-width="2" stroke="red" fill="none"></rect>
    <line x1="0" y1="0" x2="500" y2="500" stroke="red" stroke-width="2"></line>
    <line x1="500" y1="0" x2="0" y2="500" stroke="red" stroke-width="2"></line>

    <!-- our pinguin in the center -->
    <image x="200" y="200" width="100" height="100" href="assets/pinguin.png"></image>
  </svg>

The result

Now, if we want to mirror our pinguin horizontally (switching left and right) it is tempting to just use a transform with scale(-1 1). However, our pinguin just dissappears when we try that.

  <svg width="500" height="500">
    ...
    <image ... transform="scale(-1 1)"></image>
  </svg>

pinguin is gone

The reason, is that the default point of reflection (the so-called "transform-origin") for our transform is not in the center of our image, but is actually still at the (0,0) point.

The most obvious solution is to move the point of reflection to the central point of the image (250,250). (in this case, the center of our canvas).

  <svg width="500" height="500">
    ...
    <image ... transform="scale(-1 1)" transform-origin="250 250"></image>
  </svg>

Change the transform origin

And resizing works exactly the same. You can do it in 2 scales or combine them in 1 scale.

  <svg width="500" height="500">
    <!-- use 2 scales -->
    <image x="200" y="200" width="100" height="100" href="assets/pinguin.png"
           transform="scale(-1 1) scale(2 2)" transform-origin="250 250">
    </image>    
    <!-- or just multiply the values of both scales -->
    <image x="200" y="200" width="100" height="100" href="assets/pinguin.png"
           transform="scale(-2 2)" transform-origin="250 250">
    </image>
  </svg>

still 2 options

bvdb
  • 22,839
  • 10
  • 110
  • 123
1

No solution worked for me, I'll post what I discovered:

You can use either matrix or css-like transformations. And they behave different.

Take a look at this very basic example with an original shape and different ways to flip it horizontally. Notice that depending on the translation (you may want to keep it in the same x-axis) and the type of transformation you are using, you will need to set a different x-axis transformation.

Observation:

  • Matrix

    • Same place (light green): translate with positive width size.
    • X translation (dark green): Expected behavior (same as light green).
  • CSS-like

    • Same place (light blue): translate with negative width size and placed after scale. The opposite order is out of viewBox (note pink shape).
    • X translation (dark blue): translate with negative width size plus positive translation and placed before scale. The opposite order is out of viewBox (note orange shape).

<svg 
  viewBox="0 0 15 30" 
  width="150" 
  height="300" 
  xmlns="http://www.w3.org/2000/svg"
>
  <defs>
    <path 
      id="triangle"
      d="M0,5 l5,5 V0z" 
    />
  </defs>
  <use 
    href="#triangle"
    fill="red" 
  />
  <use
    y="5"
    href="#triangle"
    transform="scale(-1, 1) translate(-5, 0)"
    fill="lightBlue" 
  />
  <use
    y="5"
    href="#triangle"
    transform="translate(-5, 0) scale(-1, 1)"
    fill="pink" 
  />
  <use
    y="15"
    href="#triangle"
    transform="matrix(-1 0 0 1 5 0)"
    fill="lightGreen" 
  />
  <use
    href="#triangle"
    transform="translate(10, 0) scale(-1, 1)"
    fill="darkBlue" 
  />
  <use
    href="#triangle"
    transform="scale(-1, 1) translate(10, 0)"
    fill="orange" 
  />
  <use
    href="#triangle"
    transform="matrix(-1 0 0 1 15 0)"
    fill="darkGreen" 
  />
</svg>
llobet
  • 2,672
  • 23
  • 36
0

Here is the Livescript-ish code snippet how you can horizontally flip and scale by any factor:

    # scale is 1 by default

    if mirror or scale isnt 1
        [minx, miny, width, height] = svg.attributes.viewBox |> split-by ',' |> cast-each-as (Number)

        s = scale
        # container is the root `g` node 
        container.attributes.transform = if mirror
            "translate(#{s * (width + minx) + minx}, #{-miny * (s-1)}) scale(#{-s},#{s})"
        else
            "translate(#{-minx * (s-1)}, #{-miny * (s-1)}) scale(#{s},#{s})"

        if scale isnt 1
            svg.attributes
                ..viewBox = "#{minx},#{miny},#{width * scale},#{height * scale}"
                ..width = "#{width * scale}"
                ..height = "#{height * scale}"
ceremcem
  • 3,900
  • 4
  • 28
  • 66