common
Class Handles

Object
  extended by common:Generic
      extended by common:Transform
          extended by common:UtilityTransform
              extended by common:Handles

class 
UtilityTransform:Handles

Handles utility transform class.

This is a utility transform used to display "handles". Handles are visual aids that show where important points that influence a formula are located within an image. Provided you have defined the handle data, it will return a solid flag for each transformed point. It is up to your code to decide what to do with that information. As an example, in a transform you might toggle the state of the #solid flag based on the handle rendering result; in a coloring formula, you might use #solid or you might use a specific color.

This class provides three types of visual aids. First are the handles themselves; these are located at a specific point, and can be a box, a circle, a diamond, an X, a point with an arrow, or a point with a double arrow. Each handle may also be labeled with a number (integer), a combination of letters and numbers (up to five), or an 8x8 pixel icon. Handles may also be rotated to arbitrary angles (especially useful for arrows). See SetHandlePoint() for more information.

The second type of visual aid are lines. Often these lines are used to connect handle points together, but there is absolutely no requirement that this be done; handle lines can be constructed anywhere a formula needs. Lines may be solid, dotted, short dashed, or long dashed. See SetHandleLine() for more information.

The third type of visual aid are circles. Circles may be placed anywhere and be of any size; as with lines, they may be solid, dotted, short dashed, or long dashed. Note that circles will always be circles, even if the aspect ratio of the fractal image is not 1:1.

Handles are intended as visual aids for the creation of fractal images, not as part of the final created image. For this reason, the default settings for the Handles class will automatically disable handle rendering for any rendered image. Also, handle sizes are always measured in pixels, which means when the image size shrinks (as it does for previews) the handles do not shrink. This makes them too large for most uses of the preview window, so the default settings for Handles disables them on preview images as well. The user can override these settings if they choose to.

To use the Handles class, create a Handles parameter for your formula. (You probably do not want it to be selectable.) Then, call SetHandleCount() to indicate the number of points, lines, and circles you will use. Call SetHandlePoint(), SetHandleLine(), and SetHandleCircle() for each element. Then, while processing, use Init() to set up each pixel and Iterate() to compute the handle solid flag value. Use IsSolid() to test the result of the handle rendering.


Ultra Fractal Source

Toggle UF Source Code Display

 class Handles(UtilityTransform) {
   ; Handles utility transform class.
   ; <p>
   ; This is a utility transform used to display "handles".
   ; Handles are visual aids that show where important points
   ; that influence a formula are located within an image.
   ; Provided you have defined the handle data, it will return a
   ; solid flag for each transformed point. It is up to your
   ; code to decide what to do with that information. As an
   ; example, in a transform you might toggle the state of the
   ; #solid flag based on the handle rendering result; in a
   ; coloring formula, you might use #solid or you might use
   ; a specific color.
   ; <p>
   ; This class provides three types of visual aids. First are
   ; the handles themselves; these are located at a specific
   ; point, and can be a box, a circle, a diamond, an X, a
   ; point with an arrow, or a point with a double arrow. Each
   ; handle may also be labeled with a number (integer), a
   ; combination of letters and numbers (up to five), or an
   ; 8x8 pixel icon. Handles may also be rotated to arbitrary
   ; angles (especially useful for arrows). See SetHandlePoint()
   ; for more information.
   ; <p>
   ; The second type of visual aid are lines. Often these lines
   ; are used to connect handle points together, but there is
   ; absolutely no requirement that this be done; handle lines
   ; can be constructed anywhere a formula needs. Lines may
   ; be solid, dotted, short dashed, or long dashed. See
   ; SetHandleLine() for more information.
   ; <p>
   ; The third type of visual aid are circles. Circles may be
   ; placed anywhere and be of any size; as with lines, they
   ; may be solid, dotted, short dashed, or long dashed. Note
   ; that circles will always be circles, even if the aspect
   ; ratio of the fractal image is not 1:1.
   ; <p>
   ; Handles are intended as visual aids for the creation of
   ; fractal images, not as part of the final created image.
   ; For this reason, the default settings for the Handles
   ; class will automatically disable handle rendering for
   ; any rendered image. Also, handle sizes are always measured
   ; in pixels, which means when the image size shrinks (as it
   ; does for previews) the handles do not shrink. This makes
   ; them too large for most uses of the preview window, so
   ; the default settings for Handles disables them on preview
   ; images as well. The user can override these settings if
   ; they choose to.
   ; <p>
   ; To use the Handles class, create a Handles parameter for
   ; your formula. (You probably do not want it to be selectable.)
   ; Then, call SetHandleCount() to indicate the number of points,
   ; lines, and circles you will use. Call SetHandlePoint(),
   ; SetHandleLine(), and SetHandleCircle() for each element.
   ; Then, while processing, use Init() to set up each pixel and
   ; Iterate() to compute the handle solid flag value. Use
   ; IsSolid() to test the result of the handle rendering.
   
 public:
   ; Constructor
   ;
   ; @param pparent a reference to the object creating the new object; typically, 'this'
   func Handles(Generic pparent)
     UtilityTransform.UtilityTransform(pparent)
     
     ; pixel coordinate transform
     m_PixelCoordinate = new PixelCoordinate(pparent)
 
     ; digit/letter bitmaps
     m_DigitsDetail[0] = (409387979.0,3552798232.0)    ; 0
     m_DigitsDetail[1] = (406329368.0,404232252.0)    ; 1
     m_DigitsDetail[2] = (1011221251.0,102261247.0)    ; 2
     m_DigitsDetail[3] = (1011221262.0,50546236.0)    ; 3
     m_DigitsDetail[4] = (271605860.0,3439266828.0)    ; 4
     m_DigitsDetail[5] = (2126576646.0,50562684.0)    ; 5
     m_DigitsDetail[6] = (476110054.0,3284362812.0)    ; 6
     m_DigitsDetail[7] = (4270197772.0,202905624.0)    ; 7
     m_DigitsDetail[8] = (1013335612.0,1656972860.0)    ; 8
     m_DigitsDetail[9] = (1013367747.0,1731921464.0)    ; 9
     m_DigitsDetail[10] = (0.0,0.0)            ; blank
     m_DigitsDetail[11] = (404237356.0,1182696323.0)    ; A
     m_DigitsDetail[12] = (4240885500.0,3334719228.0)  ; B
     m_DigitsDetail[13] = (1013170368.0,3233833788.0)  ; C
     m_DigitsDetail[14] = (4173775811.0,3284387576.0)  ; D
     m_DigitsDetail[15] = (4274045176.0,3233857791.0)  ; E
     m_DigitsDetail[16] = (4290822392.0,3233857728.0)  ; F
     m_DigitsDetail[17] = (1013170383.0,3284362044.0)  ; G
     m_DigitsDetail[18] = (3284386815.0,3284386755.0)  ; H
     m_DigitsDetail[19] = (1008211992.0,404232252.0)    ; I
     m_DigitsDetail[20] = (252052998.0,101092472.0)    ; J
     m_DigitsDetail[21] = (3335313648.0,3637298883.0)  ; K
     m_DigitsDetail[22] = (3233857728.0,3233858047.0)  ; L
     m_DigitsDetail[23] = (2177099775.0,3687039939.0)  ; M
     m_DigitsDetail[24] = (2210653171.0,3687827395.0)  ; N
     m_DigitsDetail[25] = (1013367747.0,3284362812.0)  ; O
     m_DigitsDetail[26] = (4240884678.0,4240490688.0)  ; P
     m_DigitsDetail[27] = (1013367747.0,3553322555.0)  ; Q
     m_DigitsDetail[28] = (4240884678.0,4241278659.0)  ; R
     m_DigitsDetail[29] = (1013080124.0,100894332.0)    ; S
     m_DigitsDetail[30] = (4279769112.0,404232252.0)    ; T
     m_DigitsDetail[31] = (3284386755.0,3284362812.0)  ; U
     m_DigitsDetail[32] = (2206418502.0,741087256.0)    ; V
     m_DigitsDetail[33] = (2206434118.0,1448487980.0)  ; W
     m_DigitsDetail[34] = (2210819128.0,946652867.0)    ; X
     m_DigitsDetail[35] = (3284362812.0,404232216.0)    ; Y
     m_DigitsDetail[36] = (4286975000.0,405823999.0)    ; Z
     m_DigitsDetail[37] = (417038043.0,402653184.0)    ; *
     m_DigitsDetail[38] = (101059596.0,404238384.0)    ; /    
     m_DigitsDetail[39] = (0.0,6168.0)          ; .
   endfunc
   
   ; Compute handle state for a single point within a sequence
   ; <p>
   ; Handles is a Transform, so to test the handle state for
   ; a particular point, the standard approach is to Init(),
   ; Iterate() (just once), and then test IsSolid().
   ;
   ; @param pz the complex value to test
   ; @return the same complex value (Handles is a transform but the value is in the solid test; the complex value is not transformed)
   complex func Iterate(complex pz)
     UtilityTransform.Iterate(pz)
     
     float sx
     float sy
     float vx
     float vy
     float dx
     float dy
     bool nolines = false
     
     ; note that we completely ignore pz; handles are NOT transformed along with
     ; the underlying image
 
     ; if handles are turned off, stop now
     if (!@p_showhandles || \
       (#calculationPurpose == 1 && @p_handlesnopreview) || \
       (#calculationPurpose == 3 && @p_handlesnopreview) || \
       (#calculationPurpose == 2 && @p_handlesnorender))
       return pz
     endif
 
     $ifdef debug
       int testx = 400
       int testy = 600
     $endif
 
     ; see if any handle points apply
     int j = 0
     int k = 0
     int l = length(m_HandleTypes)
     int label = 0
     int base = 0
     int digit = 0
     float n = 0
     while (j < l)
       if (m_HandleTypes[j] > 0 || m_HandleLabels[j] > 0)
         sx = real(m_HandleCenters[j])
         sy = imag(m_HandleCenters[j])
         vx = #x-sx
         vy = #y-sy
         if (m_HandleRotations[j] != 0)
           complex v = (vx + flip(vy)) * m_HandleRotations[j]
           vx = real(v)
           vy = imag(v)
         endif
         dx = abs(vx)
         dy = abs(vy)
 
         ; if we're inside any handle area, flag that no lines be drawn
         if ((m_HandleTypes[j] < 5 && dx < @p_handlesize+@p_handlewidth*2 && dy < @p_handlesize+@p_handlewidth*2) || \
           (m_HandleTypes[j] >= 5 && dx < @p_handlewidth*2.5 && dy < @p_handlewidth*2.5))
           nolines = true
         endif
 
         ; handle
         if ((m_HandleTypes[j] == 1 && \
            (dx >= @p_handlesize || dy >= @p_handlesize) && \
            (dx < @p_handlesize+@p_handlewidth && dy < @p_handlesize+@p_handlewidth)) || \
           (m_HandleTypes[j] == 2 && \
            dx*dx+dy*dy >= @p_handlesize*@p_handlesize && dx*dx+dy*dy < sqr(@p_handlesize+@p_handlewidth)) || \
           (m_HandleTypes[j] == 3 && \
            dx+dy >= @p_handlesize && dx+dy < @p_handlesize+@p_handlewidth) || \
           (m_HandleTypes[j] == 4 && \
            2*abs(dx+0.5-dy) < @p_handlewidth && (dx < @p_handlesize+@p_handlewidth && dy < @p_handlesize+@p_handlewidth)) || \
           (m_HandleTypes[j] == 5 && \
            ((dy < @p_handlewidth*0.5 && vx >= 0 && vx < @p_handlesize+@p_handlewidth) || \
             (vx >= @p_handlesize*0.75 && dx+dy >= @p_handlesize && dx+dy < @p_handlesize+@p_handlewidth) || \
             (dx*dx+dy*dy <= 1.5*@p_handlewidth*@p_handlewidth))) || \
           (m_HandleTypes[j] == 6 && \
            ((dy < @p_handlewidth*0.5 && dx < @p_handlesize+@p_handlewidth) || \
             (dx >= @p_handlesize*0.75 && dx+dy >= @p_handlesize && dx+dy < @p_handlesize+@p_handlewidth) || \
             (dx*dx+dy*dy <= 1.5*@p_handlewidth*@p_handlewidth))) \
            )
           m_Solid = !m_Solid
         endif
 
         ; number
         if (@p_handleswithnumbers && \
           m_HandleLabelSizes[j] > 0 && \
           #x >= sx+@p_handlesize+@p_handlewidth && #x < sx+@p_handlesize+@p_handlewidth*(1+5*m_HandleLabelSizes[j]) && \
           #y >= sy+@p_handlesize+@p_handlewidth && #y < sy+@p_handlesize+@p_handlewidth*5)
 
           nolines = true                  ; no lines through number
 
           dx = floor((#x-sx-@p_handlesize-@p_handlewidth)/@p_handlewidth*2)  ; pixel coordinate within label area
           dy = floor((#y-sy-@p_handlesize-@p_handlewidth)/@p_handlewidth*2)
 
           label = m_HandleLabels[j]
           if (label > 0)
             base = 10
           else
             base = 40
           endif
           digit = m_HandleLabelSizes[j]-floor(dx/10)-1  ; digit position within label
           dx = dx % 10                  ; pixel coordinate within digit
           k = floor(abs(label) / base^digit + 0.00001) % base  ; actual glyph number (includes a bit of round-off fudge)
 
           if (dx > 0 && dx < 9)              ; pixel lies within glyph boundary
             if (dy < 4)                  ; top half of glyph
               n = real(m_DigitsDetail[k])        ; bit values
               dx = (8-dx) + 8*(3-dy)          ; bit number
             else                    ; bottom half of glyph
               n = imag(m_DigitsDetail[k])        ; bit values
               dx = (8-dx) + 8*(7-dy)          ; bit number
             endif
             dy = 2^dx                  ; mask bit
             if (floor(n / dy) % 2 > 0)          ; test bit
               m_Solid = !m_Solid
             endif
           endif
         endif
         
         ; icon
         ; note the same flag that controls number rendering controls icons
         if (@p_handleswithnumbers && \
           (real(m_HandleIcons[j]) > 0 || imag(m_HandleIcons[j]) > 0) && \
           #x >= sx+@p_handlesize+@p_handlewidth && #x < sx+@p_handlesize+@p_handlewidth*5 && \
           #y >= sy+@p_handlesize+@p_handlewidth && #y < sy+@p_handlesize+@p_handlewidth*5)
 
           nolines = true                  ; no lines through number
 
           dx = floor((#x-sx-@p_handlesize-@p_handlewidth)/@p_handlewidth*2)  ; pixel coordinate within icon area
           dy = floor((#y-sy-@p_handlesize-@p_handlewidth)/@p_handlewidth*2)
 
           if (dx >= 0 && dx < 8)              ; pixel lies within glyph boundary
             if (dy < 4)                  ; top half of glyph
               n = real(m_HandleIcons[j])        ; bit values
               dx = (7-dx) + 8*(3-dy)          ; bit number
             else                    ; bottom half of glyph
               n = imag(m_HandleIcons[j])        ; bit values
               dx = (7-dx) + 8*(7-dy)          ; bit number
             endif
             dy = 2^dx                  ; mask bit
             if (floor(n / dy) % 2 > 0)          ; test bit
               m_Solid = !m_Solid
             endif
           endif
         endif
         
       endif      
       
       j = j + 1
     endwhile
 
     ; see if any handle lines apply
     float linelength = 0
     j = 0
     l = length(m_LineTypes)
     while (j < l && !nolines && @p_handleswithlines)
       if (!m_LineConnects[j])    ; this line does not connect to the previous line
         linelength = 0      ; reset the accumulated line length (helps line up dashes/dots)
       endif
       
       if (m_LineTypes[j] > 0)
         dx = ((real(m_LineEnds[j]) - real(m_LineStarts[j])) * (#y - imag(m_LineStarts[j])) - (#x - real(m_LineStarts[j])) * (imag(m_LineEnds[j]) - imag(m_LineStarts[j]))) / m_LineDistances[j]
         dy = ((imag(m_LineEnds[j]) - imag(m_LineStarts[j])) * (#y - imag(m_LineStarts[j])) - (#x - real(m_LineStarts[j])) * (real(m_LineStarts[j]) - real(m_LineEnds[j]))) / m_LineDistances[j]
         if (abs(dx) < @p_handlewidth*0.5 && dy > 0 && dy < m_LineDistances[j])
           if (m_LineTypes[j] == 1)
             m_Solid = !m_Solid
           elseif (m_LineTypes[j] == 2)
             if (dy % (@p_handlewidth*2) > @p_handlewidth)
               m_Solid = !m_Solid
             endif
           elseif (m_LineTypes[j] == 3)
             if (dy % (@p_handlesize) > @p_handlesize*0.5)
               m_Solid = !m_Solid
             endif
           elseif (m_LineTypes[j] == 4)
             if (dy % (@p_handlesize*2) > @p_handlesize)
               m_Solid = !m_Solid
             endif
           endif
         endif
       endif
 
       j = j + 1
     endwhile
     linelength = linelength + 1    ; **** silences compiler warning
   
     ; see if any handle circles apply
     j = 0
     l = length(m_CircleTypes)
     while (j < l && !nolines && @p_handleswithlines)
       if (m_CircleTypes[j] > 0)
         dx = cabs((#x + flip(#y)) - m_CircleCenters[j]) - m_CircleRadii[j]
         dy = atan2((#x + flip(#y)) - m_CircleCenters[j])
         if (dy < 0)
           dy = dy + 2*#pi
         endif
         dy = dy * m_CircleRadii[j]
         $ifdef debug
           if (#x == testx && #y == testy)
             print("test: ", #x, ",", #y)
             print("circle ", j, " center: ", m_CircleCenters[j], " radius: ", m_CircleRadii[j])
             print("dx: ", dx, " dy: ", dy)
           endif
         $endif
         if (abs(dx) < @p_handlewidth*0.5)
           if (m_CircleTypes[j] == 1)
             m_Solid = !m_Solid
           elseif (m_CircleTypes[j] == 2)
             if (dy % (@p_handlewidth*2) > @p_handlewidth)
               m_Solid = !m_Solid
             endif
           elseif (m_CircleTypes[j] == 3)
             if (dy % (@p_handlesize) > @p_handlesize*0.5)
               m_Solid = !m_Solid
             endif
           elseif (m_CircleTypes[j] == 4)
             if (dy % (@p_handlesize*2) > @p_handlesize)
               m_Solid = !m_Solid
             endif
           endif
         endif
       endif
 
       j = j + 1
     endwhile
   
     return pz
   endfunc
 
   ; Set the number of handles to render
   ; <p>
   ; You must call this function before you can define any handles.
   ; Specify the maximum number of each type of visual aid you
   ; will use. Any handles not defined will not be rendered, but
   ; defining more than you need will slow down rendering slightly.
   ;
   ; @param phandles number of handle points
   ; @param plines number of handle lines
   ; @param pcircles number of handle circles
   ; @param pcurves number of special handles; this is being reserved for future use, so you should always use 0
   func SetHandleCount(int phandles, int plines, int pcircles, int pcurves)
     if (phandles >= 0)
       setLength(m_HandleTypes, phandles)
       setLength(m_HandleLabels, phandles)
       setLength(m_HandleLabelSizes, phandles)
       setLength(m_HandleIcons, phandles)
       setLength(m_HandleCenters, phandles)
       setLength(m_HandleRotations, phandles)
     endif
 
     if (plines >= 0)
       setLength(m_LineTypes, plines)
       setLength(m_LineConnects, plines)
       setLength(m_LineDistances, plines)
       setLength(m_LineStarts, plines)
       setLength(m_LineEnds, plines)
     endif
 
     if (pcircles >= 0)
       setLength(m_CircleTypes, pcircles)
       setLength(m_CircleCenters, pcircles)
       setLength(m_CircleRadii, pcircles)
     endif
   endfunc
 
   ; Define a handle point
   ; <p>
   ; This function defines all the information about a handle
   ; point. You will call this once for each handle point.
   ; <p>
   ; Handles can be one of several pre-defined shapes. You may
   ; specify a blank handle, which is useful if you simply want
   ; to place a label within your image. Squares are, by
   ; convention, used to indicate points that are on an edge
   ; that the user can directly select. Circles are used to
   ; indicate points that are NOT on an edge that the user may
   ; select. Diamonds are used to indicate interior or center
   ; points; the user may or may not be allowed to directly
   ; select this point. X should be used for an implied or
   ; computed point that the user CANNOT select. Arrows should
   ; be used for points where a particular direction needs to
   ; be indicated (specify the angle as something other than
   ; zero). Double-arrows should be used for points where the
   ; location along the line perpendicular to the arrows is
   ; irrelevant, and only the location in the direction the
   ; arrows show is relevant; they may also be used to mark
   ; points on a handle circle. A single triangle is used to
   ; mark a point on a line that the user may set indirectly.
   ; A double triangle should be used to indicate a point on
   ; a line where the line is set automatically but the
   ; position along the line is under the user's control
   ; (different from the double-arrow, where the user sets the
   ; line position; here they are setting the position of a
   ; point along a line).
   ; <p>
   ; Any handle shape may be rotated, but rotation is primarily
   ; intended for arrows. Rotation does not affect labels or
   ; icons, only the handle shape.
   ; <p>
   ; Labels can be numbers or short letter/number combinations.
   ; For positive integers, simply provide the number. For
   ; words, the process is a bit more complicated. Words may
   ; be five characters or less (including spaces). Each character
   ; is encoded in a base-40 encoding scheme:
   ; <p>
   ; 0 to 9: digits 0 to 9<br>
   ; 10: space<br>
   ; 11 to 36: letters A to Z<br>
   ; 37: asterisk (*)<br>
   ; 38: slash (/)<br>
   ; 39: period (.)<br>
   ; <p>
   ; As an example, to encode the word "TEST", convert each letter
   ; to its number: 30 15 29 30
   ; <p>
   ; Next, treat these as base 40 values:
   ; (((30*40+15)*40+29)*40+30) = 1945190
   ; <p>
   ; Then, pass in the value as a negative integer to indicate it
   ; should be interpreted as characters rather than just a number:
   ; -1945190
   ; <p>
   ; Icons may be used in place of letters. (If you place an icon
   ; and characters on the same handle point, they will overlap.)
   ; Icons are 8x8 pixel bitmaps encoded as a single complex value:
   ; <p>
   ; row 1: real(icon) bits 31 to 24<br>
   ; row 2: real(icon) bits 23 to 16<br>
   ; row 3: real(icon) bits 15 to 8<br>
   ; row 4: real(icon) bits 7 to 0<br>
   ; row 5: imag(icon) bits 31 to 24<br>
   ; row 6: imag(icon) bits 23 to 16<br>
   ; row 7: imag(icon) bits 15 to 8<br>
   ; row 8: imag(icon) bits 7 to 0
   ; <p>
   ; Because 32 bits are used for each icon, you must specify the
   ; values as explicitly real (include .0 on the end). Note that
   ; internally, the letters, numbers, and symbols are specified
   ; with this same format.
   ;
   ; @param phandle slot number, starting at 0; if you indicate -1, the next slot will be used automatically
   ; @param ptype handle type: 0 = blank (just number), 1 = square, 2 = circle, 3 = diamond, 4 = X, 5 = single arrow, 6 = double arrow, 7 = single triangle, 8 = double triangle
   ; @param plabel label number: 0 = blank, >0 = number, <0 = number/letter
   ; @param picon icon (use 0 for blank)
   ; @param pz location of handle
   ; @param pr rotation of handle in degrees
   func SetHandlePoint(int phandle, int ptype, int plabel, complex picon, complex pz, float pr)
     float logbase = 10.0      ; work-around for log(10.0) optimization introducing round-off errors
 
     if (phandle == -1)
       phandle = m_HandleNext
       m_HandleNext = m_HandleNext + 1
     endif
     
     ; save point metadata
     m_HandleTypes[phandle] = ptype
     m_HandleLabels[phandle] = plabel
     m_HandleIcons[phandle] = picon
     m_HandleRotations[phandle] = (0,1)^(pr/90.0)
 
     ; convert point to pixel coordinates now    
     m_PixelCoordinate.Init(pz)
     m_HandleCenters[phandle] = m_PixelCoordinate.Iterate(pz)
 
     ; determine label length now
     if (plabel > 0)
       m_HandleLabelSizes[phandle] = floor(log(plabel)/log(logbase))+1
     elseif (plabel < 0)
       logbase = 40.0
       m_HandleLabelSizes[phandle] = floor(log(-plabel)/log(logbase))+1
     else
       m_HandleLabelSizes[phandle] = 0
     endif
   endfunc
 
   ; Define a handle line segment
   ; <p>
   ; Handle lines can be used to complement handle points and
   ; show relationships between them. They can also be used by
   ; themselves (there is no requirement that they be used to
   ; connect handle points).
   ; <p>
   ; Lines are automatically hidden in the area around handle
   ; points and labels/icons. This is so that the lines can
   ; never obscure the more important parts of the handle
   ; system.
   ; <p>
   ; Lines may be drawn in one of four styles. Solid lines
   ; should be used only for shapes or elements that are
   ; otherwise completely invisible. Dotted lines should be
   ; used to indicate constraining or boundary lines.
   ; Short dashed lines should be used for a shape boundary
   ; that is drawn some distance from a visible edge.
   ; <p>
   ; When lines are being drawn end-to-end without handles
   ; at the connecting points, you may wish to indicate that
   ; the lines are connected. This allows the handle system
   ; to automatically adjust the pattern on the lines to be
   ; continuous. If handles are rendered at the connecting
   ; points they will obscure the join, making this setting
   ; irrelevant.
   ;
   ; @param phandle slot number, starting at 0; if you indicate -1, the next slot will be used automatically
   ; @param pconnect whether segment connects to previous line segment
   ; @param ptype line type: 0 = blank, 1 = solid, 2 = dotted, 3 = short dashed, 4 = long dashed
   ; @param pstart starting point of line segment
   ; @param pend ending point of line segment
   func SetHandleLine(int phandle, int ptype, bool pconnect, complex pstart, complex pend)
     if (phandle == -1)
       phandle = m_LineNext
       m_LineNext = m_LineNext + 1
     endif
     
     ; save line metadata
     m_LineTypes[phandle] = ptype
     m_LineConnects[phandle] = pconnect
     
     ; convert points to pixel coordinates now
     ; we include a bit of fudge so that lines don't precisely
     ; fall on pixel boundaries; this guarantees that lines
     ; will not be an extra pixel thick due to each side being
     ; exactly the right number of pixels from the center
     m_PixelCoordinate.Init(pstart)
     m_LineStarts[phandle] = m_PixelCoordinate.Iterate(pstart) - (0.001, 0.001)
     m_PixelCoordinate.Init(pend)
     m_LineEnds[phandle] = m_PixelCoordinate.Iterate(pend) - (0.001, 0.001)
     
     ; compute line length in pixel coordinates now
     m_LineDistances[phandle] = cabs(m_LineEnds[phandle] - m_LineStarts[phandle])
   endfunc
 
   ; Define a handle circle
   ; <p>
   ; Circles are similar to lines in that they have different
   ; styles. In the current implementation, circles will always
   ; appear circular regardless of the image aspect ratio.
   ;
   ; @param phandle slot number, starting at 0; if you indicate -1, the next slot will be used automatically
   ; @param ptype circle type: 0 = blank, 1 = solid, 2 = dotted, 3 = short dashed, 4 = long dashed
   ; @param pcenter center point of the circle
   ; @param pradius radius of the circle
   func SetHandleCircle(int phandle, int ptype, complex pcenter, float pradius)
     if (phandle == -1)
       phandle = m_CircleNext
       m_CircleNext = m_CircleNext + 1
     endif
 
     ; save circle metadata
     m_CircleTypes[phandle] = ptype
     
     ; convert points to pixel coordinates now
     m_PixelCoordinate.Init(pcenter)
     m_CircleCenters[phandle] = m_PixelCoordinate.Iterate(pcenter)
     m_PixelCoordinate.Init(pcenter+pradius)
     m_CircleRadii[phandle] = cabs(m_CircleCenters[phandle]-m_PixelCoordinate.Iterate(pcenter+pradius))
   endfunc
 
   ; use this function to set the screen-relative coordinate style
   ; if you don't do this, and you allow screen-relative coordinates,
   ; your handles will appear in the wrong place
   func SetScreenRelative(int ptype)
     m_PixelCoordinate.SetScreenRelative(ptype)
   endfunc
 
 protected:
   PixelCoordinate m_PixelCoordinate
   complex m_DigitsDetail[40]
 
   int m_HandleNext
   int m_HandleTypes[]
   int m_HandleLabels[]
   int m_HandleLabelSizes[]
   complex m_HandleIcons[]
   complex m_HandleCenters[]
   complex m_HandleRotations[]
 
   int m_LineNext
   int m_LineTypes[]
   bool m_LineConnects[]
   float m_LineDistances[]
   complex m_LineStarts[]
   complex m_LineEnds[]
 
   int m_CircleNext
   int m_CircleTypes[]
   complex m_CircleCenters[]
   float m_CircleRadii[]
 
 default:
   title = "Handles utility class"
 
   int param v_handles
     caption = "Version (Handles)"
     default = 100
     hint = "This version parameter is used to detect when a change has been made to the formula that is incompatible with the previous version. When that happens, this field will reflect the old version number to alert you to the fact that an alternate rendering is being used."
     visible = @v_handles < 100
   endparam
 
   bool param p_showhandles
     caption = "Show Handles"
     default = true
     hint = "If enabled, shows 'handles' in the image to mark where the control points of the shape are located. This is very useful while you are creating your image, but you will probably not want handles in your final image render. By default, handles should not render in your final image if the render is large, but if you do a small final render you may need to use this master switch to disable handles."
   endparam
   bool param p_handlesnopreview
     caption = "Not on previews"
     default = true
     visible = (@p_showhandles)
     hint = "If checked, handles will not be shown on previews. Handles don't scale with window size, so they appear disproportionately large in the preview window."
   endparam
   bool param p_handlesnorender
     caption = "Not on renders"
     default = true
     visible = (@p_showhandles)
     hint = "If checked, handles will be disabled for any image above a certain size. This is useful for automatically disabling handles on disk renders."
   endparam
   bool param p_handleswithnumbers
     caption = "Show Numbers"
     default = true
     visible = (@p_showhandles)
     hint = "If checked, handles will be numbered."
   endparam
   bool param p_handleswithlines
     caption = "Show Lines"
     default = true
     visible = (@p_showhandles)
     hint = "If checked, lines may also appear."
   endparam
   int param p_handlesize
     caption = "Handle Size"
     default = 8
     visible = (@p_showhandles)
     hint = "Sets the size of the inside area of the handle. You may need to adjust this to make the handles more visible on some fractals."
   endparam
   int param p_handlewidth
     caption = "Handle Thickness"
     default = 2
     visible = (@p_showhandles)
     hint = "Sets the thickness of the handles. You may need to adjust this to make the handles more visible on some fractals."
   endparam
 }
 


Constructor Summary
Handles()
           
Handles(Generic pparent)
          Constructor
 
Method Summary
 complex Iterate(complex pz)
          Compute handle state for a single point within a sequence
 void SetHandleCircle(int phandle, int ptype, complex pcenter, float pradius)
          Define a handle circle
 void SetHandleCount(int phandles, int plines, int pcircles, int pcurves)
          Set the number of handles to render
 void SetHandleLine(int phandle, int ptype, boolean pconnect, complex pstart, complex pend)
          Define a handle line segment
 void SetHandlePoint(int phandle, int ptype, int plabel, complex picon, complex pz, float pr)
          Define a handle point
 void SetScreenRelative(int ptype)
          use this function to set the screen-relative coordinate style if you don't do this, and you allow screen-relative coordinates, your handles will appear in the wrong place
 
Methods inherited from class common:Transform
Init, IsSolid, IterateSilent
 
Methods inherited from class common:Generic
GetParent
 
Methods inherited from class Object
 

Constructor Detail

Handles

public Handles(Generic pparent)
Constructor

Parameters:
pparent - a reference to the object creating the new object; typically, 'this'

Handles

public Handles()
Method Detail

Iterate

public complex Iterate(complex pz)
Compute handle state for a single point within a sequence

Handles is a Transform, so to test the handle state for a particular point, the standard approach is to Init(), Iterate() (just once), and then test IsSolid().

Overrides:
Iterate in class Transform
Parameters:
pz - the complex value to test
Returns:
the same complex value (Handles is a transform but the value is in the solid test; the complex value is not transformed)

SetHandleCount

public void SetHandleCount(int phandles,
                           int plines,
                           int pcircles,
                           int pcurves)
Set the number of handles to render

You must call this function before you can define any handles. Specify the maximum number of each type of visual aid you will use. Any handles not defined will not be rendered, but defining more than you need will slow down rendering slightly.

Parameters:
phandles - number of handle points
plines - number of handle lines
pcircles - number of handle circles
pcurves - number of special handles; this is being reserved for future use, so you should always use 0

SetHandlePoint

public void SetHandlePoint(int phandle,
                           int ptype,
                           int plabel,
                           complex picon,
                           complex pz,
                           float pr)
Define a handle point

This function defines all the information about a handle point. You will call this once for each handle point.

Handles can be one of several pre-defined shapes. You may specify a blank handle, which is useful if you simply want to place a label within your image. Squares are, by convention, used to indicate points that are on an edge that the user can directly select. Circles are used to indicate points that are NOT on an edge that the user may select. Diamonds are used to indicate interior or center points; the user may or may not be allowed to directly select this point. X should be used for an implied or computed point that the user CANNOT select. Arrows should be used for points where a particular direction needs to be indicated (specify the angle as something other than zero). Double-arrows should be used for points where the location along the line perpendicular to the arrows is irrelevant, and only the location in the direction the arrows show is relevant; they may also be used to mark points on a handle circle. A single triangle is used to mark a point on a line that the user may set indirectly. A double triangle should be used to indicate a point on a line where the line is set automatically but the position along the line is under the user's control (different from the double-arrow, where the user sets the line position; here they are setting the position of a point along a line).

Any handle shape may be rotated, but rotation is primarily intended for arrows. Rotation does not affect labels or icons, only the handle shape.

Labels can be numbers or short letter/number combinations. For positive integers, simply provide the number. For words, the process is a bit more complicated. Words may be five characters or less (including spaces). Each character is encoded in a base-40 encoding scheme:

0 to 9: digits 0 to 9
10: space
11 to 36: letters A to Z
37: asterisk (*)
38: slash (/)
39: period (.)

As an example, to encode the word "TEST", convert each letter to its number: 30 15 29 30

Next, treat these as base 40 values: (((30*40+15)*40+29)*40+30) = 1945190

Then, pass in the value as a negative integer to indicate it should be interpreted as characters rather than just a number: -1945190

Icons may be used in place of letters. (If you place an icon and characters on the same handle point, they will overlap.) Icons are 8x8 pixel bitmaps encoded as a single complex value:

row 1: real(icon) bits 31 to 24
row 2: real(icon) bits 23 to 16
row 3: real(icon) bits 15 to 8
row 4: real(icon) bits 7 to 0
row 5: imag(icon) bits 31 to 24
row 6: imag(icon) bits 23 to 16
row 7: imag(icon) bits 15 to 8
row 8: imag(icon) bits 7 to 0

Because 32 bits are used for each icon, you must specify the values as explicitly real (include .0 on the end). Note that internally, the letters, numbers, and symbols are specified with this same format.

Parameters:
phandle - slot number, starting at 0; if you indicate -1, the next slot will be used automatically
ptype - handle type: 0 = blank (just number), 1 = square, 2 = circle, 3 = diamond, 4 = X, 5 = single arrow, 6 = double arrow, 7 = single triangle, 8 = double triangle
plabel - label number: 0 = blank, >0 = number, <0 = number/letter
picon - icon (use 0 for blank)
pz - location of handle
pr - rotation of handle in degrees

SetHandleLine

public void SetHandleLine(int phandle,
                          int ptype,
                          boolean pconnect,
                          complex pstart,
                          complex pend)
Define a handle line segment

Handle lines can be used to complement handle points and show relationships between them. They can also be used by themselves (there is no requirement that they be used to connect handle points).

Lines are automatically hidden in the area around handle points and labels/icons. This is so that the lines can never obscure the more important parts of the handle system.

Lines may be drawn in one of four styles. Solid lines should be used only for shapes or elements that are otherwise completely invisible. Dotted lines should be used to indicate constraining or boundary lines. Short dashed lines should be used for a shape boundary that is drawn some distance from a visible edge.

When lines are being drawn end-to-end without handles at the connecting points, you may wish to indicate that the lines are connected. This allows the handle system to automatically adjust the pattern on the lines to be continuous. If handles are rendered at the connecting points they will obscure the join, making this setting irrelevant.

Parameters:
phandle - slot number, starting at 0; if you indicate -1, the next slot will be used automatically
pconnect - whether segment connects to previous line segment
ptype - line type: 0 = blank, 1 = solid, 2 = dotted, 3 = short dashed, 4 = long dashed
pstart - starting point of line segment
pend - ending point of line segment

SetHandleCircle

public void SetHandleCircle(int phandle,
                            int ptype,
                            complex pcenter,
                            float pradius)
Define a handle circle

Circles are similar to lines in that they have different styles. In the current implementation, circles will always appear circular regardless of the image aspect ratio.

Parameters:
phandle - slot number, starting at 0; if you indicate -1, the next slot will be used automatically
ptype - circle type: 0 = blank, 1 = solid, 2 = dotted, 3 = short dashed, 4 = long dashed
pcenter - center point of the circle
pradius - radius of the circle

SetScreenRelative

public void SetScreenRelative(int ptype)
use this function to set the screen-relative coordinate style if you don't do this, and you allow screen-relative coordinates, your handles will appear in the wrong place