Clipping graphical objects in a canvas widget

Arjen Markus (7 july 2003) The canvas widget does not support clipping, that is confining the objects that are to be drawn to, say, some rectangular area. This is useful when drawing xy-graphs for instance whose axes are not scaled with the extremes of the data - lines and dots would appear outside the rectangle formed by the axes.

I realised that clipping is not as easy as it looks, given the canvas's drawing method: it keeps a list of objects to be drawn and the order in which to draw them. If that order changes, then this might affect the appearance of clipped objects, unless each object carries information about the clipping area at the time of it's creation.

Clearly, this is a complicated matter, so it would take considerable effort to implement. Instead, why not cater specifically for the most common situation in which clipping is needed, the graph example described above?


CME (7 july 2003) : For your information, Zinc a canvas supporting openGL also provides you with clipping of group of items by one item. The latter can be a multi-contour curve with bezier segments... clipping is available even if you do not have openGL.

AM (8 july 2003) Interesting, I know of Zinc, but I have never used it. I must remember it - OpenGL is troublesome to me as it requires additional support in an X Window environment.

CME (8 july 2003) : Yes if you want Zinc to use openGL, you need an openGL lib and a driver for an openGL card on you PC (it is known to work well with nvidia cards and drivers and also with some ati). However Zinc also works without openGL; for example multicontour curves, clipping and transforms work fine on X11.


The following script shows one simple implementation of a clipping window:

  • Draw what needs to be drawn and clipped
  • Install the clipping window
  • Draw what ever needs not be clipped

Subtleties not taken care of: the exact borders of the clipping rectangle - should they be clipped or not? Perhaps only a matter of definition.


 # clipping.tcl --
 #    Add a "clip window" to a canvas
 #

 # clipwindow --
 #    Add a rectangular clip window to a canvas
 #
 # Arguments:
 #    cnv       Canvas widget
 #    xmin      Minimum x value (in pixels!)
 #    ymin      Minimum y value (in pixels!)
 #    xmax      Maximum x value (in pixels!)
 #    ymax      Maximum y value (in pixels!)
 # Result:
 #    ID of the created polygon
 # Side effects:
 #    An opaque polygon is added to the canvas that has a
 #    hole in it. The polygon has the same colour as the
 #    canvas's background and the tag "CLIP".
 #
 proc clipwindow { cnv xmin ymin xmax ymax } {
 
    set width  [$cnv cget -width]
    set height [$cnv cget -height]
    set colour [$cnv cget -background]

    # Take care of border cases
    set xmin [expr {$xmin>0? $xmin : 0}]
    set ymin [expr {$ymin>0? $ymin : 0}]
    set xmax [expr {$xmax<$width?  $xmax : $width}]
    set ymax [expr {$ymax<$height? $ymax : $height}]

    set id [$cnv create polygon \
       0 0 \
       $width 0 $width $height 0 $height \
       0 0 \
       $xmin $ymin $xmin $ymax $xmax $ymax $xmax $ymin $xmin $ymin \
       0 0 \
       -tags CLIP -fill $colour -outline $colour]

    return $id
 } 

 #
 # Test code
 #
 canvas .c -width 200 -height 200
 pack   .c -fill both
 .c create oval  50  50 150 150 -fill green -outline black -width 3

 clipwindow .c 100 100 190 190

 .c create oval  50  50 150 150 -outline black -width 1