SVG Essentials/Coordinates

From WikiContent

(Difference between revisions)
Jump to: navigation, search
(Initial conversion from Docbook)
m (1 revision(s))

Revision as of 23:40, 6 March 2008

SVG Essentials

The world of SVG is an infinite canvas. In this chapter, we'll discuss how you tell a viewer program which part of this canvas you're interested in, what its dimensions are, and how you locate points within that area.

Contents

The Viewport

The area of the canvas that your document intends to use is called the viewport. You establish the size of this viewport with the width and height attributes on the <svg> element. The values of these attributes can be simply a number, which is presumed to be in pixels; this is said to be specified in user coordinates. You may also specify width and height as a number followed by a unit identifier, which can be one of the following:

em
The font size of the default font, usually equivalent to the height of a character
ex
The height of the letter x
px
Pixels
pt
Points (1/72 of an inch)
pc
Picas (1/6 of an inch)
cm
Centimeters
mm
Millimeters
in
Inches

Examples:

<svg width="200" height="150">, <svg width="200px" height="200px">
Both of these specify an area 200 pixels wide and 150 pixels tall.
<svg width="2cm" height="3cm">
Specifies an area two centimeters wide and three centimeters high.
<svg width="2cm" height="36pt">
It is possible, though unusual, to mix units; this element specifies an area two centimeters wide and thirty-six points high.

If you have one <svg> element nested within another <svg> element, the nested tag may also specify its width and height as a percentage, measured in terms of the enclosing element. We will see nested <svg> elements in Section 2.5.

Using Default User Coordinates

When you do not use unit specifiers on your <svg> element, the viewer sets up a coordinate system where the horizontal, or x-coordinate, increases as you go to the right, and the vertical, or y-coordinate, increases as you move vertically downward. The upper left corner of the viewport is defined to have an x- and y-coordinate of zero.[1] This point, written as (0, 0) is also called the origin. The coordinate system is a pure geometric system; points have neither width nor height, and the grid lines are considered infinitely thin. We'll return to this subject in Chapter 3.

Example 2-1 establishes a viewport two hundred pixels wide and two hundred pixels high, then draws a rectangle whose upper left corner is at coordinate (10, 10) with a width of 50 pixels and a height of 30 pixels.[2] Figure 2-1 shows the result, with rulers and a grid to show the coordinate system.

Example 2-1. Using default coordinates

<svg width="200" height="200">
    <rect x="10" y="10" width="50" height="30"
        style="stroke: black; fill: none;"/>
</svg>

Figure 2-1. Rectangle using default coordinates

Rectangle using default coordinates

Even if you don't specify units in the viewport, you may still use them in some SVG shape elements, as we do in Example 2-2. Figure 2-2 shows the result, with rulers and a grid to show the coordinate system.

Example 2-2. Explicit use of units

<svg width="200" height="200">
    <rect x="10mm" y="10mm" width="15mm" height="10mm"
        style="stroke:black; fill:none;"/>
</svg>

Specifying units in the <svg> element does not affect coordinates given without units in other elements. Example 2-3 shows a viewport set up in millimeters, but the rectangle is still drawn at pixel (user) coordinates, as you see in Figure 2-3.

Example 2-3. Units on the svg element

<svg width="70mm" height="70mm">
    <rect x="10" y="10" width="50" height="30"
       style="fill: none; stroke: black;"/>
</svg>

Figure 2-2. Rectangle using explicit units

Rectangle using explicit units

Figure 2-3. Viewport with units; rectangle without units

Viewport with units; rectangle without units

Specifying User Coordinates for a Viewport

In the examples so far, numbers without units have been considered to be pixels. Sometimes this is not what you want. For example, you might want to set up a system where each user coordinate represents one-sixteenth of a centimeter. (We're using this coordinate system to prove a point, not to show a paragon of good design.) In this system, a square that is 40 units by 40 units will display as 2.5 centimeters on a side.

To accomplish this effect, you set the viewBox attribute on the <svg> element. The value of this attribute consists of four numbers that represent the minimum x-coordinate, minimum y-coordinate, width, and height of the user coordinate system that you want to superimpose on the viewport.

So, to set up the sixteen-units-per-centimeter coordinate system for a four centimeter by five centimeter drawing, you'd use this starting tag:

<svg width="4cm" height="5cm" viewBox="0 0 64 80">

Example 2-4 lists the SVG for a picture of a house, displayed using the new coordinate system.

Example 2-4. Using a viewBox

<svg width="4cm" height="5cm" viewBox="0 0 64 80">
   <rect x="10" y="35" width="40" height="40"
      style="stroke: black; fill: none;"/>
   <!-- roof -->
   <polyline points="10 35, 30 7.68, 50 35" 
      style="stroke:black; fill: none;"/>
   <!-- door -->
   <polyline points="30 75, 30 55, 40 55, 40 75"
      style="stroke:black; fill: none;"/>
   </svg>

Figure 2-4 shows the result. The grid and darker numbers show the new user coordinate system; the lighter numbers are positioned at one-centimeter intervals.

Figure 2-4. New user coordinates

New user coordinates

The numbers you specify for the value of the viewBox attribute may be separated by commas or whitespace. If either the width or height is zero, none of your graphic will display. It is an error to specify a negative value for the viewBox width or height.

Preserving Aspect Ratio

In the previous example, the aspect ratio, or ratio of width to height, of the viewport and the viewBox were identical (4/5 = 64/80). What happens, though, if the aspect ratio of the viewport and the viewBox are not the same, as in this example, where viewBox has an aspect ratio of one to one, but the viewport has an aspect ratio of one to three?

<svg width="45px" height="135px" viewBox="0 0 90 90">

There are three things that SVG can do in this situation:

  • Scale the graphic uniformly according to the smaller dimension so the graphic will fit entirely into the viewport. In the example, the picture would become half its original width and height. We'll show you examples of this in Section 2.4.2.
  • Scale the graphic uniformly according to the larger dimension and cut off the parts that lie outside the viewport. In the example, the picture would become one and a half times its original width and height. We'll show you examples in Section 2.4.3.
  • Stretch and squash the drawing so that it fits precisely into the new viewport. (That is, don't preserve the aspect ratio at all.) We will cover this in Section 2.4.4.

In the first case, since the image will be smaller than the viewport in one dimension, you must specify where to position it. In the example, the picture will be scaled uniformly to a width and height of 45 pixels. The width of the reduced graphic fits the width of the viewport perfectly, but you must now decide whether the image meets (is aligned with) the top, middle, or bottom of the 135-pixel viewport height.

In the second case, since the image will be larger than the viewport in one dimension, you must specify which area is to be sliced away. In the example, the picture will be scaled uniformly to a width and height of 135 pixels. Now the height of the graphic fits the viewport perfectly, but you must decide whether to slice off the right side, left side, or both edges of the picture to fit within the 45-pixel viewport width.

Specifying Alignment for preserveAspectRatio

The preserveAspectRatio attribute lets you specify the alignment of the scaled image with respect to the viewport, and whether you want it to meet the edges or be sliced off. The model for this attribute is:

preserveAspectRatio="alignment [meet | slice]"

where alignment specifies the axis and location, and is one of xMinYMin, xMinYMid, xMinYMax, xMidYMin, xMidYMid, xMidYMax, xMaxYMin, xMaxYMid, or xMaxYMax. This alignment specifier is formed by concatenating an x-alignment and a y-alignment as shown in Table 2-1. The default value for preserveAspectRatio is xMidYMid meet.

Note

The y-alignment begins with a capital letter, since the x- and y-alignments are concatenated into a single word.

Table 2-1. Values for alignment portion of preserveAspectRatio

X alignment
Value Action
xMin Align minimum x of viewBox with left corner of viewport.
xMid Align midpoint x value of viewBox with midpoint x value of viewport.
xMax Align maximum x value of viewBox with right corner of viewport.
Y alignment
Value Action
YMin Align minimum y of viewBox with top edge of viewport.
YMid Align midpoint y value of viewBox with midpoint y value of viewport.
YMax Align maximum y value of viewBox with bottom edge of viewport.


So, if you want to have the picture with a viewBox="0 0 90 90" fit entirely within a viewport that is 45 pixels wide and 135 pixels high, aligned at the top of the viewport, you would write:

<svg width="45px" height="135px" viewBox="0 0 90 90"
   preserveAspectRatio="xMinYMin meet">

Tip

In this case, since the width fits precisely, the x-alignment is irrelevant; you could equally well use xMidYmin or xMaxYMin. However, in the interests of consistency, it's usually best to make both specifiers the same when only one axis is affected.

This is all fairly abstract; here are some concrete examples that show you how the combinations of alignment and meet and slice interact with one another.

Using the meet Specifier

The starting <svg> tags in Example 2-5 all use the meet specifier.

Example 2-5. Use of meet specifier

<!-- tall viewports -->
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 90 90"
    width="45" height="135">

<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 90 90"
    width="45" height="135">

<svg preserveAspectRatio="xMaxYMax meet" viewBox="0 0 90 90"
    width="45" height="135">
        
<!-- wide viewports -->
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 90 90"
    width="135" height="45">

<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 90 90"
    width="135" height="45">

<svg preserveAspectRatio="xMaxYMax meet" viewBox="0 0 90 90"
    width="135" height="45">

Figure 2-5 shows where the reduced image fits into the enclosing viewBox.

Figure 2-5. meet -- viewBox fits in viewport

meet -- viewBox fits in viewport

Using the slice Specifier

Figure 2-6 shows the use of the slice specifier to eliminate parts of the picture that do not fit in the viewport. They were created with the <svg> tags in Example 2-6.

Example 2-6. Use of slice specifier

<!-- tall viewports -->
<svg preserveAspectRatio="xMinYMin slice" viewBox="0 0 90 90"
    width="45" height="135">

<svg preserveAspectRatio="xMidYMid slice" viewBox="0 0 90 90"
    width="45" height="135">

<svg preserveAspectRatio="xMaxYMax slice" viewBox="0 0 90 90"
    width="45" height="135">

<!-- wide viewports -->
<svg preserveAspectRatio="xMinYMin slice" viewBox="0 0 90 90"
    width="135" height="45">

<svg preserveAspectRatio="xMidYMid slice" viewBox="0 0 90 90"
    width="135" height="45">

<svg preserveAspectRatio="xMaxYMax slice" viewBox="0 0 90 90"
    width="135" height="45">

Figure 2-6. slice -- graphic fills viewport

slice -- graphic fills viewport

Using the none Specifier

Finally, there is the third option for scaling a graphic when the viewBox and viewPort don't have the same aspect ratio. If you specify preserve- AspectRatio="none", then the graphic will be scaled non-uniformly so that its user coordinates fit the viewport. Figure 2-7 shows such a "fun-house mirror" effect produced with the <svg> tags in Example 2-7.

Example 2-7. Aspect ratio not preserved

<!-- tall viewport -->
<svg preserveAspectRatio="none" viewBox="0 0 90 90"
   width="45" height="135">

<!-- wide viewport -->
<svg preserveAspectRatio="none" viewBox="0 0 90 90"
  width="135" height="45">

Figure 2-7. Aspect ratio not preserved

Aspect ratio not preserved

Nested Systems of Coordinates

You can establish a new viewport and system of coordinates at any time by putting another <svg> element into your document. The effect is to create a "mini-canvas" upon which you can draw. We used this technique to create illustrations such as Figure 2-5. Rather than drawing the rectangles, then rescaling and positioning the cat inside each one (the brute force approach), we took these steps:

  • Draw the blue rectangles on the main canvas
  • For each rectangle, define a new <svg> element with the appropriate preserveAspectRatio attribute
  • Draw the cat into that new canvas (with <use>), and let SVG do the heavy lifting

Here's a simplified example that shows a circle on the main canvas, then inside a new canvas that's outlined by a blue rectangle that's also on the main canvas. Figure 2-8 is the result we wish to achieve.

Figure 2-8. Nested viewport example

Nested viewport example

First, generate the SVG for the main coordinate system and the circle. Note that we've established the user coordinates to coincide exactly with the viewport in this document.

<svg width="200px" height="200px" viewBox="0 0 200 200">
    <circle cx="25" cy="25" r="25" style="stroke: black; fill: none;"/>
</svg>

The result is in Figure 2-9.

Figure 2-9. Circle in main viewport

Circle in main viewport

Now, draw the boundary of the box showing where we want the new viewport to be:

<svg width="200px" height="200px" viewBox="0 0 200 200">
    <circle cx="25" cy="25" r="25" style="stroke: black; fill: none;"/>
    <rect x="100" y="5" width="30" height="80"
       style="stroke: blue; fill: none;"/>
</svg>

This produces Figure 2-10.

Figure 2-10. Circle and boundary box in main viewport

Circle and boundary box in main viewport

Now, add another <svg> element for the new viewport. In addition to specifying the viewBox, width, height, and preserveAspectRatio specification, you may also specify the x and y attributes — in terms of the enclosing <svg> element — where the new viewport is to be established. (If you don't give values for x and y, they are presumed to be zero.)

<svgwidth="200px" height="200px" viewBox="0 0 200 200">
    <circle cx="25" cy="25" r="25" style="stroke: black; fill: none;"/>
    <rect x="100" y="5" width="30" height="80" 
        style="stroke: blue; fill: none;"/>
        
    <svg x="100px" y="5px" width="30px" height="80px" viewBox="0 0 50 50"
        preserveAspectRatio="xMaxYMax meet"> 
    </svg>
</svg>

Setting up the new coordinates with this nested <svg> element doesn't change the visual display, but it does permit you to add the circle in that new system, producing the result shown in Figure 2-8.

<svg width="200px" height="200px" viewBox="0 0 200 200">
    <circle cx="25" cy="25" r="25" style="stroke: black; fill: none;"/>
    <rect x="100" y="5" width="30" height="80" style="stroke: blue; 
        fill: none;"/>
        
    <svg x="100px" y="5px" width="30px" height="80px" viewBox="0 0 50 50"
        preserveAspectRatio="xMaxYMax meet">
        <circle cx="25" cy="25" r="25" style="stroke: black; 
            fill: none;"/>
    </svg>
</svg>

Notes

  1. In this book, we will specify a pair of x- and y-coordinates in parentheses, with the x-coordinate first. Thus, (10, 30) represents an x-coordinate of 10 and a y-coordinate of 30.
  2. To save space, we are leaving out the <?xml ...?> and <!DOCTYPE ...> lines. These are set in stone, so you can take them for granite.
Personal tools