TEACH RC_CONSTRAINED_POINTER                      Aaron Sloman Aug 2000

Or TEACH RC_CONSTRAINED_DIAL

DRAFT (To be completed - STILL UNDER CONSTRUCTION)

LAST CHANGED: Tue Aug 29 18:40:10 BST 2000

LIB RC_CONSTRAINED_POINTER: includes

rc_constrained_pointer(
    x, y, orient, minang, maxang,
        len, width, colour, bg) -> dial;

A procedure to create a dial with moving pointer, providing two-way
interaction, analogous to sliders. This library provides the main
procedures used in LIB RC_DIAL, described in TEACH RC_DIAL.


LIB RC_LABEL_DIAL:

rc_label_dial(
    xcentre, ycentre, radius,
        start, inc, lim, num, incnum, colour, font);

A procedure to create numeric labels for such a dial.

LIB RC_DRAW_ARC_SEGMENTS:

rc_draw_arc_segments(
    xcentre, ycentre, radius, start, gap, lim, inc, width, col);

A procedure to create "tick" marks with numeric labels for such a dial.
Draw sequence of segments of angular width inc, and linewidth width,
at angular locations specified by start, gap, lim


CONTENTS

 -- Introduction
 -- Examples using rc_constrained_pointer
 -- LIB RC_CONSTRAINED_POINTER: General overview
 -- The main procedure rc_constrained_pointer
 -- Further examples, with additional procedures
 -- Example p1
 -- -- Associating a variable with the current state of the dial
 -- -- Altering the mapping between pointer angle and stored value
 -- Example p2
 -- Example p3
 -- Example p4
 -- Example p5
 -- Example p6
 -- Examples p7 and after
 -- -- Adding numeric labels and "tick" marks on a dial
 -- Methods and procedures provided

-- Introduction -------------------------------------------------------

See the tutorial examples in TEACH RC_DIAL

The remainder of this file shows some of the gory details that lie
behind rc_dial

-- Examples using rc_constrained_pointer ------------------------------

The procedure that creates the basic picture object, draws the
background and the moving pointer, without handling the range of values
or step function is rc_constrained_pointer, defined below. We give here
a range of examples of what it can do, to illustrate the versatility,
then define it formally later.


uses rclib
uses rc_window_object
uses rc_constrained_pointer

;;; Some utilities for use in examples below.
vars win1;

;;; Here's a procedure to start a new window when needed, and assign
;;; it to win1.

define new_win1();
    if rc_islive_window_object(win1) then
        rc_kill_window_object(win1);
    endif;
    ;;; create a window object, with default picture coordinates
    rc_new_window_object(
        "right", "top", 480, 480, {240 240 1 -1}, 'win1') -> win1;
enddefine;

;;; create a new window
new_win1();

;;; create a dial in the centre of the window, with orientation
;;; horizontal, angles ranging from 0 to 180, with background blue and
;;; pointer coloured yellow, pointer length 100, width 20,

vars dial1 =
    rc_constrained_pointer(0, 0, 0, 0, 180, 100, 20,
        'blue', 'yellow');

;;; Try moving the blue pointer with the mouse. Note how the pointer
;;; value of the dial changes as it is moved.
rc_pointer_value(dial1) =>

;;; It can also be moved by program

45 -> rc_pointer_value(dial1);
90 -> rc_pointer_value(dial1);
135 -> rc_pointer_value(dial1);

;;; contrast:
rc_set_axis(dial1, 90, true);
rc_set_axis(dial1, 45, true);
rc_set_axis(dial1, 130, true);

WARNING: the angle used by the method rc_set_axis, defined in
LIB rc_linepic increased in the counter-clockwise direction
when rc_yscale is negative (y increases up the screen) whereas
the pointer value decreases when that happens.

The pointer is an instance of rc_rotatable, which is explained in
HELP rc_linepic. So the method rc_axis is applicable to it, to determine
its "real" orientation, which changes as you move the pointer with the
mouse.

rc_axis(dial1) =>
rc_pointer_value(dial1)=>

;;; NOTE that the pointer is also an instance of rc_constrained_mover,
;;; and the pointer value cannot be set beyond the limits of 0 and
;;; 180, either by moving it with the mouse or by means of program
;;; commands.

-45 -> rc_pointer_value(dial1);
rc_pointer_value(dial1) =>

200 -> rc_pointer_value(dial1);
rc_pointer_value(dial1) =>

rc_set_axis(dial1, 330, true);
rc_pointer_value(dial1) =>


Besides the orientation used by rc_axis and rc_set_axis, there is also a
virtual orientation, relative to the direction specified as angle 0,
which in this case is pointing to the left. Compare the values after
moving the pointer to various positions.

rc_axis(dial1), rc_virtual_axis(dial1)=>

As one goes down the other goes up.

Later we show how "converter" procedures can be used to map the virtual
angle onto some arbitrary range of numbers, or symbols, by assigning
appropriate procedures to the slots rc_pointer_convert_in and
rc_pointer_convert_out. That is done automatically by the procedure
rc_dial, on the basis of the range argument, as
illustrated above with examples d1 to d4.

These dials are also instances of the rc_informant mixin described in
HELP RCLIB. So various utilities can be associated with them, including
reactor methods.


Here are two more examples, a pair of back to back dials, e.g. with
a range of 135, tilted through -45 and - 90 degrees respectively, using
the default colours (dark grey and paler grey).

;;; create a new window
new_win1();

vars
    dial2 =
      rc_constrained_pointer(
        -10, -100, -45, 0, 135, 80, 16, false, false),

    dial3 =
      rc_constrained_pointer(
        10, -100, 90, 0, 135, 80, 16, false, false);

;;; try moving them and noting how their values change:

rc_pointer_value(dial2), rc_pointer_value(dial3) =>

We can give them both the value 0 at the top and a value between 0 and
10 depending on how far they deviate from the top position, by setting
up approriate converter procedures.

define convert_ang_to_val(ang, pos) -> val;
    ;;; convert the range 0 to 135 or the range 135 to 0 into the range
    ;;; 0 to 10, depending whether pos is true or false.

    ang/13.5 -> val;
    unless pos then 10 - val -> val endunless;

enddefine;

define convert_val_to_ang(val, pos) -> ang;
    ;;; convert in the opposite direction

    val*13.5 -> ang;

    unless pos then 135 - ang -> ang endunless;

enddefine;

;;; Use closures of these to create converters for dial2
;;; (this is done automatically by the rc_dial procedure)
convert_ang_to_val(%false%) -> rc_pointer_convert_in(dial2);
convert_val_to_ang(%false%) -> rc_pointer_convert_out(dial2);

;;; check the effect by moving the pointer and testing the value
rc_pointer_value(dial2) =>

;;; compare the real and virtual axes:
rc_axis(dial2), rc_virtual_axis(dial2) =>

;;; and see the effect of changing the value by program

0 -> rc_pointer_value(dial2);
vars x;
for x from 0 by 1 to 10 do
    x -> rc_pointer_value(dial2);
    syssleep(20);
endfor;

Exercise 1:
;;; Now set up the converters for dial 3, and test them.

Exercise 2:
;;; Change the converters so that they produce integer results only.

;;; get rid of the window for now.
rc_kill_window_object(win1);

-- LIB RC_CONSTRAINED_POINTER: General overview -----------------------

The library introduces a class which inherits from several other
classes/mixins

    define :class rc_constrained_pointer;
        is rc_constrained_rotater rc_opaque_rotatable
            rc_selectable, rc_informant;

It is possible to use the usual objectclass mechanisms to create
instances, produce subclasses, define new methods, etc. This file
introduces the simplest procedural interface supporting the most
commonly required facilities.

It uses the procedure rc_constrained_pointer, described, with examples
below.

-- The main procedure rc_constrained_pointer --------------------------

rc_constrained_pointer(
    x, y, orient, minang, maxang, len, width, colour, bg) -> pointer;

This creates a dial with movable pointer, where the pointer is
represented as a movable coloured sector of circle pointing from the
centre of the dial to a point on its circumference.

The interpretation of three parameters orient, minang, maxang is quite
complex, so the explanations that follow should be compared with the
examples above, and the further examples given below.

The parameters for rc_constrained_pointer are as follows:

    x, y
        coordinates of location on rc_current_window_object, in
        rc_graphic coordinates. (See HELP rc_graphic). This location
        is taken as the centre of the circular segment used for the
        dial. If the dial occupies a semi-circle (i.e. minang and maxang
        differ by 180) the pointer rotates around a point a little way
        away from this location, in order to maintain the semi-circular
        appearance. Otherwise it rotates around the x,y location.

    orient
        a number representing an angle in degrees whose value modulo 180
        represents the orientation of the dial. This can be thought of
        as the orientation of the direction of the dial where the
        pointer has angle 0. In the case of a "normal" semi-circular
        dial this is the orientation of the straight edge of the
        semi-circle, and is 0 when the curved part is on top if
        rc_yscale is negative (the default case) or when the curved part
        is below, if rc_yscale is positive. (rc_xscale is always assumed
        to be positive.)

        With rc_xscale positive and rc_yscale negative you can make a
        semi-circular dial with the curve on the right and the zero
        point at the top, by setting orient to 90, or with the curve on
        the left by setting it to -90, in which case the zero point
        will be at the bottom.

        Making orient 180 will create an "upside down" dial. (However,
        the precise shape of the dial will depend on minang and maxang.)

    minang, maxang,
        Constraints on allowed values of the pointer angle, relative
        to the orientation which is 0, as defined above. If rc_yscale
        is negative (y increases upwards), then positive values of
        minang and maxang involve clockwise rotations relative to the
        zero point, otherwise counter clockwise.

        Note that if you give an orient of 0 and the values of minang
        and maxang are 0 and 180 then the dial's background will
        be displayed as a semi-circle with pointer value 0 at one
        end of the diameter and 180 at the other end(see example p1
        below). If the orient is 90, minang is -90 and maxang 90, then
        the appearance of the dial is the same, except that the top
        point (centre of the curve) corresponds to angle value 0, and
        the ends correspond to -90 and +90.

        If the minang or maxang is outside the semi-circle the dial's
        background will be extended to cover the range through which the
        pointer can move, as in example p2, below.

        If minang is -90 and and maxang is 270 a complete circle will be
        drawn. If minang and maxang are greater than 0 and less than 180
        then the dial will be smaller than a semi-circle, as in example
        p3 below.

    len, width,
        len is the lenth of the dial's pointer, which is represented as
        a narrow sector of circle. width is the width of the pointer at
        its base. The value of len will determine the radius used for
        the dial.

    colour,
        The colour of the pointer, a string, or false, meaning use the
        default.

    bg  Either a string representing the colour of the background,
        or false, or the word "background". If it is false or
        "background" the current background of the window will be used
        (NB not necessarily the colour previously drawn where the dial
        is.)

-- Further examples, with additional procedures -----------------------

;;; Set up libraries:

uses rclib
uses rc_window_object
uses rc_constrained_pointer

;;; create a window
vars win1 = rc_new_window_object( "right", 20, 450, 450, true, 'win1');

;;; If necessary the window can later be removed with this command:


rc_kill_window_object(win1);



-- Example p1 ---------------------------------------------------------
;;; Create a dial with red pointer on a yellow background, at location
;;; 120, 120, with horiziontal base, min and max angles set at 0 and
;;; 180, pointer length 60, pointer width 10.

vars p1 =
    rc_constrained_pointer(120, 120, 0, 0, 180, 60, 10, 'red', 'yellow');

;;; Try moving the dial with the mouse. To get a reaction press down
;;; button 1 with the mouse pointer near the red pointer, then drag it.
;;; After dragging to various locations check the value of the
;;; following:

rc_pointer_value(p1) =>

;;; Try updating the value of the dial using a program command and
;;; see what happens to the dial:

0 -> rc_pointer_value(p1);
20 -> rc_pointer_value(p1);
90 -> rc_pointer_value(p1);
180 -> rc_pointer_value(p1);
250 -> rc_pointer_value(p1);
290 -> rc_pointer_value(p1);
-45 -> rc_pointer_value(p1);
-135 -> rc_pointer_value(p1);

;;; The value is constrained to lie between 0 and 180 because the
;;; pointer is constrained to lie in the semi circle.

;;; You can rotate the pointer using a program command

;;; The following two procedures will be used later with other examples

define pointer_demo_1(p, minval, inc, maxval);
    lvars val;
    for val from minval by inc to maxval do
        val -> rc_pointer_value(p);
        syssleep(1);
    endfor;
enddefine;

pointer_demo_1(p1, 0, 2, 180);


;;; or using rc_turn_by to turn a small amount at a time

define pointer_demo_2(p, angmin, inc, num);
    angmin -> rc_pointer_value(p);
    repeat num times
        rc_turn_by(p, inc, true);
        syssleep(1);
    endrepeat;
enddefine;

pointer_demo_2(p1, 0, 5, 36);


-- -- Associating a variable with the current state of the dial

vars valp1;

"valp1" -> rc_informant_ident(p1);
rc_informant_value(p1) -> valp1;

;;; Try moving the dial with the mouse, or using program commands
;;; and check the value of the variable after each move

valp1 =>

-- -- Altering the mapping between pointer angle and stored value

;;; We can alter the mapping between the pointer angle and the stored
;;; value. E.g. suppose we want the stored value to be a number betwee
;;; 1 and 10, with 0 corresponding to the left hand end and 10 pointing
;;; right.

define procedure ang_to_val(ang) -> val;
    ;;; convert the range 0 to 180 into the range 0 to 10

    ang/18.0 -> val;
    ;;; round to 1 decimal point

    round(val*10)/10.0 -> val;

enddefine;

define procedure val_to_ang(val) -> ang;
    ;;; convert in the opposite direction

   val*18 -> ang

enddefine;

;;; set these up as converters for p1
ang_to_val -> rc_pointer_convert_in(p1);
val_to_ang -> rc_pointer_convert_out(p1);

;;; Now set the pointer and check the appearance
;;; This should make the angle 0 degrees (pointing right)
10 -> rc_pointer_value(p1);

rc_pointer_value(p1) =>

valp1 =>

;;; This should make the angle 90 degrees, pointing up
5 -> rc_pointer_value(p1);

valp1 =>

;;; Check these out
2.5 -> rc_pointer_value(p1);

;;; Three quarters over
7.5 -> rc_pointer_value(p1);

;;; But the maximum cannot be exceeded
12 -> rc_pointer_value(p1);

valp1 =>

14 -> rc_pointer_value(p1);

valp1 =>

;;; repeatedly move the pointer and print the value

vars x;
for x from 0 by 1 to 10 do
    x -> rc_pointer_value(p1);
    rc_pointer_value(p1) =>
    syssleep(30);
endfor;

;;; We can make the program monitor and print out the value every
;;; fifth of a second second while you move the pointer with the mouse

repeat 50 times
    valp1 =>
    syssleep(20);
endrepeat;

;;; Interrupt with CTRL-c when you have had enough.


-- Example p2 ---------------------------------------------------------

;;; This one still has orientation 0, but the min and max angles are -45
;;; and 225, producing a concave shape.
;;; The cursor cannot be made to stop in the gap.

;;; Its range is from an angle of -45 (relative to the left horizontal)
;;; to 225 (increasing from left to right)

;;; Giving false for colours gets default background and pointer colour
;;; light grey and dark grey.

vars p2 =
    rc_constrained_pointer(120, -120, 0, -45, 225,
        60, 10, false, false);

;;; Move the cursor round and check the value
rc_pointer_value(p2) =>
rc_informant_value(p2) =>

90 -> rc_pointer_value(p2);
rc_pointer_value(p2) =>
0 -> rc_pointer_value(p2);
-45 -> rc_pointer_value(p2);
rc_pointer_value(p2) =>
-75 -> rc_pointer_value(p2);
rc_pointer_value(p2) =>

pointer_demo_1(p2, -20, 2, 90);
pointer_demo_1(p2, -45, 4, 225);

;;; now use turn_by
pointer_demo_2(p2, -45, 3, 90);


Try the same converters as before, i.e. using 0-180 as 0 to 10
define procedure ang_to_val(ang) -> val;
    ;;; turn the range 0 to 180 into the range 0 to 10

    ang/18.0 -> val

enddefine;

define procedure val_to_ang(val) -> ang;
    ;;; convert in the opposite direction

   val*18.0 -> ang

enddefine;

;;; set these up as converters for p2
ang_to_val -> rc_pointer_convert_in(p2);
val_to_ang -> rc_pointer_convert_out(p2);

;;; check the effect
0 -> rc_pointer_value(p2);
;;; move through 180 degrees
10 -> rc_pointer_value(p2);

;;; move to 270 degrees position
15 -> rc_pointer_value(p2);

;;; move the pointer with the mouse and check the value
rc_pointer_value(p2) =>

Try using rc_informant_ident as above to associate a variable with p2
and see how its value changes as you move the pointer with the mouse
or the updater of rc_pointer_value

;;; A slightly different example, where the range goes from 0 to 270,
;;; over the same 2-D area
vars p2a =
    rc_constrained_pointer(
        120, -10, -45, 0, 270, 60, 10, false, false);

rc_pointer_value(p2a) =>
rc_informant_value(p2a) =>

10 -> rc_pointer_value(p2a);
90 -> rc_pointer_value(p2a);
0 -> rc_pointer_value(p2a);
;;; compare
0 -> rc_pointer_value(p2);

;;; move both pointers and check their values
rc_pointer_value(p2), rc_pointer_value(p2a) =>

-- Example p3 ---------------------------------------------------------

Try setting up converters for the following examples, or develop your
own versions and play with them.

;;; This one has min and max angles of 45 and 135:
;;; Only the required portion of the background is shown, i.e.
;;; less than a semi-circle

vars p3 =
    rc_constrained_pointer(
        -50, -180, 0, 45, 135, 70, 10, 'blue', 'red');

rc_pointer_value(p3) =>

pointer_demo_1(p3, 45, 1, 100);
pointer_demo_1(p3, 60, 2, 135);

pointer_demo_2(p3, 0, 3, 20);

;;; slightly different version, going from 0 to 90

vars p3a =
    rc_constrained_pointer(
        -120, -50, 45, 0, 90, 70, 10, 'blue', 'green');

rc_pointer_value(p3a) =>

pointer_demo_1(p3a, 0, 1, 90);
pointer_demo_1(p3, 45, 1, 80);

pointer_demo_2(p3, 0, 3, 20);


-- Example p4 ---------------------------------------------------------

A version forming a complete circle with the pointer going from
bottom (-90) to bottom (270), increasing clockwise:

vars p4 =
    rc_constrained_pointer(
        -100, 100, 0, -90, 270, 70, 10, 'blue', 'red');

;;; move the pointer and check how the value changes,
;;; especially as it moves past the bottom
rc_pointer_value(p4) =>

45-> rc_pointer_value(p4);
90-> rc_pointer_value(p4);
180-> rc_pointer_value(p4);
-90-> rc_pointer_value(p4);
225-> rc_pointer_value(p4);
270-> rc_pointer_value(p4);

;;; But it respects the upper and lower bounds
-150-> rc_pointer_value(p4);
390-> rc_pointer_value(p4);
rc_pointer_value(p4) =>

pointer_demo_1(p4, 270, -2, 0);

pointer_demo_2(p4, 45, 5, 30);

;;; Try creating converters for this making the range go from
;;; 0 to 100

-- Example p5 ---------------------------------------------------------
;;; Start a new window if necessary.

new_win1();

;;; This one is tilted left 45 degrees, but has the normal semi-circle,
;;; and range from 0 to 180, using default pointer and background
;;; colours (false, false)

vars p5 =
    rc_constrained_pointer(
        -100, 100, -45, 0, 180, 60, 10, false, false);

rc_pointer_value(p5) =>

rc_informant_value(p5) =>
45-> rc_pointer_value(p5);
90-> rc_pointer_value(p5);
180-> rc_pointer_value(p5);
200-> rc_pointer_value(p5);



-- Example p6 ---------------------------------------------------------
;;; Start a new window if necessary.

new_win1();

;;; min -45, max 225, tilted left 45
vars p6 =
    rc_constrained_pointer(
        -100, 0, -45, -45, 225, 75, 10,'yellow', 'blue');

rc_pointer_value(p6) =>

45-> rc_pointer_value(p6);
90-> rc_pointer_value(p6);
180-> rc_pointer_value(p6);
270-> rc_pointer_value(p6);

rc_pointer_value(p6) =>
rc_informant_value(p6) =>

-- Examples p7 and after ----------------------------------------------
;;; Start a new window if necessary.

new_win1();

;;; This is tilted left 90 degrees

vars p7 =
    rc_constrained_pointer(
        200, -120, -90, 0, 180, 80, 15, 'red', 'orange');

45-> rc_pointer_value(p7);
90-> rc_pointer_value(p7);
180-> rc_pointer_value(p7);
270-> rc_pointer_value(p7);
355-> rc_pointer_value(p7);
rc_pointer_value(p7)=>

new_win1();

;;; try to work out in advance what this will look like and what
;;; the range is

vars p8 =
    rc_constrained_pointer(
        120, 120, -90, -45, 225, 100, 15, 'red', 'blue');

rc_pointer_value(p8) =>
45-> rc_pointer_value(p8);
90-> rc_pointer_value(p8);
180-> rc_pointer_value(p8);
225-> rc_pointer_value(p8);
270-> rc_pointer_value(p8);
315-> rc_pointer_value(p8);

pointer_demo_1(p8, 0, 2, 180);

pointer_demo_2(p8, 45, 3, 50);

;;; new window if needed

new_win1();

;;; This has a pointer facing up which can swivel round through 270
;;; degrees from -45 (relative to the dial orientation of 135) to
;;; +225 relative to that orientation.)

vars p9 =
    rc_constrained_pointer(
        -100, -100, 135, -45, 225, 100, 15, 'red', 'blue');

;;; check values for various locations
rc_pointer_value(p9) =>

;;; The next one is equivalent to the previous one, but in a different
;;; location, and with -225 instead of 135 for the orientation.

vars p10 =
    rc_constrained_pointer(
        -100, 100, -225, -45, 225, 100, 15, 'red', 'blue');

rc_pointer_value(p10) =>
0-> rc_pointer_value(p9);
0-> rc_pointer_value(p10);

100-> rc_pointer_value(p9);
100-> rc_pointer_value(p10);

;;; A dial top left, tilted left 90, but with a range from 45 to 135,
;;; i.e. a quadrant:

vars p11 =
    rc_constrained_pointer(
        100, 100, -90, 45, 135, 80, 15, 'red', 'blue');

rc_pointer_value(p11) =>

0 -> rc_pointer_value(p11);
45 -> rc_pointer_value(p11);
60 -> rc_pointer_value(p11);
120 -> rc_pointer_value(p11);
180 -> rc_pointer_value(p11);

repeat 90 times
    rc_turn_by(p11, 1, true);
    syssleep(1);
endrepeat;

rc_set_axis(p11, 220, true);

;;; this one has value 0 in the middle of the quadrant
vars p12 =
    rc_constrained_pointer(
        150, -100, 0, -45, 45, 80, 15, 'red', 'blue');

rc_pointer_value(p12) =>

90 -> rc_pointer_value(p11);
90 -> rc_pointer_value(p12);
20 -> rc_pointer_value(p11);
20 -> rc_pointer_value(p12);


new_win1();

/*
-- -- Adding numeric labels and "tick" marks on a dial

This is done automatically in some cases. But it can also be
done by directly invoking the following procedures:
*/

;;; We can add "tick" marks around the dial edge, and some labels, using
;;; these two procedures, described later:

;;; rc_label_dial(
;;;     xcentre, ycentre, radius,
;;;         start, inc, lim, num, incnum, colour, font);
;;;
;;; rc_draw_arc_segments(
;;;         xcentre, ycentre, radius, start, gap, lim, inc, width, col);


vars p13 =
    rc_constrained_pointer(
        0, 0, 0, 0, 180, 80, 15, 'red', 'blue');


vars
    ;;; first find the centre of rocation of the pointer
    (dx1, dy1) = rc_real_pivot_coords(p13),

    ;;; and its length
    len = rc_pivot_length(p13),

    orient = (180 - rc_rotater_orient(p13) - rc_min_ang(p13)) mod 360,

    endorient = orient - rc_maxdiff(p13);

;;; We can then use dx1, dx2, len, and endorient as follows:

;;; make red marks at 18 degree intervals, of dimension 2 by 8 roughly
rc_draw_arc_segments(
    dx1, dy1, len+5, orient, -18, endorient, 2, 8, 'red');

;;; make longer black marks at intervals of 90 degrees
rc_draw_arc_segments(
    dx1, dy1, len+8, orient, -90, endorient, 2, 10, 'black');

;;; put some numeric labels indicating degrees
rc_label_dial(
    dx1, dy1, len+42, orient, -18, endorient, 0, 18, 'red', '6x13');

;;; and, if needed, some indicating the associated values,
;;; going from 0 to 10
rc_label_dial(
    dx1, dy1, len+20, orient, -18, endorient, 0, 1, 'blue', '6x13');

;;; Move the pointer and see what happens to this
rc_pointer_value(p13) =>

;;; or assign the value and see what happens
0 -> rc_pointer_value(p13);
36 -> rc_pointer_value(p13);
90 -> rc_pointer_value(p13);
144 -> rc_pointer_value(p13);


-- Methods and procedures provided ------------------------------------

From LIB rc_constrained_rotater

 define :mixin rc_constrained_rotater;
 define :method rc_setup_rotater(pic:rc_constrained_rotater);
 define :method rc_constrain_rotater(pic:rc_constrained_rotater, vang) -> newang;
 define :method rc_pivot_coords(pic:rc_constrained_rotater);
 define :method updaterof rc_pivot_coords(x, y, pic:rc_constrained_rotater);
 define :method rc_real_pivot_coords(pic:rc_constrained_rotater) -> (x,y);
 define :method updaterof rc_real_pivot_coords(x0, y0, pic:rc_constrained_rotater);
 define :method rc_end_coords(pic:rc_constrained_rotater);
 define :method updaterof rc_end_coords(/*x,y, */ pic:rc_constrained_rotater);
 define :method rc_real_end_coords(pic:rc_constrained_rotater) -> (x,y);
 define :method updaterof rc_real_end_coords(x0, y0, pic:rc_constrained_rotater);
 define :method rc_set_axis(pic:rc_constrained_rotater, ang, mode);
 define :method rc_turn_by(pic:rc_constrained_rotater, ang, draw);
 define :method rc_oriented_angle(pic:rc_constrained_rotater, ang) -> ang;
 define rc_actual_to_virtual_angle(pic, ang) -> vang;
 define :method rc_virtual_axis(pic:rc_constrained_rotater) -> vang;
 define :method updaterof rc_virtual_axis(vang, pic:rc_constrained_rotater);
 define :method rc_ang_to_point(pic:rc_constrained_rotater, x, y) -> ang;
 define :method rc_move_to(pic:rc_constrained_rotater, x, y, mode);
 define :method rc_move_by(pic:rc_constrained_rotater, dx, dy, draw);


From LIB rc_constrained_pointer

 define -- The class rc_constrained_pointer
 define :class rc_constrained_pointer;
 define -- methods for the class
 define :method print_instance(pic:rc_constrained_pointer);
 define :method rc_pointer_mouse_limit(x, y, picx, picy, pic:rc_constrained_pointer) -> boole;
 define lconstant decoration_parameters(pic) -> (x, y, len, startorient, endorient);
 define :method rc_draw_dial_marks(pic:rc_constrained_pointer, marks);
 define :method rc_draw_dial_labels(pic:rc_constrained_pointer, labels);
 define :method rc_print_dial_captions(pic:rc_constrained_pointer, captions);
 define :method rc_draw_pointer_frame(pic:rc_constrained_pointer);
 define :method rc_draw_constrained_pointer(pic:rc_constrained_pointer);
 define lconstant adjust_step_value(pic, val) -> val;
 define :method rc_set_axis(pic:rc_constrained_pointer, ang, mode);
 define :method rc_pointer_value(pic:rc_constrained_pointer) -> val;
 define :method updaterof rc_pointer_value(newval, pic:rc_constrained_pointer);
 define :method rc_pointer_reactor(s:rc_constrained_pointer, val);
 define -- spec abbreviations
 define expand_dial_spec_abbreviations(spec) -> spec;
 define -- Recognisers for optional arguments
 define iswident(w);
 define isspecvector(v);
 define isrctypespec(item);
 define ismarkspec(item);
 define islabelspec(item);
 define iscaptionspec(item);
 define -- creation of dials
 define rc_constrained_pointer_init(x, y, orient, minang, maxang, len, width, colour, bg) -> pointer;
 define rc_install_pointer(pointer, win);
 define rc_constrained_pointer(x, y, orient, minang, maxang, len, width, colour, bg) -> pointer;


LIB RC_DIAL
 define lconstant pointer_value_from_ang(vang, pic) -> val;
 define lconstant pointer_ang_from_value(val, pic) -> vang;
 define rc_dial(x, y, orient, angwidth, range, len, width, colour, bg) -> pointer;


Additional methods are in LIB rc_constrained_rotater and other libraries
from which this class inherits

TO BE CONTINUED

 define -- The most general dial creator
 define create_rc_pointer(x, y, startorient, angwidth, len, width, bg, colour) -> pointer;

This will be partly analogous to rc_slider

--- $poplocal/local/rclib/teach/rc_constrained_pointer
--- Copyright University of Birmingham 2002. All rights reserved. ------
