1// BEGIN LICENSE BLOCK 2// Version: CMPL 1.1 3// 4// The contents of this file are subject to the Cisco-style Mozilla Public 5// License Version 1.1 (the "License"); you may not use this file except 6// in compliance with the License. You may obtain a copy of the License 7// at www.eclipse-clp.org/license. 8// 9// Software distributed under the License is distributed on an "AS IS" 10// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 11// the License for the specific language governing rights and limitations 12// under the License. 13// 14// The Original Code is The ECLiPSe Constraint Logic Programming System. 15// The Initial Developer of the Original Code is Cisco Systems, Inc. 16// Portions created by the Initial Developer are 17// Copyright (C) 2006 Cisco Systems, Inc. All Rights Reserved. 18// 19// Contributor(s): 20// 21// END LICENSE BLOCK 22 23package com.parctechnologies.eclipse.visualisation.viewers; 24 25import com.parctechnologies.eclipse.*; 26import com.parctechnologies.eclipse.visualisation.*; 27 28import java.awt.Rectangle; 29import java.awt.Shape; 30import java.awt.geom.*; 31import java.awt.Color; 32import java.awt.Component; 33import java.awt.Graphics; 34import java.awt.Graphics2D; 35import java.util.*; 36import java.awt.event.ActionEvent; 37import javax.swing.*; 38import javax.swing.table.*; 39import att.grappa.*; 40 41 42/** 43 * Displays a graphical representation of the numeric range, as 44 * reported by get_bounds/3 45 **/ 46public class BoundsViewletType extends AbstractViewletType { 47 private TableCellRenderer tableCellRenderer; 48 private CustomRenderer customRenderer; 49 50 public BoundsViewletType(String changeable) { 51 super(changeable); 52 } 53 54 55 /* ViewletFactory methods */ 56 public boolean canBuildFrom(ElementType elementType) 57 { 58 //return(elementType instanceof NumericBounds); 59 return true; 60 } 61 62 public ViewletData build() 63 { 64 return new Data(); 65 } 66 67 public String getDescription() 68 { 69 return("Bounds viewlet"); 70 } 71 72 /* ViewletType methods */ 73 public synchronized TableCellRenderer getTableCellRenderer() { 74 if (tableCellRenderer == null) { 75 tableCellRenderer = new CellRenderer(); 76 } 77 return tableCellRenderer; 78 } 79 80 public Class getCustomRendererClass() { 81 return Renderer.class; 82 } 83 84 public void customizeElement(ViewletDataStore store, 85 java.util.List index, 86 Element element) { 87 Data data = (Data)(store.getViewletDataAt(index)); 88 if (element instanceof Node) { 89 if (DebuggingSupport.logMessages) { 90 DebuggingSupport.logMessage(this,"Bounds customizeElement for element "+element + " step 1"); 91 } 92 // set the custom renderer 93 element.setAttribute("shape",new Integer(Grappa.CUSTOM_SHAPE)); 94 element.setAttribute(Grappa.CUSTOM_ATTR,getCustomRendererClass().getName()); 95 if (DebuggingSupport.logMessages) { 96 DebuggingSupport.logMessage(this,"Bounds customizeElement for element "+element + " step 2"); 97 } 98 // set the node label 99 element.setAttribute("label",""); 100 if (DebuggingSupport.logMessages) { 101 DebuggingSupport.logMessage(this,"Bounds customizeElement for element "+element + " step 3"); 102 } 103 // set background color 104 if (data.getHoldsOnUpdates()) { 105 element.setAttribute("color", Color.blue.darker()); 106 } else { 107 element.setAttribute("color", Color.blue); 108 } 109 if (DebuggingSupport.logMessages) { 110 DebuggingSupport.logMessage(this,"Bounds customizeElement for element "+element + " step 4"); 111 } 112 // set filled 113 element.setAttribute("style", "filled"); 114 if (DebuggingSupport.logMessages) { 115 DebuggingSupport.logMessage(this,"Bounds customizeElement for element "+element + " step 5"); 116 } 117 // force shape update 118 element.object = data; 119 element.getGrappaNexus().updateShape(); 120 if (DebuggingSupport.logMessages) { 121 DebuggingSupport.logMessage(this,"Bounds customizeElement for element "+element + " step 6"); 122 } 123 } else { 124 // instance of edge 125 } 126 } 127 128 129 public BatchGoal collectPreBuildGoal(Viewer viewer, 130 ViewletDataStore store, 131 ViewletRange range) 132 { 133 BatchGoal result = new BatchGoal(); 134 Iterator indexListIterator = range.iterator(); 135 java.util.List currentIndex; 136 Collection viewlets; 137 138 while(indexListIterator.hasNext()) 139 { 140 currentIndex = (java.util.List) indexListIterator.next(); 141 CompoundTerm goal = 142 new CompoundTermImpl("get_var_bounds", new CompoundTermImpl("element",currentIndex), null, null); 143 144// CompoundTerm goal = 145// new CompoundTermImpl(":", new Atom("vc_support"), 146 147// new CompoundTermImpl("viewable_element_to_string", 148// new CompoundTermImpl("element",currentIndex), null)); 149 result.add(composeElementGoal(currentIndex, store.getViewable().getNameAtom(), goal)); 150 } 151 return(result); 152 } 153 154 protected void processResults(Viewer viewer, 155 ViewletDataStore store, 156 ViewletRange range, 157 List results, 158 boolean isBuildResults) { 159 Iterator indexListIterator = range.iterator(); 160 Iterator resultsIterator = results.iterator(); 161 CompoundTermImpl elementResult; 162 List currentIndex; 163 if (DebuggingSupport.logMessages) { 164 DebuggingSupport.logMessage(this, 165 "startBuild called with range=" + 166 range + " results=" + results); 167 } 168 169 while(indexListIterator.hasNext()) { 170 currentIndex = (List) indexListIterator.next(); 171 if (DebuggingSupport.logMessages) { 172 DebuggingSupport.logMessage(this, "currentIndex="+currentIndex); 173 } 174 Data viewletData = 175 (Data)(store.getViewletDataAt(currentIndex)); 176 if (DebuggingSupport.logMessages) { 177 DebuggingSupport.logMessage(this, "viewletData="+viewletData); 178 } 179 elementResult = (CompoundTermImpl) resultsIterator.next(); 180 if (DebuggingSupport.logMessages) { 181 DebuggingSupport.logMessage(this, "elementResult="+elementResult); 182 } 183 // store the goal result text into the viewletDataStore 184 Double lowerBound = (Double)decomposeElementGoal(elementResult).arg(2); 185 Double upperBound = (Double)decomposeElementGoal(elementResult).arg(3); 186 if (viewletData == null) { 187 viewletData = (Data)build(); 188 } 189 if (isBuildResults) { 190 viewletData.setAbsoluteBounds(lowerBound.doubleValue(), upperBound.doubleValue()); 191 viewletData.setInitialBounds(lowerBound.doubleValue(), upperBound.doubleValue()); 192 } 193 viewletData.setCurrentBounds(lowerBound.doubleValue(), upperBound.doubleValue()); 194 store.setViewletDataAt(currentIndex, viewletData); 195 } 196 } 197 198 public void startBuild(Viewer viewer, 199 ViewletDataStore store, 200 ViewletRange range, 201 List results) { 202 processResults(viewer, store, range, results, true); 203 } 204 205 public BatchGoal collectPreUpdateGoal(Viewer viewer, 206 ViewletDataStore store, 207 ViewletRange range, 208 UpdateEvent updateEvent) 209 { 210 BatchGoal result = new BatchGoal(); 211 Iterator indexListIterator = range.iterator(); 212 java.util.List currentIndex; 213 Collection viewlets; 214 215 while(indexListIterator.hasNext()) 216 { 217 currentIndex = (java.util.List) indexListIterator.next(); 218 CompoundTerm goal = 219 new CompoundTermImpl("get_var_bounds", new CompoundTermImpl("element",currentIndex), null, null); 220 221// CompoundTerm goal = 222// new CompoundTermImpl(":", new Atom("vc_support"), 223// new CompoundTermImpl("viewable_element_to_string", 224// new CompoundTermImpl("element",currentIndex), null)); 225 result.add(composeElementGoal(currentIndex, store.getViewable().getNameAtom(), goal)); 226 } 227 return(result); 228 } 229 230 protected void setUpdating(Viewer viewer, 231 ViewletDataStore store, 232 ViewletRange range, 233 int fadeCount) { 234 Iterator indexListIterator = range.iterator(); 235 List currentIndex; 236 while(indexListIterator.hasNext()) { 237 currentIndex = (List) indexListIterator.next(); 238 if (DebuggingSupport.logMessages) { 239 DebuggingSupport.logMessage(this, "currentIndex="+currentIndex); 240 } 241 Data viewletData = 242 (Data)(store.getViewletDataAt(currentIndex)); 243 if (DebuggingSupport.logMessages) { 244 DebuggingSupport.logMessage(this, "viewletData="+viewletData); 245 } 246 // Set the updating flag and store the new data 247 if (viewletData == null) { 248 viewletData = (Data)build(); 249 } 250 //viewletData.setFadeCount(fadeCount); 251 store.setViewletDataAt(currentIndex, viewletData); 252 } 253 } 254 255 public void startUpdate(Viewer viewer, 256 ViewletDataStore store, 257 ViewletRange range, 258 List results, 259 UpdateEvent updateEvent) 260 { 261 processResults(viewer, store, range, results, false); 262 // do the same as for building a viewlet 263 //startBuild(viewer, store, range, results); 264 // set the fade counter 265 //setUpdating(viewer, store, range,((updateEvent instanceof ForwardUpdateEvent)?MAX_FADE:-MAX_FADE)); 266 267// ViewletRange all = store.getEntireViewletRange() ; 268// ViewletRange faded = new ViewletRangeCollection(); 269// for(Iterator it = all.iterator(); it.hasNext(); ) { 270// List index = (List)it.next(); 271// Data data = (Data)(store.getViewletDataAt(index)); 272// if (data.fade()) { 273// faded.add(index); 274// } 275// } 276 // Indicate that these cells were updated 277 store.fireViewletRangeUpdated(range); 278 // store.fireViewletRangeUpdated(range); 279 } 280 281 /** 282 * For the given index, return the smallest pertinent value 283 **/ 284 public double getMin(ViewletDataStore store, List index) { 285 return ((Data)(store.getViewletDataAt(index))).absoluteMin; 286 } 287 288 /** 289 * For the given index, return the largest pertinent value 290 **/ 291 public double getMax(ViewletDataStore store, List index) { 292 return ((Data)(store.getViewletDataAt(index))).absoluteMax; 293 } 294 295 /* 296 * Data is a viewlet which can monitor elements of any type. It is 297 * responsible for: 298 * <ul> 299 * <li> Maintaining a record of the text representation of the term. 300 * </ul> 301 */ 302 public static class Data extends ViewletDataImpl 303 { 304 double absoluteMin, absoluteMax; 305 double initialMin, initialMax; 306 double min, max; 307 boolean vertical; 308 309 public Data() 310 { 311 super(); 312 } 313 314 public String toString() { 315 return initialMin+".."+min+".."+max+".."+initialMax; 316 } 317 318 public void setAbsoluteBounds(double min, double max) { 319 absoluteMin = min; 320 absoluteMax = max; 321 } 322 323 public void setInitialBounds(double min, double max) { 324 initialMin = min; 325 initialMax = max; 326 } 327 328 public void setCurrentBounds(double min, double max) { 329 this.min = min; 330 this.max = max; 331 } 332 } 333 334 335 /** 336 * Return a collection of actions which can be applied to viewlets 337 * in this table 338 */ 339 public Collection getActions(ViewletDataStore store, 340 ViewletRange range) { 341 Collection ll = super.getActions(store, range); 342 if ((range != null) & (!range.isEmpty())) { 343 // Add new actions here 344 ll.add(new AlignBoundsAction(store, range)); 345 ll.add(new ToggleHorizontalVerticalAction(store, range)); 346 } 347 if ((range != null) & (range.size()==1)) { 348 java.util.List index = (java.util.List)(range.iterator().next()); 349 // Add new actions here which apply only to single viewlets 350 ll.add(new DisplayBoundsInDetailAction(store, index)); 351 } 352 return ll; 353 } 354 355 protected void allignBounds(ViewletDataStore store, 356 ViewletRange range) { 357 double newMin = Double.POSITIVE_INFINITY; 358 double newMax = Double.NEGATIVE_INFINITY; 359 360 for(Iterator iterator = range.iterator(); iterator.hasNext(); ) { 361 List index = (List) iterator.next(); 362 Data viewlet = (Data)(store.getViewletDataAt(index)); 363 if(viewlet.initialMin < newMin) { 364 newMin = viewlet.initialMin; 365 } 366 if(viewlet.initialMax > newMax) { 367 newMax = viewlet.initialMax; 368 } 369 } 370 371 for(Iterator iterator = range.iterator(); iterator.hasNext(); ) { 372 List index = (List) iterator.next(); 373 Data viewlet = (Data)(store.getViewletDataAt(index)); 374 viewlet.setAbsoluteBounds(newMin, newMax); 375 store.setViewletDataAt(index, viewlet); 376 } 377 store.fireViewletRangeUpdated(range); 378 } 379 380 /** 381 * Recordable command to perform the bounds allignment 382 */ 383 public static class AlignBoundsCommand extends ViewletTypeRangeCommand { 384 public AlignBoundsCommand(ViewletType type, 385 ViewletDataStore store, 386 ViewletRange range) { 387 super(type, store, range); 388 } 389 390 public void postRecordIssue() { 391 if (DebuggingSupport.logMessages) { 392 DebuggingSupport.logMessage(this, "AlignBoundCommand postRecordIssue invoked with type="+getViewletType()+" store="+getViewletDataStore()+" range="+getViewletRange()); 393 } 394 ((BoundsViewletType)getViewletType()). 395 allignBounds(getViewletDataStore(), getViewletRange()); 396 } 397 } 398 399 /** 400 * Action class to set a range of viewables to have common 401 * AbsoluteMin and AbsolutMax 402 **/ 403 protected class AlignBoundsAction extends ViewletAction { 404 ViewletRange range; 405 ViewletDataStore store; 406 407 AlignBoundsAction(ViewletDataStore store, ViewletRange range) { 408 super("Align bounds"); 409 putValue(Action.NAME, "Align bounds"); 410 putValue(Action.LONG_DESCRIPTION, 411 "Re-align a selection of bounds viewlets so that their bounds are displayed on the same axes"); 412 putValue(Action.SHORT_DESCRIPTION, 413 "Re-align so that bounds viewlets use the same axes"); 414 //putValue(Action.SMALL_ICON, new FadeIcon(20, 20)); 415 this.store = store; 416 this.range = range; 417 } 418 419 public void actionPerformed(ActionEvent e) { 420 new AlignBoundsCommand(BoundsViewletType.this, store, range).issue(); 421 } 422 } 423 424 425 protected void toggleHorizontalVertical(ViewletDataStore store, 426 ViewletRange range) { 427 boolean allHorizontal = true; 428 429 for(Iterator iterator = range.iterator(); iterator.hasNext(); ) { 430 List index = (List) iterator.next(); 431 Data data = (Data)(store.getViewletDataAt(index)); 432 if(data.vertical) { 433 allHorizontal = false; 434 } 435 } 436 // if all the viewlets are horizontal then set them all 437 // vertical, otherwise set them all horizontal 438 for(Iterator iterator = range.iterator(); iterator.hasNext(); ) { 439 List index = (List) iterator.next(); 440 Data data = (Data)(store.getViewletDataAt(index)); 441 data.vertical = allHorizontal; 442 store.setViewletDataAt(index, data); 443 } 444 store.fireViewletRangeUpdated(range); 445 } 446 447 /** 448 * Recordable command to perform the bounds allignment 449 */ 450 public static class ToggleHorizontalVerticalCommand extends ViewletTypeRangeCommand { 451 public ToggleHorizontalVerticalCommand(ViewletType type, 452 ViewletDataStore store, 453 ViewletRange range) { 454 super(type, store, range); 455 } 456 457 public void postRecordIssue() { 458 if (DebuggingSupport.logMessages) { 459 DebuggingSupport.logMessage(this, "AlignBoundCommand postRecordIssue invoked with type="+getViewletType()+" store="+getViewletDataStore()+" range="+getViewletRange()); 460 } 461 ((BoundsViewletType)getViewletType()). 462 toggleHorizontalVertical(getViewletDataStore(), getViewletRange()); 463 } 464 } 465 466 467 /** 468 * Action class to set a range of viewables to have common 469 * AbsoluteMin and AbsolutMax 470 **/ 471 protected class ToggleHorizontalVerticalAction extends ViewletAction { 472 ViewletRange range; 473 ViewletDataStore store; 474 475 ToggleHorizontalVerticalAction(ViewletDataStore store, 476 ViewletRange range) { 477 super("Toggle horizontal/vertical range bar"); 478 putValue(Action.NAME, "Toggle horizontal/vertical range bar"); 479 putValue(Action.LONG_DESCRIPTION, 480 "Change whether the range bar is displayed horizontally or vertically"); 481 putValue(Action.SHORT_DESCRIPTION, 482 "Change whether the range bar is horizontal or vertical"); 483 this.store = store; 484 this.range = range; 485 } 486 487 public void actionPerformed(ActionEvent e) { 488 new ToggleHorizontalVerticalCommand(BoundsViewletType.this, 489 store, 490 range).issue(); 491 } 492 } 493 494 /** 495 * Action class to display the bounds in detail in a popup window 496 **/ 497 protected class DisplayBoundsInDetailAction extends ViewletAction { 498 List index; 499 ViewletDataStore store; 500 501 DisplayBoundsInDetailAction(ViewletDataStore store, 502 java.util.List index) { 503 super("Display bounds"); 504 putValue(Action.NAME, "Display bounds"); 505 putValue(Action.LONG_DESCRIPTION, 506 "Popup window displaying bounds in detail"); 507 putValue(Action.SHORT_DESCRIPTION, 508 "Popup window displaying bounds in detail"); 509 //putValue(Action.SMALL_ICON, new FadeIcon(20, 20)); 510 this.store = store; 511 this.index = index; 512 } 513 514 public void actionPerformed(ActionEvent e) { 515 Data data = (Data)(store.getViewletDataAt(index)); 516 JOptionPane. 517 showConfirmDialog(null, 518 "Initial upper bound: "+data.initialMax+"\n\n"+ 519 "Current upper bound: "+data.max+"\n\n"+ 520 "Current lower bound: "+data.min+"\n\n"+ 521 "Initial lower bound: "+data.initialMin, 522 "Bounds data in detail", 523 JOptionPane.DEFAULT_OPTION); 524 } 525 } 526 527 /** 528 * The default text cell render 529 */ 530 private class CellRenderer extends DefaultTableCellRenderer { 531 532 public CellRenderer() { 533 super(); 534 setHorizontalAlignment(SwingConstants.CENTER); 535 } 536 537 public Component getTableCellRendererComponent(JTable table, 538 Object value, 539 boolean isSelected, 540 boolean hasFocus, 541 int row, 542 int column) { 543 544 JLabel result ; 545 Rectangle bounds ; 546 if (table == null) { 547 result = new JLabel(); 548 bounds = result.getBounds(); 549 } else { 550 result = 551 (JLabel)(super.getTableCellRendererComponent(table, 552 "", 553 isSelected, 554 hasFocus, 555 row, 556 column)); 557 bounds = table.getCellRect(row, column, false); 558 } 559 Data data = (Data)value; 560 561 562 Graphics2D g2d = (Graphics2D)result.getGraphics(); 563 Element element = new Graph("bar"); 564 element.object = null; 565 Renderer renderer = new Renderer(element, 566 0,0, 567 1,1); 568 // set background color 569 if (data.getHoldsOnUpdates()) { 570 if (isSelected) { 571 result.setBackground(Color.white.darker().darker().darker()); 572 } else { 573 result.setBackground(Color.white.darker()); 574 } 575 } else { 576 if (isSelected) { 577 result.setBackground(Color.white.darker().darker()); 578 } else { 579 result.setBackground(Color.white); 580 } 581 } 582 result.setIcon(new RendererIcon(renderer,data,bounds)); 583 //result.setBackground(getColor(data, isSelected)); 584// if (data.getFading()) { 585// result.setIcon(fadeIcon); 586// } else { 587// result.setIcon(null); 588// } 589 return result; 590 } 591 592 public void setValue(Object value) { 593 super.setValue(value); 594 setToolTipText(value.toString()); 595 } 596 597 } 598 599 public static class RendererIcon implements Icon { 600 Renderer renderer; 601 Data data; 602 Rectangle bounds; 603 604 public RendererIcon(Renderer renderer, 605 Data data, 606 Rectangle parentBounds) { 607 this.renderer = renderer; 608 this.data = data; 609 int w = (parentBounds.width * 9) / 10; 610 int h = (parentBounds.height * 9) / 10; 611 int x = (parentBounds.width * 1) / 20; 612 int y = (parentBounds.height * 1) / 20; 613 this.bounds = new Rectangle(x,y,w,h); 614 } 615 616 public int getIconHeight() { 617 // nominal height 618 return bounds.height; 619 } 620 public int getIconWidth() { 621 // nominal width 622 return bounds.width; 623 } 624 public void paintIcon(Component c, 625 Graphics g, 626 int x, 627 int y) { 628 //Data data = renderer.getViewletData(); 629 if (DebuggingSupport.logMessages) { 630 DebuggingSupport.logMessage(this, "Clip = "+g.getClip().getBounds2D()+" bounds="+bounds+" (x,y)=("+x+","+y+")"); 631 } 632 //renderer.configure(new Rectangle2D.Double(x,y,c.getWidth(),c.getHeight()), 633 //renderer.configure(g.getClip().getBounds2D(), 634 renderer.configure(bounds, 635 data.absoluteMin, 636 data.absoluteMax, 637 data.initialMin, 638 data.initialMax, 639 data.min, 640 data.max, 641 data.vertical); 642 g.setColor(Color.black); 643 renderer.draw((Graphics2D)g); 644 g.setColor(Color.blue); 645 renderer.fill((Graphics2D)g); 646 } 647 } 648 649 650 public static class Renderer extends CustRenderer { 651 public Renderer(Element element, 652 double x, double y, double w, double h) { 653 super(element, x, y, w, h); 654 if (DebuggingSupport.logMessages) { 655 DebuggingSupport.logMessage(this,"Bounds Renderer constructed for element "+element); 656 } 657 // somehow work out a way of getting the actual 658 // ViewletData for this element (must work when the 659 // viewlettype is contained in a multiViewlet type) 660 //Data data = (Data)element.object; 661 Data data = (Data)getViewletData(); 662 if (data != null) { 663 configure(new Rectangle2D.Double(x,y,w,h), 664 data.absoluteMin, 665 data.absoluteMax, 666 data.initialMin, 667 data.initialMax, 668 data.min, 669 data.max, 670 data.vertical); 671 } 672 } 673 674 public void configure(Rectangle2D bounds, 675 double absoluteMin, double absoluteMax, 676 double initialMin, double initialMax, 677 double min, double max, boolean vertical) { 678 // draw a "box and stick" diagram within the specified bounds 679 // 680 // G +-- +-------+ --+ 681 // | | | | 682 // H | | | | 683 // I1 |----| |-----| 684 // I | | 685 // I2 |----| |-----| 686 // J | | | | 687 // | | | | 688 // K +-- +-------+ --+ 689 // A B C D E F 690 // 691 //Rectangle2D bounds = getBounds2D(); 692 float A,B,C,D,E,F,G,H,I,I1,I2,J,K; 693 if (vertical) { 694 A = (float)bounds.getMinY(); 695 F = (float)bounds.getMaxY(); 696 } else { 697 A = (float)bounds.getMinX(); 698 F = (float)bounds.getMaxX(); 699 } 700 // remove potential for divide by zero 701 if (absoluteMax == absoluteMin) { 702 absoluteMax++; 703 } 704 // calculate scale for drawing 705 float scaleX = (F-A) / (float)(absoluteMax - absoluteMin); 706 B = A + (float)initialMin * scaleX; 707 C = A + (float)min * scaleX; 708 D = A + (float)max * scaleX; 709 E = A + (float)initialMax * scaleX; 710 711 if (vertical) { 712 G = (float)bounds.getMinX(); 713 K = (float)bounds.getMaxX(); 714 } else { 715 G = (float)bounds.getMinY(); 716 K = (float)bounds.getMaxY(); 717 } 718 float sixteenth = (K-G)/16; 719 I = G+8*sixteenth; 720 721 H = I - 4*sixteenth; 722 I1 = I - 1*sixteenth; 723 I2 = I + 1*sixteenth; 724 J = I + 4*sixteenth; 725 726 // draw outline 727 float vertices[][]; 728 729 if (vertical) { 730 float vert[][] = {{H,B},{I1,B},{I1,C},{G,C},{G,D},{I1,D},{I1,E}, 731 {H,E},{J,E},{I2,E},{I2,D},{K,D},{K,C},{I2,C}, 732 {I2,B},{J,B}}; 733 vertices = vert; 734 } else { 735 float horiz[][] = {{B,H},{B,I1},{C,I1},{C,G},{D,G},{D,I1},{E,I1}, 736 {E,H},{E,J},{E,I2},{D,I2},{D,K},{C,K},{C,I2}, 737 {B,I2},{B,J}}; 738 vertices = horiz; 739 } 740 path.moveTo(vertices[0][0], vertices[0][1]); 741 for(int i = 1; i < vertices.length; i++) { 742 if (DebuggingSupport.logMessages) { 743 DebuggingSupport.logMessage(this,"Bounds point "+i+ 744 " x="+vertices[i][0]+ 745 " y="+vertices[i][1]); 746 } 747 path.lineTo(vertices[i][0], vertices[i][1]); 748 } 749 path.closePath(); 750 } 751 } 752 753} 754 755