Anatomy of VML
The idea behind VML is that many images that exist today as
bitmaps on web pages would be better represented via vector drawing.
For example, any image that contains text - if the text content has
to be changed the whole image has to be recreated from scratch. With
VML, the designer only has to retype the text. VML is also scalable
with no additional byte cost. A square shape of 10px by 10px claims
the same transmission kB in the web page as any other size
implementation of it, 1000px by 1000px, say. The third and most
critical advantage over raster images is that the VML-shape contents
are accessible via client script. The shapes can be added, erased
and modified programmatically, enabling a live and interactive user
interface.
VML is not appropriate everywhere, especially not where "arty"
images are required - as a rule of thumb, where a GIF format would
be preferable to a JPEG then VML would probably be even better. An
example would be a graph, a cartoon, or a geometric shape like the
one shown below:
The code used for producing the shape is this
(_vml_first.html): <HTML xmlns:v="urn:schemas-microsoft-com:vml">
<HEAD>
<STYLE>
v\:* { behavior: url(#default#VML);}
</STYLE>
<TITLE>VML Sample</TITLE>
</HEAD>
<BODY>
<table>
<tr><td>
<v:shape id="test" fillcolor="yellow"
style="position:relative;top:1;left:1;width:700px;height:700px;rotation:0;"
coordsize="1000,1000"
path = "m 200,200 l 400,300, 400,500, 200,600, 1,500, 1,300 x
m 200,250 l 350,320, 350,480, 200,550, 50,480, 50,320 x e">
</v:shape>
</td></tr>
</table>
</BODY>
</HTML>
We will see an explanation of how the shape is drawn with the
next shape - firstly, there are three important areas to look
at:
- The namespace declaration
- The definition of the behavior for the v: tag prefix
- The creation of the shape itself
The namespace and behavior declarations are the minimum required
for a shape to be created. Although Microsoft's documentation
specifies an additional namespace of Microsoft Office Extensions
(tag prefix o:) it is not required for
web applications - it is used by applications like PowerPoint and
Visio.
One of the main objectives of the VML Group of the W3C (composed
of Microsoft, Visio, Autodesk, Hewlett Packard and Macromedia) was
that the processing model of VML should be the same as HTML. This is
so that the wheel does not have to be reinvented for functionality
that exists in the HTML or CSS infrastructure. Like HTML, VML
describes objects, which will often be further edited. In the case
of HTML, these objects are paragraphs, forms or tables. In the case
of VML, the objects are shapes.
Backward and forward compatibility is also easily achieved by
using the namespace declaration so that older browsers can ignore
the unknown tags. Let's look at the most generic tag, <v:shape> a bit more closely by
analyzing its most important attributes:
Copyright © 1998 W3C All Rights Reserved. http://www.w3.org/Consortium/Legal/?WROXEMPTOKEN=55322ZWhkXjJQlY4zPJFFnpWa1
Status:submission
Namespace |
Attribute |
Description |
VML |
id |
A unique ID that identifies the
shape so that scripts can reference it. |
VML |
type |
A reference to a shapetype id that describes the
standard path, fill and stroke properties of a shape. If any
of these properties are re-defined in the shape they will
override the shapetype
properties. |
VML |
adj |
A list of numbers separated by
commas, that are the parameters passed onto the guide formulas
that define the path of the shape. There can be up to 8 adjust
parameters. |
VML |
path |
A string containing the commands
that define the path. We will examine the path definition in
detail later. |
CSS |
visibility |
If hidden the shape is not rendered and
does not generate mouse events. |
CSS |
top, margin-top, center-y, etc |
The position of the top of the
containing block of the shape in CSS units. If the element
belongs to a group, then the units are the coordmap units of
the parent element. We will look at using parent-child
coordinates later. |
CSS |
left, margin-left, center-x, etc |
The position of the left of the
containing block of the shape, as above |
CSS |
width |
The width of the container rectangle
of the shape. In CSS units or, for elements in a group, in the
coordmap units of the parent element. |
CSS |
height |
The height of the containing block
of the shape in CSS units or, for elements in a group, in the
coordmap units of parent element. |
CSS |
z-index |
The z-index of the shape. Positive
numbers are in front of the screen. Negative numbers are
behind the screen. |
CSS |
rotation |
The angle to rotate the reference
rectangle. Positive angles are clockwise. |
CSS |
flip |
Takes values "x" or "y" or both.
Indicates that the shape image inside the reference rectangle
should be flipped as appropriate along the listed axes in the
order specified. i.e. flip: x means flip about the y-axis so
that x becomes -x. |
CSS |
position |
May be any CSS value when this is a
top-level element. When it is contained inside a group, it
must always be absolute. |
VML |
opacity |
The opacity of the entire shape. A
fraction between 0 (completely transparent) and 1 (completely
opaque.) |
VML |
chromakey |
A color value that will be
transparent and show anything behind the shape. |
VML |
fill |
If true, the path defining the shape will
be filled. By default, it will be filled using a solid color
unless there is a <fill>
sub-element that specifies more complex fill properties. If
false, the fill is
transparent. |
VML |
fillcolor |
The primary color of the brush to
use to fill the path of this shape. |
VML |
v |
A string containing the commands
that define the path - see also the W3C documentation on the
use of formulas to define the path. |
VML |
print |
If true, this shape can be printed. |
VML |
coordsize |
The number of units that the width
and height of this shape is divided in. This defines the x and
y of the coordinate space inside the containing block of this
shape. If it is not specified, it is the same as the width and
height of the rectangle. |
VML |
coordorigin |
The coordinates at the top-left
corner of the containing block. |
VML |
wrapcoords |
In the form "x1,y1,x2,y2,x3,y3..."
(same as coords in an AREA). Describes in drawing units around
a shape. Used for the wrapping of text around an object.
|
Note that because VML supports CSS2 style properties there is
more than one way of defining certain attributes. For example, rotation can be specified within the style
attribute (as we have done in our example above) or via a separate
rotation attribute - both have the
same effect of course.
The most critical attribute is path: path = "m 200,200 l 400,300, 400,500, 200,600, 1,500, 1,300 x
m 200,250 l 350,320, 350,480, 200,550, 50,480, 50,320 x e">
The numbers inside the path string refer to a coordinate system
of our choosing. They can use various units of measurements, e.g.
pixels (px), millimetres (mm), points (pt), but we can create a
local unitless coordinate system as follows: coordsize="1000,1000"
This allows us to design our shape and resize it freely by
changing the width and height elements of the style attribute.
Otherwise, if we wanted to resize the shape we would have to change
every single coordinate in the path! Make sure that the ratio of
width to coordsize(x) is the same as
height to coordsize(y), otherwise the
shape will appear distorted.
To understand how the path element draws vectors we need to look
at the commands used within it - a full list can be found in the W3C
specification, but here are the ones we will be using in this
article:
Copyright © 1998 W3C All Rights Reserved. http://www.w3.org/Consortium/Legal/?WROXEMPTOKEN=55322ZWhkXjJQlY4zPJFFnpWa1
Status:submission
Command |
Parameters |
Description |
m |
2 |
Start a new sub-path at the given
(x,y) coordinate |
l |
2* |
Draw a line from the current point
to the given (x,y) coordinate which becomes the new current
point. A number of coordinate pairs may be specified to form a
polyline. |
c |
6* |
Draw a cubic bézier curve from the current point to
the coordinate given by the final two parameters, the control
points given by the first four parameters. |
x |
0 |
Close the current sub-path by
drawing a straight line from the current point to the original
moveto point. |
e |
0 |
End the current set of sub-paths. A
given set of sub-paths (as delimited by end) is filled using
eofill. Subsequent sets of
sub-paths are filled independently and superimposed on
existing ones. |
wa |
8* |
Draw arc in a clockwise direction
using the first 4 values (left,
top, right, bottom) to define the bounding box of
an ellipse, and the last four (start(x,y) end(x,y)) as the
definitions of two radial vectors. A line is drawn from the
current point to the start of the arc. |
wr |
8* |
As above, but with an implied move
(command m) to the first point
(i.e. no line is drawn to the starting point). |
qx |
2* |
Draw a quarter ellipse from the
current point to the given end point. The elliptical segment
is initially tangential to a line parallel to the x-axis.
(i.e. the segment starts out horizontal) |
qy |
2* |
Same as above except that the
elliptical segment is initially tangential to a line parallel
to the y-axis. (i.e. the segment starts out
vertical) |
* means any number of consecutive parameter groups.
You can make really complicated shapes with these commands:
This was created with less than 1Kb of code
(_vml_ionic.html), and of course with lots of patience: coordsize="500,250"
path = "m 100,125 qy 75, 150 qx 50, 110 qy 110, 75 qx 150, 150 qy 75, 200
l 0, 200 l 0, 250 l 500, 250 l 500, 200 l 75, 200
qx 0, 100 qy 105, 25
l 0, 25 l 0, 0 l 500, 0 l 500, 25 l 105, 25
c 170, 40, 200, 70, 250, 90 c 300, 70, 330, 40, 395, 25
qx 500, 100 qy 425, 200 qx 350, 150 qy 390, 75 qx 450, 110 qy 425, 150 qx 400,
125 l 399, 125 qy 425, 151 qx 451, 110 qy 390, 74 qx 339, 135 qy 250, 200
qx 161, 135 qy 110, 74 qx 49, 110 qy 75, 151
qx 101, 125
x e">
whereas the image you see above is more than 5Kb.
The path string above can be translated in English as
follows:
- m 100, 125 = move to coordinate
point x=100, y=125 without writing anything
- qy 75, 150 = draw an elliptic
quadrant that starts from the current point (100,125), moves
initially in the y direction (downwards or upwards, not
horizontally), and finish at the point x=75, y=150
- qx 50, 110 = draw an elliptic
quadrant again, initially moving horizontally, ending at the
specified point
A few more instructions further down we see:
- l 0, 200 = draw a straight line
from current point to x=0, y=200
- c 170, 40, 200, 70, 250, 90 =
draw a cubic bézier curve to point x=250, y=90, as modified by
points x=170, y=40 and x=200, y=70.
The rest of the instructions are easy to follow.
As mentioned earlier, the W3C specification provides a
comprehensive example and a lot of information on using VML.
However, it contains no information on one very important Internet
Explorer implementation detail: the <v:extrusion> subelement. We will be
looking at how we can use that to produce a useful,
network-efficient, portable 3D visualizing tool.
Three Dimensional VML
Let's go back to our modest hexagon shape. We can create a simple
JavaScript interface (_vml_simple.html) for changing shape
and extrusion attributes, hopefully making it easier to visualize
the effect of each change. Behind each table cell in the screenshot
you see below, there is a script function that sets a shape
attribute - for example, hovering over the 3D
type toggle cell we can change
the value of the type attribute from perspective to parallel and
vice versa. The lower / higher cells introduce a timer that calls
themselves repeatedly, as long as the mouse is kept hovering over
them. We won't analyze the JavaScript functions here, they were
simply written to automate the changing of the attributes.
As you can see, with 3D we have some new attributes - a table
follows later which describes them in detail. Before that, here is
an image of how our hexagon looks in 3D (after modifying some
attribute values via the lower / higher cells) and then an explanation of how
the <v:extrusion> sub element
was used to achieve that:
<v:shape ..... >
...
<v:extrusion id="testchild" on="True" BackDepth="50" lockrotationcenter="true"
type="parallel" color="yellow" skewangle="0" Orientation="0.1, 0.2, 0.5"
Orientationangle="-90" skewamt="0%"
Viewpoint="-125000emu, 1250000emu" Viewpointorigin="0.3, 0.1" />
...
</v:shape>
Not bad functionality for 4 lines of code. As you can see the
extrusion element is contained within the shape tags. The id attribute enables it to
be referred in script - the rest of the attributes are slightly more
complex:
Table content copyright © Microsoft Corp.
Attribute (grouped by function) |
Description |
Editing |
On |
Determines whether an extrusion will
be displayed. |
Ext |
Defines the default extrusion
behavior for graphical editors. |
Design |
|
Type |
Defines the way that the shape is
extruded, perspective or parallel. |
BackDepth |
Defines the amount of backward
extrusion. |
ForeDepth |
Defines the amount of forward
extrusion. |
Plane |
Specifies the plane that is at right
angles to the extrusion, xy (default), yz, xz. |
SkewAmt |
Defines the amount of skew of an
extrusion. |
SkewAngle |
Defines the angle of skew of an
extrusion. |
Edge |
Defines the apparent bevel of the
extrusion edges. |
Facet |
Defines the number of facets used to
describe curved surfaces of an extrusion. |
Viewing |
AutoRotationCenter |
Determines whether the center of
rotation will be the geometric center of the extrusion. |
LockRotationCenter |
Determines whether the rotation of
the extruded object is specified by the RotationAngle attribute. Default False |
RotationCenter |
Specifies the center of rotation for
a shape. |
RotationAngle |
Specifies the rotation of the object
about the x- and y-axes. |
Orientation |
Specifies the vector around which a
shape will be rotated (i.e. a more flexible RotationCenter). |
OrientationAngle |
Defines the angle that an extrusion
rotates around the orientation. |
Viewpoint |
Defines the viewpoint of the
observer. |
ViewpointOrigin |
Defines the origin of the viewpoint
within the bounding box of the shape. |
Appearance |
Color |
Defines the color of the extrusion
faces. |
ColorMode |
Determines the mode of extrusion
color, auto, custom (i.e. value of the Color attribute). |
Brightness |
Specifies the amount of brightness
of a scene. |
Diffusity |
Defines the amount of diffusion of
reflected light from an extruded shape. |
LightFace |
Determines whether the front face of
the extrusion will respond to changes in the lighting. |
LightHarsh |
Determines whether the primary light
source will be harsh. |
LightHarsh2 |
Determines whether the secondary
light source will be harsh. |
LightLevel |
Defines the intensity of the primary
light source for the scene. |
LightLevel2 |
Defines the intensity of the
secondary light source for the scene. |
LightPosition |
Specifies the position of the
primary light in a scene. |
LightPosition2 |
Specifies the position of the
secondary light in a scene. |
Metal |
Determines whether the surface of
the extruded shape will resemble metal. |
Render |
Defines the rendering mode of the
extrusion, solid, wireframe, boundingcube. |
Shininess |
Defines the concentration of
reflected light of an extrusion surface. |
Specularity |
Defines the specularity of an
extruded shape. |
There are definitely a lot of tags and attributes. Microsoft
provides a simple VML
tag generator that can do simple typing for you, but it is not a
graphical editor. The 3D section lists all the available attributes,
which you can enter by clicking:
In the bottom left corner you can also see the Shape Types drop down list - it creates
special cases of the <v:shape>
element, using different tags, for example oval, rect,
arc, roundrect etc. I personally use the editor
mainly to look up tag names and get some default values. You can get
it from http://msdn.microsoft.com/downloads/samples/internet/default.asp?url=/downloads/samples/internet/vml/vmlgenerator/default.asp&WROXEMPTOKEN=55322ZWhkXjJQlY4zPJFFnpWa1.
So we have a form of simple 3D visualizer - however, we can
combine a variety of the above attributes to create an
extended-functionality 3D environment.
Multiple Shapes
In the file _vml_ballroom.html
of the download material you can see the code with which the
two shapes below were drawn. With a bit of imagination you can see
male and female ballroom dancers:
Note that there is not yet an easy way that I know of to draw the
curves - it is mostly trial and error. The two path shapes are
enclosed together in a <v:group>
super element which enables us to treat them as one item.
Unfortunately, this only applies to rotation around the z-axis,
since the <v:group> element does
not have a RotationAngle attribute.
Instead we can rotate (or change any other attribute) our two shapes
separately, but in-sync. So, with the values shown in the screenshot
below we can have a flying dancing couple:
If you want to avoid overlapping the shape with the text you can
use standard CSS2 positioning techniques, for example in the style
attribute of the <v:group>
element: style="position:relative;top:1;left:200;"
The way to achieve the synchronized rotation around the y-axis is
by arranging the rotation center of the first shape as: rotationcenter="0.5,0.5"
and of the second shape as: rotationcenter="-0.5,0.5"
which means that the axis around which the two shapes rotate is
located at the same point (rightmost for the first shape, which is
also the leftmost for the second shape).
Another possibility is to display one of the extruded shapes on a
different plane, thereby creating any form of real-life object.
Taken further, this could mean the creation of an HTML-only version
of "virtual reality" with quite realistic results (see the below
image from _vml_athens.html). I put virtual reality in quotes
because when the shapes are rotated the z-index (the order of which
shape overlaps the others) is not automatically adjusted. It can
obviously be handled in code, but that would be specific for each
and every shape - hardly the virtual reality technology we need!
In addition, the technology does not appear to be completely
ready. The picture below shows the same object as above, slightly
rotated. As you can see, the rendering of the curved extrusion is
imperfect (to put it mildly). It looks as though the inner layer of
curved extrusion somehow prevents the outermost curve from
rendering.
A rotationangle value higher than
0 seems to correct the problem, but
obviously this means that curved extruded surfaces are not
completely ready for 3D display. The download material contains the
relevant code and you can perhaps find a suitable solution.
Conclusion
VML is definitely a powerful and flexible drawing language. The
imagination of the developer is the only limit for producing highly
interactive interfaces, product displays, 3D maps and graphs,
low-download-size pictures and so on.
Hopefully you will have found this article to have been a useful
introduction to this exciting technology. |