You might try something like this. It is a better approach, I think, but will probably have its own set of problems, so test carefully.
// all new code, all new bugs, check carefully to see if this does what you want
dtt = Open( "/Z:/failures(2).jmp" );
dtS = Open( "/Z:/Summary Bins(2).jmp" );
// the X-Y die coordinates are expected to be integers between -9,999,996 and +9,999,996
encodeLimit = 1e7; // pack/unpack two signed 7-digit integers in one 14-digit number
// a "key" is an X-Y pair packed into a single number. It is called a key
// because it is used as a key in an associative array. These next two
// routines convert between the two representations
keysToXY = Function( {keys},
{x, y},
x = Round( keys / encodeLimit );
y = keys - x * encodeLimit;
x || y;
);
XYtoKeys = Function( {xy},
xy[0, 1] * encodeLimit + xy[0, 2]
);
// load the X-Y pairs that describe failure positions
failXY = dtt[0, {x coord, y coord}];
// elementary sanity checks
If( Any( failXY <= -encodeLimit + 4 ) | Any( failXY >= encodeLimit - 4 ),
Throw( "data range" )
);
If( Any( failXY != Floor( failXY ) ),
Throw( "integer coords" )
);
// convert X-Y failure locations to keys, verify they convert back again
failkeys = XYtoKeys( failXY );
If( !All( keysToXY( failkeys ) == failXY ),
Throw( "encode/decode 1" )
);
// template for nearby points. THIS TABLE ENCODES THE 2 PIXEL BORDER.
// you might, or might not, want the corners included; the corners are
// commented out because I like the rounded appearance better.
template = [
//-2 -2, // corner
-2 -1,
-2 0,
-2 1,
//-2 2, // corner
-1 -2,
-1 -1,
-1 0,
-1 1,
-1 2,
0 -2,
0 -1,
0 0, // center
0 1,
0 2,
1 -2,
1 -1,
1 0,
1 1,
1 2,
// 2 -2, // corner
2 -1,
2 0,
2 1
//, 2 2 // corner
];
// convert the template X-Y pairs into keys, verify they convert back again
templatekeys = XYtoKeys( template );
If( !All( keysToXY( templatekeys ) == template ),
Throw( "encode/decode 2" )
);
// somthing very cool is going to happen next: template keys will be added to a failure
// key to get a set of keys around the failure key. The template, above, represents a
// square (with rounded corners) about an origin. Adding a failure key moves the
// origin to the X-Y failure point. All the nearby locations are then inserted into a
// collection (associative array) of points that are within 2 of a failure point.
// When done, the actual failure points are removed from the collection, leaving the
// collection as the 2-wide border.
// use AssociativeArray as a set to hold the key values;
// the nearbyPoint set gets populated with both the failure
// points AND the points that are nearby as identified by the template.
nearbyPoints = Associative Array();
For Each( {failkey}, failkeys,
nearbykeys = failkey + templatekeys; // nearby is the same shape as template, but at failkey, not 0,0
nearbyPoints << Insert( Associative Array( nearbykeys ) );
);
// now remove the set of points formed from all the failures...after this,
// nearbypoints is the 2-cell boundary without the interior.
nearbyPoints << Remove( Associative Array( failkeys ) );
// now make a table for a plot. red are from the failures table, blue are the border.
border = keysToXY( Matrix( nearbypoints << getkeys ) ) || J( N Items( nearbypoints ), 1, 1 );
center = failXY || J( N Rows( failXY ), 1, 2 );
dtg = As Table( border |/ center, <<columnnames( {"x coord", "y coord", "highlight"} ) );
dtg << Graph Builder(
Size( 1493, 888 ),
Show Control Panel( 0 ),
Variables( X( :x coord ), Y( :y coord ), Color( :highlight ) ),
Elements( Points( X, Y, Legend( 8 ) ) ),
SendToReport( Dispatch( {}, "Graph Builder", FrameBox, {Marker Size( 2 ), Marker Drawing Mode( "Normal" )} ) )
);
//////////////// check this carefully; I'm not at all sure it does what you want ///////////////
// I don't know what the summary bins represents or why it has the odd cut-out areas.
dtS << deletecolumns( highlight ); // remove the old highlight, replace with new
dtS << Update( With( dtg ), Match Columns( :X coord = :x coord, :Y coord = :y coord ) );
dtS:highlight << Set Modeling Type( "Ordinal" );
dtS << Select Where( :highlight == 1 ) << Markers( "Dot" );
dtS << Select Where( :highlight == 2 ) << Markers( "FilledCircle" );
dtS << clear select;
dtS << Graph Builder(
Size( 875, 811 ),
Show Control Panel( 0 ),
Variables( X( :X coord ), Y( :Y coord ), Color( :highlight ) ),
Elements( Points( X, Y, Legend( 7 ) ) ),
SendToReport(
Dispatch( {}, "X coord", ScaleBox, {Format( "Best", 12 )} ),
Dispatch( {}, "Y coord", ScaleBox, {Format( "Best", 12 )} ),
Dispatch( {}, "400", ScaleBox,
{Legend Model(
7,
Properties( -1, {gradient( {Color Theme( "Green to Black to Red" )} )}, Item ID( "highlight", 1 ) ),
Properties( -1, {Line Color( -58112 ), Fill Color( -58112 )}, Item ID( "Y coord", 1 ) )
)}
),
Dispatch( {}, "Graph Builder", FrameBox, {Marker Size( 1 ), Marker Drawing Mode( "Normal" )} )
)
);
Craige