Irregular Clickable HotSpots Using Raw Image Index

Date:    Wed, 12 Jul 1995 17:14:07 +0000
From:    Daetwyler Christof <daetwyler@AUM.UNIBE.CH>
Subject: Irregular clickable "sprites"
A few days ago, I posted, that you can now ftp a little program, that demonstrates our technique, how multiple irregular-shaped hot-spots can be easily done. The program is called EYE, it allows the interactive examination of a fundus altered by diabetic disease. The program is in english and in german.

I forgot to mention the sites, where you can get the Windows-Version and where you find the story, how this technique has been evoluted with the help of the listserver DIRECT-L in the Internet.

If you take our Web-Site into your Bookmarks, you won't have to copy the links. The URL where our Projects are described is:

  http://www.iawf.unibe.ch/aum/Portrait/prod/cbt.htm

Here's again the story of making as many irregular-shaped-hotzones as you want:

PART 1 OF THE eMAIL:

How to make irregular-shaped 'hot-spots' in multiple layers? We do it by means of colours. On several photocopies of the same region (different regions are at example: man from frontal, man from side, man from back, feet from top, feet from ground etc.), we draw the outlines of the different qualities; then we scan it into the computer and fill the different regions with different colors. When done, we make an export as RAW-File (the picture as one string where each of 256 pictures is represented as an other ASCII-Sign) - so that with an easy script the position of the cursor over a picture can be compared with the corresponding ASCII-Sign. For example: when the cursor is over the knee, from first RAW-File we get the information: peripheral nerve is Nervus saphenus; from the second RAW-File we get the information: segmental nerve is lumbal 4; from the third we get the information that we can do the 'quadriceps-femoris-reflex' and from the forth we get the information that the sensibility at this point is normal. This is done very fast, so that every position of the cursor over the picture gives immediate results.

PART 2 OF THE eMAIL: HISTORY of the solution
or
How a listserver like DIRECT-L can help to develop something new together

First I asked the Direct-L Forum the following:
I'm working on an interactive education programm in neurology for medical students. One big problem is, how interactivity can be made. My idea is, that lots of problems can be easy solved... ...IF I COULD DETECT THE COLOR OF THE BITMAP-PIXEL UNDER MY CURSOR!

Imagine: A very complicated anatomy-Pictuere with dozends of nerves, every of them in an slightly different color - and my program could detect over which nerve the cursor rolls by detecting the color. No need to make complicated-formed sprites and detect the rollovers!

If anybody know, how this could be done, please let me know. Many thanks in advance.

Second I get your XCmd

Third I wanted a similar XCmd for the Window-side

Fourth I got the following solution:

Here's the best solution I could come up with for finding the pixel color under the cursor (short of writing an XObject.) In the process of coming up with this solution I discovered how to make unlimited irregular shaped hotspots without using a single channel! Yipee! :)

STEP 1: Pull your 640x480 backdrop up in Photoshop and convert it to 8 bit (your choice of palette.)

STEP 2: Save a copy out in RAW format and choose TEXT (upper case) for the file type. The data fork should be exactly 307200 bytes for a 640x480. If it's 921600 then you saved it as RGB which will mess up the scheme. The resource fork is unimportant. Let's save it with the name 'MyRawPictFile'

STEP 3: Now in your Director movie, load the raw pict file data into a string using fileIO. (PC users beware of 64K fileIO limit, you'll have to chop it up into 64K chunks) Something like this will do:

on LoadRawPictIntoString
  global gPictInAString

  if objectP(myObj) then myObj(mDispose)
  set whichFIle = the pathName & "MyRawPictFile"
  put FileIO(mNew,"read",whichFIle) into myObj
  put myObj(mReadFile) into gPictInAString
end

STEP 4: Put the following subroutine in a movie script.

on FindColorOfPixelUnderCursor
  global gPictInAString

  set x = the mouseH
  set y = the mouseV
  set locOfPixelInString = (y*640) + x + 1
  return charToNum(char locOfPixelInString of gPictInAString)
end

STEP 5: To check what color is under your cursor call the FindColorOfPixelUnderCursor routine. Pop these two event handlers into the same score script and open your message window to try it out. You should get index numbers from 0 to 255 which correspond to the colors in the current palette:

on mouseDown
  put FindColorOfPixelUnderCursor()
end

on exitFrame
  go to the frame
end

NOTE: In case it's not obvious, I'm not really checking the color of the pixel under the cursor. I'm just associating the coordinates of the mouseclick with a location in a string. The string represents a particular picture which may or may not be on the stage. It will return the indexes to the pict file represented by the string regardless of what's on stage.

But wait there's more! Consider the following derivative of this solution.

WE CAN HAVE 256+ IRREGULAR SHAPED CLICK ZONES!!!

By using this same process you can have 256 irregularly formed, disconnected click zones. Just pull your 640x480 pict file up in Photoshop 3.0. Create a new layer and go around painting over each click zone in its own unique color. After you've painted each of your click zones in a different color, copy this *matte* layer to a new document, change the new document to indexed color, and proceed as above.

Notice that this is not really a matte. It doesn't hide under your backdrop or take up a channel. It's sort of a virtual matte if you will. :) It just sits out in its string which you reference easily as in FindColorOfPixelUnderCursor. For this purpose its not the color that's important but rather the *index*. The color is just providing an easy way to assign a unique identifier to each and every pixel location on the stage.

Once you have your indexes you can just set up a property list to connect the index numbers of your click zones with their respective handlers. Like...

on mouseDown
  set clickList = [0:"handler1",1:"handler2"]
  do getaProp(clickList,FindColorOfPixelUnderCursor())
end

ou can even make the cursor change over the click zones by FindingColorOfPixelUnderCursor and hooking up the index with a property list of cursors! Like so:

Init a list of cusors ...

on InitCursorLIst
  -- Index 0 = arrow
  -- Index 1 = crosshair
  -- Index 2 = custom 1bit cusor in cast 7 with mask in cast 8
  set gCursorList = [0:-1,1:2,2:[7,8]]
end
Then set the cursor according to the pixel under the cursor:
on exitFrame
  global gCursorList
  cursor (getaProp(gCursorList,FindColorOfPixelUnderCursor()))
  go to the frame
end
Pitfalls to watch for:
  1. Make sure you are painting with the pencil not the brush in Photoshop. The pencil will provide clean edges. The antialiasing of the paint brush would cause problems when you convert to indexed color.
  2. Make sure your using colors which will map to different indexes. The easiest way to do this is to load a System Palette into the swatches. Then dab into the swatches with the color picker to choose your colors for the pencil. Then convert to system pal when your done and the RGB colors will index exactly.
  3. Furthermore if you need even more than 256 click zones, just do the same sort of thing in RGB. The RAW format will give three bytes per pixel instead of one so the math will be a little different but all in all it should work out pretty much the same. You will then be able to have 16.7 million click zones!!! Guess this is kind of overkill since a 640x480 has only 307200 pixels. ;)
Is this not Inspirational!!! :) Maybe Director is not so bad after all! ;)

P.S. I have actually tried this out and it works. :) I'm posting this to DIRECT-L too.

+-----------------------------------+
|           Steve Gerber            |
|         sgerber2@aol.com          |
+-----------------------------------+
| Freelance Macromedia Director(tm) |
|         Lingo Programmer          |
+-----------------------------------+

Fifth I made out of it one RAW per Sprite:

Now I got the solution, how every Sprite can have as many RAW-files (ego Layers with Information), as I like. The script is following: You need 2 frames, in the first frame you set the globals, in the second frame you detect the 'colours'

  1. Framescript of frame 1:
    on exitFrame
      RawFilesImport
      put " " into field "message"
    end
    
    on RawFilesImport
      global gRawPeri, gRawSegm, gRawRefl, gRawSpez, gSpriteV, gSpriteH,
    gSpriteWidth
    
    This handler loads data of rawFiles into global variables and puts the location of sprite 1 into global variables
      put the top of Sprite 1 into gSpriteV
      put the left of Sprite 1 into gSpriteH
      put the width of Sprite 1 into gSpriteWidth
     
      - load RawFile with data about peripheral nerves into global gRawPeri
      set whichFile = the pathName & "rPeri_1.txt"
      put FileIO(mNew, "read", whichFile) into myObj
      put myObj(mReadFile) into gRawPeri
    
      - load RawFile with data about segmental nerves into global gRawSegm
      if objectP(myObj) then myObj(mDispose)
      set whichFile = the pathName & "rSegm_1.txt"
      put FileIO(mNew, "read", whichFile) into myObj
      put myObj(mReadFile) into gRawSegm
    
      - load RawFile with data about reflex-zones into global gRawRefl
      if objectP(myObj) then myObj(mDispose)
      set whichFile = the pathName & "rRefl_1.txt"
      put FileIO(mNew, "read", whichFile) into myObj
      put myObj(mReadFile) into gRawRefl
    
      - load RawFile with data about special messages (blow-up's, change of shown picts etc)
      if objectP(myObj) then myObj(mDispose)
      set whichFile = the pathName & "rSpez_1.txt"
      put FileIO(mNew, "read", whichFile) into myObj
      put myObj(mReadFile) into gRawSpez
      if objectP(myObj) then myObj(mDispose)
      put "" into myObj
    end
    
  2. Framescript of frame 2:
    on exitFrame
      if rollover(1) then
        PixelToQualities
      else
        put empty into field "message"
      end if
      go to the frame
    end
    
    on PixelToQualities
      global gRawPeri, gRawSegm, gRawRefl, gRawSpez, gSpriteV, gSpriteH,
    gSpriteWidth
    
      - This handler gets the position of the cursor relative to Sprite 1
      - then it gets the corresponding character in different RAW-Files
      - and Get the information about the qualities to show out of a item List
    
      set x = the mouseH - gSpriteH
      set y = the mouseV - gSpriteV
      set z = 180
      set RawChar = ((y) * z) + x + 1
    
      set raw_1 to charToNum(char RawChar of gRawPeri)
      set raw_2 to charToNum(char RawChar of gRawSegm)
      set raw_3 to charToNum(char RawChar of gRawRefl)
      set raw_4 to charToNum(char RawChar of gRawSpez)
    
      if raw_1 > 0 then put item raw_1 of field "PeripherieText" into raw_1TEXT
      if raw_2 > 0 then put item raw_2 of field "SegmentText" into raw_2TEXT
      if raw_3 > 0 then put item raw_3 of field "ReflexText" into raw_3TEXT
      if raw_4 > 0 then put item raw_4 of field "SpezialText" into raw_4TEXT
    
      put raw_2TEXT & return & raw_1TEXT & return & raw_3TEXT & return & raw_4TEXT into field "Message"
      updateStage
    end
    
I'm sure, you'll enjoy the new possibilities. To give very easy the different qualities to different colors, I use the following script, that I paste into the Sprite 1:
on mouseUp
  - functionates, if the numToChar of the hotRegion is shown in line 1 of field "Message":
  if (value(line 1 of field "Message")) > 0 then
     put line 1 of field "input" into item (value(line 1 of field "Input")) of field "Itemlist_n"
  end if
end
Q: Are RAW-Files for Windows different from RAW-Files for Mac?
A: No. You can use the same RAW-Files created by Photoshop. Because of a bug in DFW 4.04 it is necessary to subtract 65280 from the values returned by charToNum, when it is > 256. This script has the fortune, that it will work fine with a further version of MMD where this bug will be fixed. (watch for **2** in the script-example)

Q: How can I break RAW-Files into smaller chunks?
A: The easiest way is to change the resolution from 72dpi to 36dpi in PhotoShop before you make the RAW-Files. This makes the RAW-Files 4 times smaller!(or 16 times when you would use 18dpi and so on). But then you have to change the script to find the right 'color'-value in that way, that you divide the x-wide and the y-height by either 2 (in case of 36dpi as documented in the script below), or 4 (would be the case by 18dpi) and so on:

  -- The sprite with the RAW-File correlated is sprite 1
  -- The RAW-File is exactly 109 pixels wide (was 218 pixels in 72dpi)
  -- the global variable gRawChar1 gets the position of the ASCII-Value in the RAW-File
  -- according to the position of the cursor over sprite 1
  -- The variable RAW-File contains a RAW-File read in by fileIO

  set x = (the mouseH - (the left of sprite 1)) / 2
  set y =(the mouseV - (the top of sprite 1)) / 2
  set z = 109

  set gRawChar = ((y) * z) + x + 1

  set RawInput to charToNum(char gRawChar of RAWfile)

 -- **2** the following fixes a bug in DfW v4.04
    if RawInput > 255 then set RawInput to value(RawInput - 65280)
 -- end of bug-fix
 
-------------------------------------------------
       _?_       Christof Daetwyler MD
      /_ _\      Dept. for Education Media AUM
     [\O-O/]     AUM - IAWF, University of Berne
      \_-_/      Inselspital 38
      _| |_      CH - 3010 Bern
     / \_/ \     Tel: +41 31 632 25 12
 __oOOO___OOOo__ Fax: +41 31 381 93 22
-------------------------------------------------