I am having trouble with applying a transformation to each path in my SVG. I know the exact transform I need to add as I am using a map from wikimedia and have tested it by adding it using firebug/chromebug.
I need to add:
transform="translate(0,239) scale(0.016963,-0.016963)"
This is an example of what I have:
http://jsfiddle.net/m7t4X/1/
If you inspect it, each path looks like this:
<path fill="#ff0000" stroke="#ccc6ae" d="M16760,5958C16752,5945,16745,5920,16743,5901C16740,5859,16734,5853,16710,5868C16694,5878,16690,5878,16690,5866C16690,5858,16696,5849,16703,5846C16709,5843,16705,5843,16693,5846C16670,5851,16666,5845,16669,5808C16670,5799,16666,5789,16660,5785C16655,5782,16650,5770,16650,5760C16650,5747,16645,5743,16635,5746C16626,5750,16620,5746,16620,5737C16620,5717,16549,5667,16495,5649C16473,5642,16451,5632,16446,5628C16440,5624,16426,5620,16413,5620C16395,5620,16390,5615,16390,5593C16390,5577,16381,5552,16371,5536C16351,5506,16351,5482,16371,5367C16383,5300,16383,5298,16354,5242C16338,5211,16321,5182,16317,5179C16282,5154,16269,5063,16294,5016C16302,5000,16306,4972,16303,4946C16300,4920,16302,4900,16309,4896C16315,4892,16320,4881,16320,4871C16320,4849,16350,4820,16373,4820C16382,4820,16390,4816,16390,4810C16390,4795,16442,4799,16464,4815C16475,4823,16496,4830,16511,4830C16541,4830,16580,4870,16580,4899C16580,4908,16586,4924,16594,4935C16601,4946,16613,4980,16620,5010C16628,5040,16642,5081,16651,5101C16661,5121,16675,5159,16681,5186C16688,5213,16710,5278,16731,5332C16752,5385,16772,5446,16775,5467C16787,5539,16790,5550,16803,5547C16815,5545,16816,5554,16804,5630L16797,5675L16817,5651L16837,5626L16854,5658C16866,5682,16868,5695,16860,5709C16855,5720,16850,5749,16850,5773C16850,5797,16845,5832,16839,5851C16825,5895,16786,5979,16780,5980C16777,5980,16768,5970,16760,5958Z" stroke-opacity="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); stroke-opacity: 1;"></path>
If I add it manually via firebug etc to change the start to look like this:
<path transform="translate(0,239) scale(0.016963,-0.016963)" fill="#ff0000" stroke="#ccc6ae" d="
then it works perfectly. I just cannot replicate this.
I have tried various ways of adding the transform, but none have worked fully.
From what I gather, RaphaelJS is backwards compatible so all of the solutions I have found (which haven't fully worked anyway) wouldn't have worked in any older IEs for instance.
The first thing I tried was to change:
paper.setViewBox(0, 0, w, h, true);
to:
paper.setViewBox(true);
However this did not work with the shapes I am using (http://upload.wikimedia.org/wikipedia/commons/9/95/Continents.svg)
This is where I got the transform amounts from (can be seen if you view source).
Please advise on where I'm going wrong, and how I could go about adding this.
Many thanks!
---------------------------------EDIT -------------------------------------
After applying the fix below, I have found it appears to be displaying the SVG rotated 180degrees and flipped horizontally. I don't quite understand why either.
This is how it is meant to look:
And this is how it comes out:
(also noticed I'm missing south america despite it being referenced)
This is a new fiddle of how it currently looks:
http://jsfiddle.net/wrayvon/m7t4X/3/
Thanks again
The transform should work, but I think you just need to set a bigger width/height for the viewBox. These should be for the co-ordinate space, not for pixels on screen.
var w = 15000;
var h = 25000;
paper.setViewBox(10000, 0, w, h, true);
Should do it. You can also transform it if you want, by adding something like the following to the element when created.
.transform('t2000,1000')
edit: As you have changed the SVG... If there are transforms in the original, you will probably need to apply the same transforms to the elements...so a line like this...
var c = paper.path(worldmap.shapes[country]).attr({ stroke: "#ccc6ae", fill: "#ff0000", "stroke-opacity": 1 }).transform('s1,-1,13000,10000');
I'm just guessing the centre point to scale from there (13,000,10,000 its probably a bit more), you will need to figure that separately.
Or you could match the scale as per the original SVG, and then you probably won't need to change the viewBox (apart from matching the original).
Original jsfiddle
Updated jsfiddle
I have had success with the exact same problem. Your question is the clearest articulation of it.
As a bit of background, I found an SVG map of the US and all of its counties. I thought, "great, I'll just copy these paths into Raphael, and Bob's your uncle". So, I did, but it appeared exactly upside down after the import. I considered applying some math to the paths, but decided against it. Instead I investigated the transform() option too.
In the transform method, there are a few options. One of the options is to use the matrix ("m"). The matrix method has 6 options. - see here
for more details. But the matrix setting for the 'as-the-path' dictates is this:
.transform("m1,0,0,1,0,0");
If you make the 4th place negative, it will invert your image.
.transform("m1,0,0,-1,0,0");
HOWEVER...it has to choose a location for the axis on which to flip. That, as far as I can tell, is the top edge of the the container div. So your image is now above you. Use the viewbox to find it and zoom as you desire:
R.setViewBox(x,y,x-size,y-size);//The Y (second param) will need to be negative.
Here's my example to find my shape: (make your 200's into 1000's to more easily locate you image, then play with the numbers to center, and enlarge)
R.setViewBox(0,-575,200,200);
The downside using the transform is the overhead of drawing, then relocating the images. In my case, I am less concerned about this as speed doesn't seem to be affected, which may be due to the relatively small number of paths involved.
I'm trying to rotate and scale shapes within an SVG around their center point. I've looked into several libraries, including Jquery, Greensock, D3, RaphaelJS, but I haven't been able to find any that provide a straightforward way to accomplish this. Each animates the shape from the origin point (which I understand is the default). I want to be able to spin a shape around its center point or scale it up or down from the center point.
Here are a couple examples using Greensock and D3 that illustrate the default behavior: http://jsbin.com/AHEXiPa/1/edit?html,js,output
Each of these examples bounce in and out from the top left as opposed to remaining stationary and expanding from the center of the triangle out in all directions.
Can one of the libraries I mentioned accomplish this, or is there another library or method I should consider?
Ideally, I need to be able to apply the animation/transform to an existing object in the DOM. D3 is good at this for instance, but Raphael seems to require converting an SVG to Raphael first prior to injecting it into the DOM.
Really its a case of pick the library that suits your needs, and then you will figure a way. As BigBadaboom says, if you do a search, there are lots of solutions.
To try and combine your questions, as sometimes the tricky bit is using an existing DOM object, I've included an example in Snap.svg. You can often do something similar in most libraries.
jsfiddle here Fiddle using your existing html.
s = Snap("#mySVGContainer1"); // create a canvas from existing svg
var triangle1 = s.select("#myShape1").transform("r90"); //select&transform existing object
p = Snap("#mySVGContainer2");
var triangle2 = p.select("#myShape2");
var bbox = triangle2.getBBox(); //bounding box, centre cx/cy
//rotate and scale with transform string (raphael/snap format)
triangle2.animate({ transform: "r180," + bbox.cx + ',' + bbox.cy + "s3,3," + bbox.cx + "," + bbox.cy }, 2000);
For rotations, as #Ian points out, you can specify the center of rotation. For other transformations, changes are defined relative to the path's (0,0) point.
The easiest way to get transformations to work relative to the path's center is to either:
Define the path so that it is centered around the (0,0) point; or
Wrap the path in a <g> element, and then translate it so it is centered on the (0,0) point of the <g> element's coordinate system.
Then, you can apply rotations, scales and transforms (on the <g> element, if using) and they will all be nicely centred.
The trickiest part is figuring out the "center" of an arbitrary shape. #Ian's approach of using the center of the bounding box will usually give decent results. If your shape is a polygon there are d3 functions you could use.
Example showing a shape moving with the mouse, rotating and changing scale, all centered around the center of the bounding box:
http://fiddle.jshell.net/LgfE3/
Edit: simplier jsfiddle
I've been looking for a long time, and will settle for the following.
1. Design your svg shape at coordinate x:0,y:0.
2. Identify by hand the center of rotation, by example, center = [ x:50,y:100].
3. Build a spinIt() function such :
function spinIt() {
needle.transition()
.duration(2000)
.attrTween("transform", tween);
function tween() {
return d3.interpolateString("rotate(-180, 50, 100)", "rotate(90, 50, 100)");
}
}
4. Use it on a triger:
svg.on("click", spinIt);
http://jsfiddle.net/SHF2M/79/
Essentially, what I want to do (in DirectX) is to take two partially-transparent images and blend them together. This works fine with default blending, insofar as they both show up as overlapping, etc. However, the problem is that the opacity goes up markedly where the two intersect. This causes increasing problems as more sprites overlap. What I'd like to do is keep the blending the same, except keep a global opacity for all these sprites being blended, regardless of how they overlap.
Seems like there would be a render setting for this (all of these sprites are alone in their sprite batch, which keeps that part easy), but if so I don't know it. Right now I'm kind of shooting in the dark, and I've tried a lot of different things and none of them have looked right at all. I know I probably need some sort of variant of D3DBLENDOP, but I just don't know what sort of settings there I really need (I have tried many things, but it is all guessing at this stage).
Here is a screenshot of what is actually happening with standard blending (the best I can get it): http://arcengames.com/share/FFActual.png Here is a screenshot with a mockup of how I would want the blending to turn out (the forcefields were added to the same layer in Photoshop, then given a shared alpha value): http://arcengames.com/share/FFMockup.png
This is how I did it in Photoshop:
1. Take the two images, and remove all transparency (completely transparent pixels excepted).
2. Combine them into one layer, which blends the color but which has no partial alpha at all.
3. Now set the global transparency for that layer to (say) 40%.
The result is something that looks kind of blended together color-wise, but which has no increase in opaqueness on the overlapped sections.
UPDATE: Okay, thanks very much to Goz below, who suggested using the Z-Buffer. That works! The blending, by and large, is perfect and just what I would want. The only remaining problem? Using that new method, there is a huge artifact around the edge of the force field image that is rendered last. See this: http://www.arcengames.com/share/FFZBuffer.png
UPDATE: Below is the final solution in C# (SlimDX)
Clearing the ZBuffer to black, transparent, or white once per frame all has the same effect (this is right before BeginScene is called)
Direct3DWrapper.ClearDevice( SlimDX.Direct3D9.ClearFlags.ZBuffer, Color.Transparent, 0 );
All other sprites are drawn at Z=1, with the ZBuffer disabled for them:
device.SetRenderState( RenderState.ZEnable, ZBufferType.DontUseZBuffer );
The force field sprites are drawn at Z=2, with the ZBuffer enabled and ZWrite enabled and ZFunc as Less:
device.SetRenderState( RenderState.ZEnable, ZBufferType.UseZBuffer );
device.SetRenderState( RenderState.ZWriteEnable, true );
device.SetRenderState( RenderState.ZFunc, Compare.Less );
The following flags are also set at this time, to prevent the black border artifact I encountered:
device.SetRenderState( RenderState.AlphaTestEnable, true );
device.SetRenderState( RenderState.AlphaFunc, Compare.GreaterEqual );
device.SetRenderState( RenderState.AlphaRef, 55 );
Note that AlphaRef is at 55 because of the alpha levels set in the specific source image I was using. If my source image had a higher alpha value, then the AlphaRef would also need to be higher.
Best I can tell is that the forcefields are a whole object. Why not render them last, in front to back order, and with Z-buffering enabled. That will give you the effect you are after.
ie its not blending settings thats your problem at all.
Edit: Can you use render-to-texture then? IF so you could easily do what you did under photoshop. Render them all together into the texture and then blend the texture back over the screen.
Edit2: How about
ALPHATESTENABLE = TRUE;
ALPHAFUNC = LESS
ALPHABLENDENABLE = TRUE;
SRCBLEND = SRCALPHA;
DESTBLEND = INVSRCALPHA;
SEPERATEALPHABLENDENABLE = TRUE;
SRCBLENDALPHA = ONE;
DESTBLENDALPHA = ZERO;
You need to make sure the alpha is cleared to 0xff in the frame buffer each frame. You then do the standard alpha blend. while passing the alpha value straight through to the backbuffer. This is, though, where the alpha test comes in. You test the final alpha value against the one in the back buffer. If it is less than whats in the backbuffer then that pixel has not been blended yet and will be put into the frame buffer. If it is equal (or greater) then it HAS been blended already and the alpha value will be discarded.
That said ... using a Z-Buffer would cost you a load of RAM but would be faster overall as it would be able to throw away the pixels far earlier in the pipeline. Seeing as all the shields would just need to be written to a given Z-plane you wouldn't even need to go through the hell I suggested earlier. If the Z value it receives is less than whats there already then it will render it if it is greater or equal then it will discard it, fortunately before the blend calculation is ever performed.
That said ... you could also do it by using the stencil buffer which would require a Z-buffer anyway.
Anyway ... hope one of those methods is of some help.
Edit3: DO you render the forcefield with some form of feathering around the edge? Most likely that edge is caused by the fact that the alpha fades off slightly and then the "slightly alpha" pixels are getting written to the z-buffer and hence any subsequent draw doesn't overwrite them.
Try the following settings
ALPHATESTENABLE = TRUE
ALPHAFUNC = GREATEREQUAL // if this doesn't work try less .. i may be being a retard
ALPHAREF = 255
To fine tune the feathering around the edge adjust the alpharef but i'd suspect you need to keep it as above.
You can specify the D3DBLENDOP used when blending the two images together for the alpha channel. It sounds like your using D3DBLENDOP_ADD currently - try switching this to D3DBLENDOP_MAX, as that will just use the opacity of the "most opaque" image.
It is hard to tell exactly what you are trying to accomplish from your mock up since both forcefields are the same color; do you want to blend the colors and cap the alpha? Just take one of the colors?
Based off the above discussion it isnt' clear if you are setting all the relevant render states:
D3DRS_ALPHABLENDENABLE = TRUE (default: FALSE)
D3DRS_BLENDOP = D3DBLENDOP_MAX (default: D3DBLENDOP_ADD)
D3DRS_SRCBLEND = D3DBLEND_ONE (default: D3DBLEND_ONE)
D3DRS_DESTBLEND = D3DBLEND_ONE (default: D3DBLEND_ZERO)
It sounds like you are setting the first two, but what about the last two?