Sick of fuzzy images?
In 2001 business graphics, maps, charts, CAD drawings usually did't look very good on the web. Again and again
we came across pages where we had to squint our eyes and ask ourself "What's that".
The reason was that these drawings were converted to GIF or JPeG files. Drawing packages are vector based and GIF or JPeG are not, so a lot of sharpness, contrast, and detail is lost during conversion. GIF and JPeG are great for photos and other half tone images, but for vector drawings they are not.
SVG was just in the making at that time and we needed a quick solution. That's why we came up with the CGM viewer applet (cgmVA). Most drawing packages
could export CGM (Computer Graphic Metafile, an ISO standard). The applet was written in Java, is quite small (53k), and should run on virtually any platform.
By now, cgmVA has been downloaded many times. Especially in the engineering area
where CGM files are quite common this applet is very popular. The following
images demonstrate the difference between bitmapped images and the vector graphics of
cgmVA.
| GIF - 10k |
JPeG medium quality - 9k |
CGM zipped -11k |
 |
 |
|
|
Try to zoom in
|
Try to zoom in
|
Click on image to zoom in
|
While in this example the CGM file is the largest of the three, CGM files don't get larger if you decide to display the image in a larger size. You can display a drawing full screen, without download time penalties.
Our cgmVA is interactive - users can zoom into an image and can pan within the applet window. For practical reasons we have limited the zoom factor to 16:1. The mouse controls are explained in the browsers status line (move the mouse over the picture). cgmVA can also be controlled via a toolbar using Javascript. For an example please see here.
But that is only where the fun starts: cgmVA supports animation. Instead of inventing an own animation script language, we decided to offer an animation interface instead and to use JavaScript to control the animation. This allows very sophisticated animations mixed with other multimedia elements. For an example, see below.
Platforms
cgmVA runs on any Java-enabled browser that supports at least Java SDK
1.1.8.
Animation requires a JavaScript/ECMAScript enabled browser. Popular browsers
such as Microsoft Internet Explorer or Firefox should have no trouble executing
this applet. However, you may want to make sure that both Java and JavaScript
are enabled.
top
In the meantime, SVG (Scalable Vector Graphics) was introduced as the W3C
vector graphics standard for the web. However, SVG needs a rather large browser
plug-in which is only available for certain platforms, and existing CGM drawings
would need to be converted to SVG first.
WebCGM is a standard maintained by OASIS and aims to bring CGM to web pages.
However, WebCGM requires a browser plug-in. Several manufacturers provide such
plug-ins but most of these plug-ins support only a limited browser base.
In addition to cgmVA, we provide cgmPanel, a Java class
library for CGM support in Java applications. This class library
implements a CGM enabled subclass of JPanel under Java Swing and Java 1.3 or
later. It supports all features of the cgmVA (plus some more) including
animation. A demo version can be downloaded from here.
Also available is cgmSwt, an Eclipse plug-in
for CGM support. This includes a SWT based library supporting all the features of the cgmVA (plus some more)
including animation, and an CGM viewer plug-in for the Eclipse platform. cgmSwt
requires Java 1.4, GDI+ on Windows (Windows XP) or the Cairo Vector Engine under
Linux GTK. A demo version can be downloaded from here.
top
CGM functionality
cgmVA can read binary and ASCII CGM files, but not ISO character encoded CGM files.
cgmVA supports most of the functionality of CGM Version 1 with a few exceptions:
- advanced text attributes like Character Orientation and Character Expansion are only supported for Hershey fonts.
- only hollow, empty, solid, horizontal and vertical hatching is supported for Interior Style.
- Generalized Drawing Primitives are not supported (GDPs are mostly used for proprietary extensions).
In addition cgmVA supports Bezier polylines from the CGM 3 standard.
top
The following HTML code displays the image my.cgm in a 400x400 area.
We start by specifying the applet itself. We assume that the applet classes are stored in a subdirectory called cgmva/.
<applet code="CgmViewApplet.class" codebase="cgmva/" width=400
height=400>
Alternatively you can use a ZIP file containing all classes. (Our package contains a ZIP-file that has been "jaxed" in addition for further compression. If you unzip it you will note that the class names are obfuscated.)
<applet code="CgmViewApplet.class" archive="cgmva.zip"
codebase="cgmva/" width=400 height=400>
Next we define which CGM files to display:
<param name="filename" value="cgmva/cgms/my.cgm">
You can specify several filenames separated by commas. All these images will be displayed in Layer 0 (see
below) on top of each other. For instance:
<param name="filename" value="cgmva/cgms/back.cgm,cgmva/cgms/front.cgm">
In this case it might be better to specify the directory for the images separately:
<param name="imagebase" value="cgmva/cgms/">
<param name="filename" value="back.cgm,front.cgm">
For security reasons, Java applets can only access files within the codebase directory. Images must be stored in the same directory as the applet classes or in a subdirectory.
Note: The file extension ".cgm" can be omitted - it is the default file extension for CGM images.
Finally, we close the applet definition:
</applet>
top
Using zipped archives
It is possible to specify keep the image files in a zipped archive with an optional parameter cgmArchive. cgmVA will load the whole archive and will satisfy requests for individual image files from this archive. This can result in dramatic download speed improvements, especially with animations, where many images are used.
Note: These ZIP-archives should not contain path names.
Example:
<param name="imagebase" value="cgmva/cgms/">
<param name="filename" value="scene">
<param name="cgmArchive" value="hello.zip">
The file scene.cgm will be retrieved from archive hello.zip.
(If not found there it will be retrieved from its own URL.)
Note: The file extension ".zip" can be omitted - it is the default file extension for ZIP archives.
top
Pixel based images
GIF-images can be displayed along with CGM images. GIF-images are especially useful if you want to use a photograph as background of a drawing:
<param name="filename" value="cgmva/cgms/back.gif,cgmva/cgms/front.cgm">
Note: Pixel based images files cannot be placed into a zipped archive, but are loaded individually.
top
Fonts
Normally, cgmVA uses native fonts for text output. In addition,
it is possible to use Hershey fonts. To use your own Hershey fonts, simply put them into the zipped cgmArchive. Alternatively, you may specify a font list to load font files individually:
<param name="fontbase" value="cgmva/fonts/">
<param name="hersheyFonts" value="Times,GillSans,GillSans_BoldItalic">
Note: The default extension for font file names is ".jhf".
cgmVA uses these Hershey fonts if their file names match with the specified font names in the drawing. A typical font name consists of the font family name, like "GillSans", and an optional style modifier: "_BoldItalic", "_Bold", "_Italic".
To find out which fonts are used in a drawing, open the drawing with a text editor and look for font names.
Hershey fonts are available from http://www.nyx.net/~jbuzbee/font.html
| Note: Advanced text features like text rotation (character orientation) or character
expansion are not available with native fonts but only with Hershey fonts.
cgmVA switches automatically to the the Hershey font Futura light
when rotated text is to be displayed in a native font. You must place the
file futural.jhf into the fontbase and
declare it like shown above, when
your drawing contains rotated text. |
top
Background colour
The background colour can be specified with the parameter
<param name="bgcolor" value="#rrggbb">
The colour encoding is the same as in HTML (#ffffff for white, #000000 for black).
If this parameter is not specified, the background colour is determined by
the first image. If this image does not set
the background colour, cgmVA defaults to the applet background colour, which is grey.
Note, that many CGM images establish an own canvas in form of a filled
rectangle. This will, of course, override the background setting.
top
Inverse colours
The parameter
<param name="inverse" value="true">
makes all CGM-colours to appear inverse.
top
cgmVA can notify a JavaScript function about Animation and Navigation events. You can specify
the name of a JavaScript function as an element handler with
<param name="eventHandler" value="myHandler">
The default value is "appletEvent". To enable event notification you must also specify a mayscript attribute in the applet clause:
<applet code="CgmViewApplet.class" ... mayscript ...>
top
Fail safe
What do we do with older browsers, that don't support Java? In this case we use a GIF version of the drawing to fall back on. Here is the way how it is done. Just include the GIF image between the <applet> and </applet> tags:
<applet code="CgmViewApplet.class" codebase="cgmva/" width=400 height=400>
<IMG SRC="Images/my.GIF" width=400 height=400>
</applet>
top
Running under the Java2 plug-in
cgmVA can run in a Java2 environment under the Java2 plug-in. We do not recommend this because:
- loading is much slower
- The HTML code to embed an applet is platform specific. The old APPLET tag is replaced by OBJECT for IE4/5 and by EMBED for Navigator. Pages that must run on all platforms get rather complex. If you still want to do it, use JavaSoft's java-plug-in-html-converter to convert APPLET tags into EMBEDs or OBJECTs.
- NO scripting! Back to basics!
However, the plug-in may be your only choice for browsers that don't support Java1.1x such as Opera.
top
Mouse controls
Zooming and scrolling is achieved by using the mouse. Mouse controls can be used , too, when an animation is running.
By default, mouse controls are enabled and a help text is displayed in the browsers status bar as the user moves the mouse across the applet area. This help text moves with the mouse movement to be fully readable in short status lines. You can avoid this movement by specifying a text that starts with a period.
The default text is:
Click/drag=Zoom in, AltClick=Zoom out, AltDrag=Pan, AltDoubleClick=Reset, ShiftClick=Centre
You can supply your own message with the parameter controls:
<param name="controls"
value="What else would you want?">
If you supply an empty or blank string, the mouse controls are switched off:
<param name="controls" value="">
The zoom factor can be set with:
<param name="zoomFactor"
value="1.2">
The default value is 1.414...
top
The parameter
<param name="keyActions" value="true">
enables the keyboard for scrolling and panning. This is to allow mouseless
operation. The following keys are available:
| + |
zoom in |
| - |
zoom out |
| Enter |
fire hyperlink |
| Spacebar |
reset |
| ! |
find next hotspot |
| Cursor keys |
panning |
top
Imagemaps
Using image maps with cgmVA is easy. Just specify another parameter
<param name="imagemap" value="mapname,url1,url2,...">
mapname specifies another CGM file relative to imagebase. This CGM file contains a drawing that determines the hot areas in the drawing. Please note, that cgmVA can only recognize filled elements as hot areas. If you move the mouse over such an area, cgmVA displays the attached URL in the status line. When you click, the URL well be fetched.
If no URL is attached to an image map element, cgmVA displays the number of the element in the status line. This helps to find out element numbers during development and to attach the correct URLs.
Optional, an hyperlink can be highlighted when the mouse enters the hotspot
region. Specify
<param name="showHotspots" value="true">
When
<param name="keyActions" value="true">
is specified, the "!" key can be used to jump from hotspot to
hotspot.
Image maps override other mouse controls.
An example is seen in the toolbar demonstration.
top
You can define a define a separate JavaScript event handler for each instance of cgmVA (see above). You must then supply a JavaScript function that will be invoked for each key and mouse action:
function appletEvent(task,device,event) {
....
}
This function is supplied with the following parameter values:
task
| "Navigation" |
A navigation event (scroll, zoom) |
| "Animation" |
An animation event (mouse animation event or key event) |
device
| "Mouse" |
A mouse event |
| "Key" |
A key event |
event
| "Down" |
mouse button or key was pressed |
| "Up" |
key was released |
| "Click" |
mouse click (button released) |
| "DoubleClick" |
mouse double click |
| "Drag" |
during dragging (button still pressed) |
| "Drop" |
Dragging finished (button released) |
| "Move" |
mouse was moved |
An example is seen in the toolbar demonstration.
top
Animation
The applet version string can be obtained with
string=getVersion()
Layers, Images, Panes, Components
cgmVA organises each scene in up to 16 layers (0-15). Each layer can contain an unlimited number of images. Each image is represented by a separate file and can contain many components organised in several panes.
Layers with higher numbers are in front of layers with lower numbers. The same is true for panes and components.
It is possible to control each image separately or to apply controls to whole layers. A limited set of controls is available for panes and components within images. By default new layers are visible and are located at (0,0) with a magnification of (1,1). When the applet is invoked, the main image (specified in the "filename" parameter) is located in layer 0 at position 0,0 with normal size.
top
Coordinates
The base image also defines the coordinates for all subsequent operations. (0,0) is the left upper corner of the image, and the longest edge of the image has the length of 1000.
Each image is automatically scaled to fit into a 1000x1000 square when loaded. Translating operations are used to position and size the image as required. The aspect of images is maintained but can be changed by translating operations.
The working area for all animation operations is the square (0,0) (1000,1000). cgmVA finally maps this area to the actual applet area.
top
Visibility
There are four visibility levels that can be applied to single layers or single pictures:
- show
The image is displayed with all lines and fills, find operations succeed on fills.
show(l,name) Loads the image "name" into layer "l" if not already loaded. The image is set to "visible".
You can also specify a comma separated name list.
show(l) Sets layer "l" to "visible", all visible or wire images in this layer are displayed.
show(l,name,c) sets component "c" in image "name" in layer "l" to visible.
- wire
The image is displayed without fills, but find operations succeed on the invisible fills.
wire(l,name) Loads the image "name" into layer "l" if not already loaded. The image is set to "wire".
You can also specify a comma separated name list.
wire(l) Sets layer "l" to "wire", all visible or wire images in this layer are displayed in transparent fashion.
wire(l,name,c) sets component "c" in image "name" in layer "l" to "wire".
- map
The image is not displayed, but find operations succeed on the invisible fills. Used for image maps.
map(l,name) Loads the image "name" into layer "l" if not already loaded. The image is set to "map".
You can also specify a comma separated name list.
map(l) Sets layer "l" to "map", all images in this layer are not displayed.
map(l,name,c) sets component "c" in image "name" in layer "l" to "map".
- hidden
The image is not displayed. Find operations fail.
hide(l,name) Loads the image "name" into layer "l" if not already loaded. The image is set to "invisible".
You can also specify a comma separated name list.
hide(l) Sets layer "l" to "invisible", all images in this layer are hidden.
hide(l,name,c) sets component "c" in image "name" in layer "l" to "invisible".
For example, hide(l,name,0) can be used to hide the first component of a
picture which usually contains the background rectangle.
- clip(x,y,w,h)
- Sets a clipping rectangle at position (x,y) with with w and height h for the whole scene.
Can be used for blending effects.
- clip(l,x,y,w,h)
- Sets a clipping rectangle at position (x,y) with with w and height h for layer "l".
Can be used for blending effects.
-
replaceText(l,name,n,text)
- Replaces the text in the n-th text element of image "name" in layer "l" with the specified text string. This operation changes all clones, too. Counting begins at 1.
Good for reusing images where only the text changes.
top
Streaming
preload(names)
- Specify a comma separated name list. The list can contain zipped archives (.zip), sounds (.au), Hershey fonts (.jhf), and cgm files (.cgm). The default file extension is .cgm. The files are preloaded for later use. The use of this command is optional, but can result in a smoother animation.
- unload(names)
- Specify a comma separated name list. The list can contain sounds (.au), Hershey fonts (.jhf), and cgm files (.cgm). The default file extension is .cgm. The files are removed from memory. This command is only necessary for very long animations with changing scenes when memory space becomes an issue.
(Sound files can be removed while playing - the sound file will be removed after it has stopped playing).
- top
Translating Operations
Layer translations are relative to the base image area. Image translations are relative to the containing layer.
- translate(x,y)
- translate the whole scene by a (x,y) distance. This has only effect on scenes magnified by a scale operation (see below). With translate x- and y- values are move-to values, not move-by values.
translate(l,x,y)
- translate layer "l" by a (x,y) distance. Layer numbering starts at 0.
- translate(l,name,x,y)
- translate picture "name" in layer "l" by a (x,y) distance relative to layer origin.
scale(mx,my)
- scale the whole scene horizontally by factor mx and vertically by factor my. Factors between 1 and 16 are allowed.
- scale(l,mx,my)
- scale layer "l" horizontally by factor mx and vertically by factor my. Factors between 0 and 16 are allowed.
- scale(l,name,mx,my)
- scale picture "name" in layer "l" horizontally by factor mx and vertically by factor my. Factors between 0 and 16 are allowed.
- scroll(x,y)
- scrolls the visible window by a (x,y) distance. This is equivalent to a user scroll
operation.
-
- setViewX(x)
- sets the visible window to scene x-position.
- setView(y)
- sets the visible window to scene position y on the Y-axis.
- getViewX()
- returns the x-position of the visible window within the scene
- getViewY()
- returns the y-position of the visible window within the scene
- getViewWith()
- returns the width of the visible window in scene units
- getViewHeight()
- returns the height of the visible window in scene units
-
- zoom(m,x,y)
- set zoom magnification to"m" towards point (x,y).
This has the same effect as a user zoom action. It overrides user zoom actions and can be overridden by user zoom actions.
- zoom(m)
- set zoom magnification to"m" towards centre of window.
- getZoom()
- returns the current zoom magnification.
- setZoomFactor(f)
- sets the factor by which the zoom magnification is increased or decreased. The default value is sqrt(2).
- getZoomFactor()
- returns the zoom factor.
getLayerX(l)
getX(l)
(deprecated)
- get horizontal position of layer "l".
- getCgmX(l,name)
getX(l,name)
(deprecated)
- get horizontal position of picture "name" in layer "l" relative to layer origin.
getComponentX(l,name,comp)
getX(l,name,comp)
(deprecated)
- get horizontal position of component "comp" in picture "name" in layer "l" relative to layer origin.
getLayerY(l)
- getY(l)
(deprecated)
- get vertical position of layer "l".
- getCgmY(l,name)
getY(l,name)
(deprecated)
- get vertical position of picture "name" in layer "l" relative to layer origin.
- getComponentY(l,name,comp)
getY(l,name,comp)
(deprecated)
- get vertical position of component "comp" in picture "name" in layer "l" relative to layer origin.
- movePane(l,name,p,x,y)
- Moves pane "p" within image "name" in layer "l" by the distance specified in "x" and "y". Pane numbering starts at 0.
- moveComponent(l,name,c,x,y)
- Moves component "c" within image "name" in layer "l" by the distance specified in "x" and "y". Component numbering starts at 0.
top
Cloning
To use several instances of the same image append an identification to the name. Identification must start with a "#" character. For instance,
show(3,dolly.cgm#2)
shows a clone of dolly.cgm in layer 3.
Clones can be translated, scaled, shown and hidden individually. However, text replacements affect all clones simultaneously.
top
Rendering
- render()
- This will redraw the whole scene.
Show, hide, and display do not have any effect on the display until render()
is called.
- top
User interaction
- setEventMask(modifiers)
By default, all mouse events with the CTRL-key pressed are regarded as animation mouse events.
You can change this with setEventMask. (Values 1-4 can be combined).
| 0 |
pass all mouse events to the animation interface.
(disables user zoom). |
| 1 |
Shift-modifier |
| 2 |
CTRL-modifier (default) |
| 4 |
Meta-modifier or right mouse button |
modifier=getModifier()
- returns the modifier value from the last mouse event.
| 1 |
Shift |
| 2 |
CTRL |
| 4 |
Meta or right mouse button |
| 8 |
Alt or middle mouse button |
-
- setModifier(modifier,mode)
- sets the modifier keys for the next key or mouse click or drag event.
modifier
| 0 |
No modifier |
| 1 |
Shift |
| 2 |
CTRL |
| 4 |
Meta or right mouse button |
| 5 |
Meta+Shift. This enables drag but inhibits zoom. The cursor changes to a hand. |
| 8 |
Alt or middle mouse button |
| 16 |
Double Click |
mode
| 0 |
Switch off |
| 1 |
Only for next mouseclick/keypress |
| 2 |
until switched off |
-
- event = mouseControl(statusline)
- Shows the specified text in the status line and returns a mouse event.
If no new mouse event happened during the last invocation of mouseControl an empty string is returned.
Otherwise:
| "Down" |
mouse button was pressed |
| "Click" |
mouse click (button released) |
| "DoubleClick" |
mouse double click |
| "Drag" |
during dragging (button still pressed) |
| "Drop" |
Dragging finished (button released) |
| "Move" |
mouse was moved |
event = mouseControl()
- Same as above, but status line remains untouched.
- event = mouseControl("")
- Same as above, but status line is reset to the state before animation was started.
getSceneX()
-
getX()
(dropped)
- returns horizontal mouse position for recent mouse control.
- getSceneY()
getY()
(dropped)
- returns vertical mouse position for recent mouse control.
- getKey()
- returns the key code of the most recent key pressed. If the key is still down, the value is negative, if the key is up again, the value is positive.
Note: Key events are not buffered.
- getKeyModifier()
- returns the modifier for the last key (key down) event. Modifier values same as above.
- top
Finding objects
cgmVA can find the foremost object in a scene for a specified (x,y) position and returns filename and clone id of the found picture. The algorithm used is:
- fail on hidden objects or layers (returns empty string)
- CGM-pictures: succeed on foremost object that has a fill at the search position.
The visibility status of the objects must be "show", "wire" or "map".
- GIF-images: succeed on all non transparent pixels of the image.
name=findPicture(x,y)
find the picture at position x,y.
- Removed: Use findPicture(-1,x,y).
- name=findPicture(l,x,y)
- find the picture at position x,y in layer "l". Specify -1 for the layer
number to search in all layers
name=findPicture(event)
- same as above, but the position is derived from the event:
| "Down", "Click", "DoubleClick" |
Position when mouse button was pressed |
| "Drag" or "Drop" |
Current mouse position during drag operation |
| "Move" |
Mouse position during mouse move |
-
- name=findPicture(l,event)
- as above, but restricts the search to layer "l".
- name=findText(t)
- find the specified string t in a text element. A matching text element is marked. Supply the empty string to find the next occurence.
- name=findText(l,t)
- as above, but restricts the search to layer "l".
- number=getComponent()
- returns the index of the image component that caused a previous "find" operation to succeed.
-
- number=getLayer()
- returns the layer index that caused a previous "find" operation to succeed.
- top
Sound
Although you can use the browsers sound facilities from JavaScript, we provide methods to play sound (.au) files.
The advantage is that this is truly platform independent and does not require plug-ins.
- playSound(filename)
- plays the specified sound file. The default file extension is .au.
- stopSound(filename)
- stops the playing of the specified sound file.
- loopSound(filename)
- play the specified sound file continuously.
Optionally you can use the sound-parameter in the applet definition to preload sound files:
<param name="soundbase" value="cgmva/sounds/">
<param name="sound" value="sound1,sound2">
top
Invocation from JavaScript
Note:
Invoking Java-Methods from JavaScript requires Netscape Navigator with LiveConnect/LiveWire or MicroSoft InterExplorer 4.0.
First embed the applet into HTML. For instance:
<applet code="CgmViewApplet.class" codebase="cgmva/" name="cgm1" width=400 height=400>
<param name="imagebase" value="cgmva/cgms/">
<param name="filename" value="my">
</applet>
Note the name definition cgm1. This name is used in the following JavaScript function to address the specific applet instance.
Now we define a JavaScript function in the HTML header section:
<html><head><title>Test</title>
<script language="JavaScript">
function test(i) {
// we run 100 times
if (i < 100) {
// each time make layer 0 a bit smaller
document.cgm1.scale(0,(100-i)/100,(100-i)/100);
// done, now display the result
document.cgm1.render();
// increment counter
i++;
// now post a timer event with "test(i+1)" to be executed
timerOne=window.setTimeout("test(" + i + ")",100);
}
}
</script>
</head>
top
Finally, you have to invoke the JavaScript function somewhere. Here we define a button and an event handler:
<form name="Input">
<input type=button value="Test" onclick="test(0)">
</form>
Note:
JavaScript functions can be contained in separate files, so they can be used from different HTML pages.
top
Hello, world
"Hello, world" illustrates several animation techniques:
- use mouse controls to zoom into the scene
(left click to zoom in, drag to scroll, right click to zoom out)
- start the animation by pressing the Start-button below
When the animation is running you can
- move the mouse over the dinosaur. This will make the dinosaur issue a
statement.
press the Ctrl-key and
- drag the dinosaur across the scene.
- click on the planet. Surprise!
Applet Definition:
<APPLET code="CgmViewApplet.class" archive="cgmVA.zip" codebase="cgmva/" name="hi"
width=450 height=450 MAYSCRIPT>
<PARAM name="imagebase" value="cgmva/cgms/">
<PARAM name="cgmArchive" value="hello.zip">
<PARAM name="filename" value="Scene">
<PARAM name="soundbase" value="cgmva/sounds/">
<PARAM name="sound" value="hi">
</APPLET>
<FORM name="Input">
<INPUT type=button value="Start" onclick="hello(0)">
<INPUT type=button value="Stop" onclick="cancel_hello()"> <BR>
<BR>
</FORM>
The JavaScript definitions for hello(0) and
cancel_hello() are in the HTML
header.
Please see the HTML code of this page.
top
History and Status
Acknowledgements
This project was initiated by Alexander Larsson (alla@lysator.liu.se), who wrote V0.1.
Text objects, mouse control, animation, ZIP- and GIF-support were added by Berthold Daum (berthold.daum@online.de).
The Hershey-class was contributed by James P. Buzbee (jbuzbee@nyx.net).
Dirk Bosmans (dirk.bosmans@tijd.com) simplified the fail safe clause.
Some algorithms were imported from
Core Web Programming from Prentice Hall Publishers, 1997 Marty Hall, hall@apl.jhu.edu.
top
Download
If you wish to be notified about new versions of cgmVA please leave your name and email address here:
Download latest
version of cgmVA
top
To include CGM functionality into your own
Java applications we provide the packages CgmCanvas
and CgmSWT. See here.
top
|