1. SVG graphic scaling

The following creates an SVG display of 5 lines at a specific place on the screen.
Code:
```<svg xmlns="http://www.w3.org/2000/svg"
height="200" width="300" style="border:1px solid black">
<line x1="0"  y1="10" x2="0"   y2="110" style="stroke-width:6; stroke:#ff0000;"/>
<line x1="20" y1="20" x2="120" y2="50" style="stroke-width:6; stroke:#00ff00;"/>
<line x1="30" y1="30" x2="140" y2="100" style="stroke-width:6; stroke:#0000ff;"/>
<line x1="40" y1="40" x2="150" y2="150" style="stroke-width:6; stroke:#006600;"/>
<line x1="75" y1="25" x2="200" y2="25" style="stroke-width:6; stroke:#006600;"/>
</svg>```
What I would like to know is: Is it possible to make the hard coded values of x1,y1, x2,y2
to be scaled to a world range?

For example, if I define the "world" view to be 1000 wide an 800 high,
and given the actual screen if 300 pixels wide by 200 pixels high,
I could calculate the world positions on the screen as:

Code:
```<script>
var World = [1000,800];
var Actual = [300,200];
var x1 = 175;
var y2 = 125;
var x2 = 200;
var y2 = 225;
sx1 = x1 / World[0] * Actual[0];
sy1 = y1 / World[1] * Actual[1];
sx2 = x2 / World[0] * Actual[0];
sy2 = y2 / World[1] * Actual[1];
</script>
<!-- following line does not work -->
<line x1=sx1 y1=sy1 x2=sx2 y2=sy2 style="stroke-width:6; stroke:#006600;"/>```
I want to use real world numbers like feet, yards, meters, etc
and scale them to the actual size of the graphics display
but I don't know how to NOT hard code into the SVG tags.

Is there a way to do this?

2. Registered User
Join Date
Mar 2011
Posts
1,252
Since I know almost nothing about SVG graphics, I thought I'd share my lack of wisdom with you in the great tradition of online forums.... Actually, I have been curious about SVG, and just thought I might pick up something in your discussion - especially about the scaling parts. Anyway, it just struck me that as long as you could write the JavaScript that corresponds to your needs, you could always output the SVG tags in your HTML mark-up using that code. That would get you by until you can find the 'real' solution.

I am truly rotten when it comes to graphics, so when I need something like this, I generally rely on ImageMagick via PHP (or Perl, in days gone by). I've played with the HTML 5 <canvas> and managed to create a single SVG image, and I think there's a lot of ways I could use it once IE8 use is no longer significant. Good luck!

3. Yes, I can create a JS script to create the SVG tags
but, even without any errors being reported, nothing is displayed.

See this example...
Code:
```<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8" />

<title> SVG creation with JS </title>
<body>

<h2> Normal version </h2> 300 x 250
<svg id="actual" xmlns="http://www.w3.org/2000/svg"
width="300" height="250" style="border:1px solid black">
<line x1="0"  y1="10" x2="0"   y2="110" style="stroke-width:6; stroke:#ff0000;"/>
<line x1="20" y1="20" x2="120" y2="50" style="stroke-width:6; stroke:#00ff00;"/>
<line x1="30" y1="30" x2="140" y2="100" style="stroke-width:6; stroke:#0000ff;"/>
<line x1="40" y1="40" x2="150" y2="150" style="stroke-width:6; stroke:#006600;"/>
<line x1="75" y1="25" x2="200" y2="25" style="stroke-width:6; stroke:#006600;"/>
<line x1="175" y1="125" x2="200" y2="225" style="stroke-width:6; stroke:#000000;"/>
</svg>

<h2> Scaled version </h2> 1000 x 800
<svg id="world" xmlns="http://www.w3.org/2000/svg"
width="300" height="200" style="border:1px solid black">
</svg>

<script type="text/javascript">
var World = [1000,800]; // w,h
var Actual = [300,250]; // w,h

function scaleX(x) { return Math.floor(x / World[0] * Actual[0]); }
function scaleY(y) { return Math.floor(y / World[1] * Actual[1]); }
function scaleLine(x1,y1,x2,y2) {
var str = '<line x1="'+x1+'" y1="'+y1+'" x2="'+x2+'" y2="'+y2+'"';
str +=' style="stroke-width:6; stroke:#000000;"/>';
//  alert('World\n'+x1+' '+y1+' '+x2+' '+y2);  // shows scaling works
return str;
}

function init() {
var x1 = 175;  var sx1 = scaleX(x1);  var y1 = 125;  var sy1 = scaleY(y1);
var x2 = 200;  var sx2 = scaleX(x2);  var y2 = 225;  var sy2 = scaleY(y2);
//  alert('Actual\n'+x1+' '+y1+' '+x2+' '+y2);  // shows scaling works
document.getElementById('world').innerHTML = scaleLine(sx1,sy1,sx2,sy2);
}
window.onload = function() { init(); }

</script>
</body>
</html>```
I thought I could create the SVG line command, but it does not seem to activate this way?

4. Works OK. Still looking for better...

I've played around with it some more and can now do what I originally wanted in post #1.
It is not a clean as I would like as it requires defining a scaled SVG command for every normal SVG command.

Would still be interested in making the change using variables within the normal commands,
but this will suffice for my needs at this time.

Code:
```<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8" />

<title> SVG creation with JS </title>
<body>

<h2> Normal version </h2> 300 x 250
<svg id="actual" xmlns="http://www.w3.org/2000/svg"
width="300" height="250" style="border:1px solid black">
<line x1="0"  y1="10" x2="0"   y2="110" style="stroke-width:6; stroke:#ff0000;"/>
<line x1="20" y1="20" x2="120" y2="50" style="stroke-width:6; stroke:#00ff00;"/>
<line x1="30" y1="30" x2="140" y2="100" style="stroke-width:6; stroke:#0000ff;"/>
<line x1="40" y1="40" x2="150" y2="150" style="stroke-width:6; stroke:#006600;"/>
<line x1="75" y1="25" x2="200" y2="25" style="stroke-width:6; stroke:#006600;"/>
<line x1="175" y1="125" x2="200" y2="225" style="stroke-width:6; stroke:#000000;"/>
<circle cx="100" cy="150" r="40" stroke="black" stroke-width="2" fill="red"/>
</svg>

<h2> Scaled version </h2> 1000 x 800
<svg id="world" width="300" height="250" xmlns="http://www.w3.org/2000/svg"
width="300" height="200" style="border:1px solid black">
</svg>

<script type="text/javascript">
var World = [1000,800]; // w,h
var Actual = [300,250]; // w,h

function scaleX(x) { return Math.floor(x / World[0] * Actual[0]); }
function scaleY(y) { return Math.floor(y / World[1] * Actual[1]); }
function scaleLine(x1,y1,x2,y2,stkW,stkC) {
x1 = scaleX(x1);  y1 = scaleY(y1);
x2 = scaleX(x2);  y2 = scaleY(y2);

var str = '';
str += '<svg>';
str +=' <line x1="'+x1+'" y1="'+y1+'" x2="'+x2+'" y2="'+y2+'"';
str +=' style="stroke-width:'+stkW+'; stroke:'+stkC+'"/>';
str +='</svg>';
return str;
}
function scaleCircle(x,y,r,stkW,stkC,stkF) {
x = scaleX(x);  y = scaleY(y);  r = scaleX(r);

var str = '';
str += '<svg>';
str += '<circle cx="'+x+'" cy="'+y+'" r="'+r+'"';
str += ' stroke="'+stkC+'" stroke-width="'+stkW+'" fill="'+stkF+'"/>';
str += '</svg>';
return str;
}

function init() {
var sx1 = sy1 = sx2 = sy2 = 0;

sx1 = 0;   sy1 = 10;  sx2 = 0;   sy2 = 110;
document.getElementById('world').innerHTML += scaleLine(sx1,sy1,sx2,sy2,'3','#ff0000');

sx1 = 20;   sy1 = 20;  sx2 = 120;   sy2 = 50;
document.getElementById('world').innerHTML += scaleLine(sx1,sy1,sx2,sy2,'3','#00ff00');

sx1 = 30;   sy1 = 30;  sx2 = 140;   sy2 = 100;
document.getElementById('world').innerHTML += scaleLine(sx1,sy1,sx2,sy2,'3','#0000ff');

sx1 = 40;   sy1 = 40;  sx2 = 150;   sy2 = 150;
document.getElementById('world').innerHTML += scaleLine(sx1,sy1,sx2,sy2,'3','#006600');

sx1 = 75;   sy1 = 25;  sx2 = 200;   sy2 = 25;
document.getElementById('world').innerHTML += scaleLine(sx1,sy1,sx2,sy2,'3','#006600');

sx1 = 175;   sy1 = 125;  sx2 = 200;   sy2 = 225;
document.getElementById('world').innerHTML += scaleLine(sx1,sy1,sx2,sy2,'3','#000000');

sx1 = 100;   sy1 = 150;  sx2 = 40;
document.getElementById('world').innerHTML += scaleCircle(sx1,sy1,sx2,'#000000','3','#ff0000');

}
window.onload = function() { init(); }

</script>
</body>
</html>```

5. Registered User
Join Date
Mar 2011
Posts
1,252
I had a hunch that searching with the keyword "dynamic" might be helpful, but all of the references I found relied on JavaScript, so I think that's the only viable approach at the moment.

6. Registered User
Join Date
Dec 2012
Posts
264
It might be a bit neater if you create SVG elements and append them to the world element instead of creating a string and updating the innerHTML, you could create a function that automates the creation of an SVG element like so

HTML Code:
```function svgElement(element,attributes) {
var element = document.createElementNS("http://www.w3.org/2000/svg",element);
for(key in attributes) {
element.setAttribute(key,attributes[key]);
}
return element;
}
function scaleLine(x1,y1,x2,y2,stkW,stkC) {
x1 = scaleX(x1);  y1 = scaleY(y1);
x2 = scaleX(x2);  y2 = scaleY(y2);
var attributes = {
"x1":x1,
"x2":x2,
"y1":y1,
"y2":y2,
"style":"stroke-width:"+stkW+"; stroke:"+stkC
};
var line = svgElement("line",attributes);
return line;
}
function scaleCircle(x,y,r,stkW,stkC,stkF) {
x = scaleX(x);  y = scaleY(y);  r = scaleX(r);
var attributes = {
"cx":x,
"cy":y,
"r":r,
"stroke":stkC,
"stroke-width":stkW,
"fill":stkF
};
var circle = svgElement("circle",attributes);
return circle;
}
function init() {
var sx1 = sy1 = sx2 = sy2 = 0;
var world = document.getElementById('world');
sx1 = 0;   sy1 = 10;  sx2 = 0;   sy2 = 110;
world.appendChild(scaleLine(sx1,sy1,sx2,sy2,'3','#ff0000'));

sx1 = 20;   sy1 = 20;  sx2 = 120;   sy2 = 50;
world.appendChild(scaleLine(sx1,sy1,sx2,sy2,'3','#00ff00'));

sx1 = 30;   sy1 = 30;  sx2 = 140;   sy2 = 100;
world.appendChild(scaleLine(sx1,sy1,sx2,sy2,'3','#0000ff'));

sx1 = 40;   sy1 = 40;  sx2 = 150;   sy2 = 150;
world.appendChild(scaleLine(sx1,sy1,sx2,sy2,'3','#006600'));

sx1 = 75;   sy1 = 25;  sx2 = 200;   sy2 = 25;
world.appendChild(scaleLine(sx1,sy1,sx2,sy2,'3','#006600'));

sx1 = 175;   sy1 = 125;  sx2 = 200;   sy2 = 225;
world.appendChild(scaleLine(sx1,sy1,sx2,sy2,'3','#000000'));

sx1 = 100;   sy1 = 150;  sx2 = 40;
world.appendChild(scaleCircle(sx1,sy1,sx2,'#000000','3','#ff0000'));

}```
You can also scale SVG images by using the viewbox attribute, so for example if you want to scale an SVG image that is 300 x 250 up to 1000 x 800 you can add a viewbox attribute to do so by setting the viewbox to the original size and then setting the height and width attributes to the size you want it to appear

HTML Code:
```<svg id="actual" xmlns="http://www.w3.org/2000/svg"
width="1000" height="800" style="border:1px solid black"
viewBox="0 0 300 250">
<line x1="0"  y1="10" x2="0"   y2="110" style="stroke-width:6; stroke:#ff0000;"/>
<line x1="20" y1="20" x2="120" y2="50" style="stroke-width:6; stroke:#00ff00;"/>
<line x1="30" y1="30" x2="140" y2="100" style="stroke-width:6; stroke:#0000ff;"/>
<line x1="40" y1="40" x2="150" y2="150" style="stroke-width:6; stroke:#006600;"/>
<line x1="75" y1="25" x2="200" y2="25" style="stroke-width:6; stroke:#006600;"/>
<line x1="175" y1="125" x2="200" y2="225" style="stroke-width:6; stroke:#000000;"/>
<circle cx="100" cy="150" r="40" stroke="black" stroke-width="2" fill="red"/>
</svg>```
Last edited by iBeZi; 05-12-2013 at 11:44 AM.

7. Originally Posted by iBeZi
It might be a bit neater if you create SVG elements and append them to the world element instead of creating a string and updating the innerHTML, you could create a function that automates the creation of an SVG element like so

HTML Code:
`...`
You can also scale SVG images by using the viewbox attribute, so for example if you want to scale an SVG image that is 300 x 250 up to 1000 x 800 you can add a viewbox attribute to do so by setting the viewbox to the original size and then setting the height and width attributes to the size you want it to appear

HTML Code:
`...`
Thank you, both versions work well.

Version 1 is a little closer to what I was trying to accomplish,
but I can see some other applications for the second as well.

I was not aware of the 'viewbox' command.

My ultimate goal is to be able to create dynamic optical ray diagrams
from user input for the display of simple and complex optical systems such as,
thin lens, telescopes, compound microscopes and simple optical principles like,
Snell's law, prismatic refraction and critical angles.

I want the user to be able to input values for the formulas to see the actions that occur.
Actual input values can be large and the resulting graphics would need to be scaled down
to be able to be displayed on the screen.

You have given me some possibilities that I shall investigate further. Thanks!

I'll post back if I come up with some acceptable designs for the displays.