1 /* 2 * Steven Herbst 3 * AP Physics 2004 - 2005 4 * Thomas Jefferson HS for Science and Technology 5 * Instructor: Dr. John Dell 6 * 7 * Software licensed under GNU General Public License (GPL) 8 * See http://www.gnu.org/copyleft/gpl.html for details 9 */ 10 import java.util.*; 11 import java.text.DecimalFormat; 12 13 // Grid Constants 14 int TICK_SCALE = 10; // Relative spacing of tick marks 15 int TICK_LENGTH = 2; 16 int gridX = 75; 17 int gridY = 75; 18 int translateX, translateY; 19 // Point Constants 20 int DOT_RADIUS = 1; 21 float FLOAT_ERROR = 1e-10; 22 DecimalFormat displayFormat; 23 // Line Constants 24 int START_LINE = 1; 25 // Text Constants 26 int TEXT_HEIGHT = 8; 27 int TEXT_WIDTH = 8; 28 int TEXT_DISPLACEMENT = 40; 29 int TRANSFORM_DISPLACEMENT = 60; 30 // Menu Constants 31 int minimumTime = 500; // Menu selection considered invalid if menu is open for less than minimumTime 32 // Hover Constants 33 float LINE_DIST_MARGIN = 4.0; 34 float POINT_DIST_MARGIN = 5.0; 35 36 // Global variables 37 // Relativity 38 float beta; 39 float gamma; 40 float[][] transformMatrix, inverseMatrix; 41 // Display 42 float zoom = 1; 43 Point points, transform, lineStart, transformLineStart; 44 // Frames 45 State states, sentinel; 46 // Mouse 47 boolean dragged; // Used to determine context of mouse release 48 // Menu 49 Menu menu; 50 int menuTime; // Time when menu was opened 51 boolean pointAdded; // Used to determine if menu should open 52 // I/O 53 boolean saveFrame; 54 //GUI 55 LinkedList items; 56 // Labels 57 Label info; 58 Label graphLabel, transformLabel; 59 Label lineLabel, transformLineLabel; 60 // Buttons 61 Button layerButtons[]; // Contains the 4 buttons that allow user to navigate frames 62 // Sliders 63 Slider pslider, sslider, xslider, yslider, zslider; 64 65 /* Class GUI_Item 66 * Subclasses: 67 * Label 68 * Menu 69 * MenuItem 70 * Button 71 * Slider 72 */ 73 abstract class GUI_Item{ 74 abstract void display(); 75 abstract void update(); 76 } 77 class Label extends GUI_Item{ 78 int x, y; 79 String text; 80 Label(String text, int x, int y){ 81 this.x = x; 82 this.y = y; 83 this.text = text; 84 items.add(this); 85 } 86 void display(){ 87 display(0, 0, 0); 88 } 89 void display(int r, int g, int b){ 90 fill(r, g, b); 91 text(text, x, y - TEXT_HEIGHT/ 2); 92 } 93 void update(){ 94 } 95 void setText(String text){ 96 this.text = text; 97 } 98 String getText(){ 99 return text; 100 } 101 } 102 class MenuItem extends GUI_Item{ 103 String text; 104 int x, y; 105 int width, height; 106 static final int errorX = 5; 107 static final int bufferX = 5; 108 static final int bufferY = 10; 109 boolean selected = false; 110 Label myLabel; 111 MenuItem(String text, int x, int y){ 112 this.text = text; 113 this.x = x; 114 this.y = y; 115 myLabel = new Label(text, x + bufferX, y + bufferY + TEXT_HEIGHT); 116 width = 2 * bufferX + text.length() * TEXT_WIDTH; 117 height = 2 * bufferY + TEXT_HEIGHT; 118 } 119 boolean over(){ 120 return x - errorX <= mouseX && 121 mouseX <= x + width + errorX && 122 y <= mouseY && 123 mouseY <= y + height; 124 } 125 void update(){ 126 if (over()){ 127 selected = true; 128 } 129 else{ 130 selected = false; 131 } 132 } 133 void display(){ 134 if (selected){ 135 fill(200, 200, 200); 136 } 137 else{ 138 fill(225, 225, 225); 139 } 140 stroke(0, 0, 0); 141 rect(x, y, width, height); 142 myLabel.display(); 143 } 144 } 145 class Menu extends GUI_Item{ 146 LinkedList menuItems; 147 MenuItem selected; 148 int urx, ury, maxWidth; 149 Menu(int urx, int ury){ 150 this.urx = urx; 151 this.ury = ury; 152 menuItems = new LinkedList(); 153 } 154 void addMenuItem(String text){ 155 MenuItem newItem = new MenuItem(text, urx, ury + menuItems.size() * (2 * MenuItem.bufferY + TEXT_HEIGHT)); 156 if (maxWidth < newItem.width){ 157 maxWidth = newItem.width; 158 } 159 menuItems.add(newItem); 160 } 161 void update(){ 162 selected = null; 163 Iterator it = menuItems.iterator(); 164 MenuItem tmp; 165 while(it.hasNext()){ 166 tmp = (MenuItem)it.next(); 167 tmp.width = maxWidth; 168 tmp.update(); 169 if (tmp.selected){ 170 selected = tmp; 171 } 172 } 173 } 174 void display(){ 175 Iterator it = menuItems.iterator(); 176 while(it.hasNext()){ 177 ((GUI_Item)it.next()).display(); 178 } 179 } 180 void clear(){ 181 Iterator it = menuItems.iterator(); 182 MenuItem tmp; 183 while(it.hasNext()){ 184 tmp = (MenuItem)it.next(); 185 items.remove(tmp.myLabel); 186 it.remove(); 187 } 188 } 189 String getSelected(){ 190 if (selected != null){ 191 return selected.text; 192 } 193 else{ 194 return null; 195 } 196 } 197 } 198 class Button extends GUI_Item{ 199 int x, y, width, height; 200 Label myLabel; 201 boolean pressed, enabled = true; 202 static final int errorMarginX = 0; 203 static final int errorMarginY = 5; 204 static final int depth = 1; 205 static final int padding = 3; 206 Button(String text, int x, int y){ 207 this.x = x; 208 this.y = y; 209 this.width = text.length() * TEXT_WIDTH; 210 this.height = TEXT_HEIGHT + 2 * padding; 211 myLabel = new Label(text, x - width/ 2, y + padding); 212 items.add(this); 213 } 214 Button(String text, int x, int y, int width, int height){ 215 this.x = x; 216 this.y = y; 217 this.width = width; 218 this.height = height; 219 myLabel = new Label(text, x - width/ 2, y); 220 items.add(this); 221 } 222 boolean over(){ 223 return (x - width/ 2 - errorMarginX) <= mouseX && 224 mouseX <= (x + width/ 2 + errorMarginX) && 225 (y - height/ 2 - errorMarginY) <= mouseY && 226 mouseY <= (y + height/ 2 + errorMarginY) ; 227 } 228 void update(){ 229 if (mousePressed && over()){ 230 if (enabled){ 231 pressed = true; 232 } 233 pointAdded = true; 234 } 235 } 236 void display(){ 237 if (enabled){ 238 fill(225, 225, 225); 239 } 240 else{ 241 fill(200, 200, 200); 242 } 243 if (pressed && mousePressed && over()){ 244 rect(x - width/ 2 + depth, y - width/ 2 - depth, width, height); 245 myLabel.x = x - width/ 2 + depth; 246 myLabel.y = y - depth + TEXT_HEIGHT + padding; 247 myLabel.display(); 248 } 249 else{ 250 rect(x - width/ 2, y - width/ 2, width, height); 251 myLabel.x = x - width/ 2; 252 myLabel.y = y + TEXT_HEIGHT + padding; 253 myLabel.display(); 254 } 255 } 256 void reset(){ 257 pressed = false; 258 } 259 void setEnabled(boolean choice){ 260 if (choice){ 261 enabled = true; 262 } 263 else{ 264 enabled = false; 265 } 266 } 267 } 268 class Slider extends GUI_Item{ 269 int x, y, width, height, pos, center, displacement; 270 Label myLabel; 271 static final int errorMarginX = 5; 272 static final int errorMarginY = 15; 273 Slider(int x, int y, int width, int height, int displacement){ 274 this.x = x; 275 this.center = x; 276 this.y = y; 277 this.width = width; 278 this.height = height; 279 this.displacement = displacement; 280 pos = 0; 281 items.add(this); 282 } 283 Slider(int x, int y, int width, int height, int displacement, String text){ 284 this.x = x; 285 this.center = x; 286 this.y = y; 287 this.width = width; 288 this.height = height; 289 this.displacement = displacement; 290 pos = 0; 291 myLabel = new Label(text, x - displacement, y - height); 292 items.add(this); 293 } 294 boolean over(){ 295 return (center - displacement - errorMarginX) <= mouseX && 296 mouseX <= (center + displacement + errorMarginX) && 297 (y - height/ 2 - errorMarginY) <= mouseY && 298 mouseY <= (y + height/ 2 + errorMarginY) ; 299 } 300 void update(){ 301 if (mousePressed && over()){ 302 pointAdded = true; 303 if(abs(mouseX - center) < displacement){ 304 x = mouseX; 305 } 306 else if (mouseX < center){ 307 x = center - displacement; 308 } 309 else{ 310 x = center + displacement; 311 } 312 } 313 if (abs(x - center) < 5){ 314 x = center; 315 } 316 } 317 void display(){ 318 stroke(0, 0, 0); 319 fill(0, 0, 0); 320 line(center - displacement, y, center + displacement, y); 321 line(center - displacement, y - 5, center - displacement, y + 5); 322 line(center, y - 5, center, y + 5); 323 line(center + displacement, y - 5, center + displacement, y + 5); 324 rect(x - width/ 2, y - height/ 2, width, height); 325 if (myLabel != null){ 326 myLabel.display(); 327 } 328 } 329 float getStatus(){ 330 return 1.0 * (x - center)/ displacement; 331 } 332 void setStatus(float s){ 333 if (abs(s) <= displacement){ 334 x = (int)(displacement * s + center); 335 } 336 } 337 } 338 class Point{ 339 float x, y, z; 340 float t; 341 Point next; 342 int condition; 343 Point(){ 344 } 345 Point(float x, float t){ 346 this.x = x; 347 this.t = t; 348 } 349 Point(float x, float y, float t){ 350 this.x = x; 351 this.y = y; 352 this.t = t; 353 } 354 Point(Point p){ 355 this.x = p.x; 356 this.y = p.y; 357 this.t = p.t; 358 this.next = p.next; 359 this.condition = p.condition; 360 } 361 String toString(){ 362 return "(" + displayFormat.format(x) + ", " + displayFormat.format(t) + ")"; 363 } 364 } 365 class State{ 366 State prev, next; 367 float beta, zoom, thetaX, thetaY, thetaZ; 368 Point points; 369 State(Point points){ 370 this.points = points; 371 } 372 State(Point points, float beta, float zoom){ 373 this.points = points; 374 this.beta = beta; 375 this.zoom = zoom; 376 } 377 void append(State s){ 378 State tmpNode = next; 379 tmpNode.prev = s; 380 s.next = tmpNode; 381 s.prev = this; 382 this.next = s; 383 } 384 } 385 386 void setup(){ 387 size(600, 600, P3D); 388 textFont(loadFont("CourierNew36.vlw"), 12); 389 dragged = false; 390 pointAdded = false; 391 saveFrame = false; 392 translateX = 0; 393 translateY = 0; 394 points = null; 395 transform = null; 396 lineStart = null; 397 transformLineStart = null; 398 items = new LinkedList(); 399 layerButtons = new Button[4]; 400 sentinel = new State(null); 401 pslider = new Slider(3 * width/ 4, height/ 8, 10, 10, 50, "Beta:"); 402 sslider = new Slider(3 * width/ 4, height/ 4, 10, 10, 50, "Scale:"); 403 layerButtons[0] = new Button("<", width/ 4 - gridX, height/ 4 + gridY + 25); 404 layerButtons[1] = new Button(">", width/ 4 - gridX + 10, height/ 4 + gridY + 25); 405 layerButtons[2] = new Button("<", 3 * width/ 4 - gridX, 3 * height/ 4 + gridY + 25); 406 layerButtons[3] = new Button(">", 3 * width/ 4 - gridX + 10, 3 * height/ 4 + gridY + 25); 407 initMatrices(); 408 initStates(); 409 graphLabel = new Label("",3 * width/ 16, height/ 4 + gridY + 2 * TEXT_HEIGHT); 410 transformLabel = new Label("", 11 * width/ 16, 3 * height/ 4 + gridY + 2 * TEXT_HEIGHT); 411 lineLabel = new Label("", 3 * width/ 16, height/ 4 + gridY + 5 * TEXT_HEIGHT); 412 transformLineLabel = new Label("", 11 * width/ 16, 3 * height/ 4 + gridY + 5 * TEXT_HEIGHT); 413 info = new Label("", 8 * TEXT_WIDTH, height/ 2); 414 displayFormat = new DecimalFormat("#.##"); 415 framerate(30); 416 } 417 void draw(){ 418 pointAdded = false; 419 translateX = 0; 420 translateY = 0; 421 if (menu != null){ 422 menu.update(); 423 menu.display(); 424 } 425 else{ 426 background(255, 255, 255); 427 displayAll(); 428 beta = pslider.getStatus(); 429 zoom = pow(10, sslider.getStatus()); 430 setTranslate(width/ 4, height/ 4); 431 drawGrid(); 432 plotPoints(points); 433 boolean pointSelected = false; 434 Point p = closestPoint(points, getMouseCoords()); 435 if (p != null){ 436 graphLabel.setText("" + p); 437 transformLabel.setText("" + doTransform(p)); 438 pointSelected = true; 439 } 440 setTranslate(3 * width/ 4, 3 * height/ 4); 441 drawGrid(); 442 updateTransform(); 443 transformPoints(); 444 plotPoints(transform); 445 p = closestPoint(transform, getMouseCoords()); 446 if (p != null){ 447 graphLabel.setText("" + reverseTransform(p)); 448 transformLabel.setText("" + p); 449 pointSelected = true; 450 } 451 if (!pointSelected){ 452 graphLabel.setText(""); 453 transformLabel.setText(""); 454 } 455 if (layerButtons[0].pressed || layerButtons[2].pressed){ 456 prevState(); 457 layerButtons[0].reset(); 458 layerButtons[2].reset(); 459 } 460 else if(layerButtons[1].pressed || layerButtons[3].pressed){ 461 nextState(); 462 layerButtons[1].reset(); 463 layerButtons[3].reset(); 464 } 465 if (saveFrame){ 466 saveFrame = false; 467 saveFrame(); 468 } 469 } 470 } 471 /* Display methods: 472 * displayLineInfo() 473 * setTranslate() 474 * drawGrid() 475 * plot() 476 * plotPoints() 477 * connect() 478 * addPoint() 479 */ 480 void displayLineInfo(float slope, float interval){ 481 if (1 < abs(slope)){ 482 info.setText("\n\nTime-like\n\nInterval: " + interval); 483 } 484 else if (abs(slope) < 1){ 485 info.setText("\n\nSpace-like\n\nInterval: " + interval); 486 } 487 else{ 488 info.setText("\n\nLight-like\n\nInterval: " + interval); 489 } 490 } 491 void setTranslate(int x, int y){ 492 translate(x - translateX, y - translateY); 493 translateX = x; 494 translateY = y; 495 } 496 void drawGrid(){ 497 stroke(0, 0, 255); 498 line(-gridX, 0, 0, gridX, 0, 0); 499 line(0, -gridY, 0, 0, gridY, 0); 500 stroke(0, 255, 0); 501 line(-gridX, gridY, 0, gridX, -gridY, 0); 502 line(-gridX, -gridY, 0, gridX, gridY, 0); 503 stroke(0, 0, 0); 504 for(int i = 0; i <= gridX; i += zoom * TICK_SCALE) /* Ticks for x-axis */ 505 { 506 line(i, -TICK_LENGTH, 0, i, TICK_LENGTH, 0); 507 line(-i, -TICK_LENGTH, 0, -i, TICK_LENGTH, 0); 508 } 509 for(int j = 0; j <= gridY; j += zoom * TICK_SCALE) /* Ticks for y-axis */ 510 { 511 line(-TICK_LENGTH, j, 0, TICK_LENGTH, j, 0); 512 line(-TICK_LENGTH, -j, 0, TICK_LENGTH, -j, 0); 513 } 514 } 515 void plot(Point p){ 516 plot(p, 255, 0, 0); 517 } 518 void plot(Point p, int r, int g, int b){ 519 stroke(r, g, b); 520 fill(r, g, b); 521 int cx = (int)(p.x); 522 int cy = (int)(p.y); 523 int ct = (int)(-1.0 * p.t); 524 point(cx - 1, ct - 1, cy - 1); 525 point(cx - 1, ct - 1, cy); 526 point(cx - 1, ct, cy); 527 point(cx - 1, ct, cy - 1); 528 point(cx, ct - 1, cy); 529 point(cx, ct - 1, cy - 1); 530 point(cx, ct, cy); 531 point(cx, ct, cy - 1); 532 } 533 void connect(Point p){ 534 connect(p, 255, 0, 0); 535 } 536 void connect(Point p, int r, int g, int b){ 537 if (p.next != null){ 538 stroke(r, g, b); 539 line(p.x, -p.t, p.y, p.next.x, -p.next.t, p.next.y); 540 } 541 } 542 void plotPoints(Point head){ 543 plotPoints(head, false); 544 } 545 void plotPoints(Point head, boolean connect){ 546 plotPoints(head, null, connect); 547 } 548 void plotPoints(Point head, Point tail, boolean connect){ 549 plotPoints(head, tail, connect, 255, 0, 0); 550 } 551 void plotPoints(Point head, Point tail, boolean connect, int r, int g, int b){ 552 Point curPoint = head; 553 Point tmpPoint; 554 boolean lineDisplayed = false; 555 while(curPoint != null && !eq(curPoint, tail)){ 556 tmpPoint = scale(curPoint); 557 if(inGraph(tmpPoint)){ 558 tmpPoint.next = scale(curPoint.next); 559 if (connect || curPoint.condition == START_LINE){ 560 float slope = computeM(curPoint, curPoint.next); 561 float y0 = computeB(curPoint, slope); 562 float d1 = computeD(scale(getMouseCoords(width/ 4, height/ 4), 1/zoom), slope, y0); 563 float d2 = computeD(scale(getMouseCoords(3 * width/ 4, 3 * height/ 4), 1/zoom), slope, y0); 564 if (d1 < LINE_DIST_MARGIN){ 565 float interval = interval(curPoint, curPoint.next); 566 lineLabel.setText("Start: " + curPoint + "\nEnd: " + curPoint.next); 567 transformLineLabel.setText("Start: " + doTransform(curPoint) + "\nEnd: " + doTransform(curPoint.next)); 568 lineDisplayed = true; 569 displayLineInfo(slope, interval); 570 } 571 if (d2 < LINE_DIST_MARGIN){ 572 float interval = interval(curPoint, curPoint.next); 573 transformLineLabel.setText("Start: " + curPoint + "\nEnd: " + curPoint.next); 574 lineLabel.setText("Start: " + reverseTransform(curPoint) + "\nEnd: " + reverseTransform(curPoint.next)); 575 lineDisplayed = true; 576 displayLineInfo(slope, interval); 577 } 578 connect(tmpPoint, r, g, b); 579 } 580 plot(tmpPoint, r, g, b); 581 } 582 curPoint = curPoint.next; 583 } 584 if (!lineDisplayed){ 585 lineLabel.setText(""); 586 transformLineLabel.setText(""); 587 } 588 } 589 void addPoint(Point p){ 590 if (points == null){ 591 points = p; 592 } 593 else{ 594 p.next = points; 595 points = p; 596 } 597 } 598 /* GUI Methods 599 * displayAll() 600 */ 601 void displayAll(){ 602 Iterator it = items.iterator(); 603 GUI_Item tmp; 604 while(it.hasNext()){ 605 tmp = (GUI_Item)it.next(); 606 tmp.display(); 607 } 608 } 609 /* Point methods 610 * closestPoint() 611 * scale() 612 * eq() 613 * dist() 614 */ 615 Point closestPoint(Point head, Point p){ 616 if (head == null){ 617 return null; 618 } 619 Point closest = head; 620 float closestDist = dist(head, p); 621 float curDist; 622 head = head.next; 623 while(head != null){ 624 curDist = dist(head, p); 625 if(curDist < closestDist){ 626 closest = head; 627 closestDist = curDist; 628 } 629 head = head.next; 630 } 631 if (closestDist < POINT_DIST_MARGIN){ 632 return closest; 633 } 634 else{ 635 return null; 636 } 637 } 638 Point scale(Point p){ 639 return scale(p, zoom); 640 } 641 Point scale(Point p, float zoom){ 642 if (p != null){ 643 Point tmp = new Point(p.x * zoom, p.y * zoom, p.t * zoom); 644 tmp.next = p.next; 645 tmp.condition = p.condition; 646 return tmp; 647 } 648 else{ 649 return null; 650 } 651 } 652 boolean eq(Point p1, Point p2){ 653 return p1 != null && 654 p2 != null && 655 abs(p1.x - p2.x) <= FLOAT_ERROR && 656 abs(p1.y - p2.y) <= FLOAT_ERROR && 657 abs(p1.t - p2.t) <= FLOAT_ERROR; 658 } 659 float dist(Point p1, Point p2){ 660 return dist(p1.x, p1.t, p2.x, p2.t); 661 } 662 float dist(Point p1){ 663 return dist(p1, new Point(0, 0, 0)); 664 } 665 666 /* Relativity methods 667 * updateGamma() 668 * initMatrices() 669 * updateTransform() 670 * reverseTransform() 671 * doTransform() 672 * transformPoints() 673 * interval() 674 * mult() 675 */ 676 void updateGamma(){ 677 gamma = 1.0/ sqrt(1.0 - beta*beta); 678 } 679 void initMatrices(){ 680 transformMatrix = new float[3][3]; 681 inverseMatrix = new float[3][3]; 682 transformMatrix[2][2] = 1; 683 inverseMatrix[2][2] = 1; 684 } 685 void updateTransform(){ 686 updateGamma(); 687 transformMatrix[0][0] = gamma; 688 transformMatrix[0][1] = -gamma * beta; 689 transformMatrix[1][0] = -gamma * beta; 690 transformMatrix[1][1] = gamma; 691 float commonFactor = 1/ (gamma * (1 - beta * beta)); 692 inverseMatrix[0][0] = commonFactor; 693 inverseMatrix[0][1] = beta * commonFactor; 694 inverseMatrix[1][0] = beta * commonFactor; 695 inverseMatrix[1][1] = commonFactor; 696 } 697 Point reverseTransform(Point p){ 698 return doTransform(p, inverseMatrix); 699 } 700 Point doTransform(Point p, float[][] m){ 701 float[][] coords = {{p.t}, {p.x}, {p.y}}; 702 float[][] resultMatrix = mult(m, coords); 703 Point transformedPoint = new Point(resultMatrix[1][0], resultMatrix[2][0], resultMatrix[0][0]); 704 transformedPoint.next = p.next; 705 transformedPoint.condition = p.condition; 706 return transformedPoint; 707 } 708 Point doTransform(Point p){ 709 return doTransform(p, transformMatrix); 710 } 711 void transformPoints(){ 712 if (points == null){ 713 transform = null; 714 return; 715 } 716 transform = new Point(); 717 Point lastPoint = new Point(); 718 Point curPoint = points; 719 Point newPoint = transform; 720 Point tmp; 721 while(curPoint != null){ 722 newPoint.next = new Point(); 723 tmp = doTransform(curPoint); 724 newPoint.x = tmp.x; 725 newPoint.y = tmp.y; 726 newPoint.t = tmp.t; 727 newPoint.condition = curPoint.condition; 728 lastPoint = newPoint; 729 newPoint = newPoint.next; 730 curPoint = curPoint.next; 731 } 732 lastPoint.next = null; 733 } 734 float interval(float x1, float t1, float x2, float t2){ 735 return (x2 - x1)*(x2 - x1) - (t2 - t1) * (t2 - t1); 736 } 737 float interval(Point p){ 738 return interval(p, p.next); 739 } 740 float interval(Point p1, Point p2){ 741 return interval(p1.x, p1.t, p2.x, p2.t); 742 } 743 744 float[][] mult(float[][] m1, float[][]m2){ 745 float result[][] = new float[3][1]; 746 result[0][0] = m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0] + m1[0][2] * m2[2][0]; 747 result[1][0] = m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0] + m1[1][2] * m2[2][0]; 748 result[2][0] = m1[2][0] * m2[0][0] + m1[2][1] * m2[2][0] + m1[2][2] * m2[2][0]; 749 return result; 750 } 751 /* GUI methods 752 * getMouseCoords() 753 * mouseDragged() 754 * mouseReleased() 755 * mousePressed() 756 * updateAll() 757 */ 758 Point getMouseCoords(){ 759 return getMouseCoords(translateX, translateY); 760 } 761 Point getMouseCoords(int tx, int tt){ 762 return getMouseCoords(new Point(mouseX, mouseY), tx, tt); 763 } 764 Point getMouseCoords(Point m, int tx, int tt){ 765 float initX = m.x - tx; 766 float initT = -(m.t - tt); 767 return new Point(initX, initT); 768 } 769 void mouseDragged(){ 770 updateAll(); 771 dragged = true; 772 } 773 void mouseReleased(){ 774 if (menu == null){ 775 Point m = getMouseCoords(width/ 4, height/ 4); 776 if (dragged && inGraph(m.x, m.t) && lineStart != null){ 777 Point lineEnd = scale(m, 1/zoom); 778 addPoint(lineEnd); 779 addPoint(lineStart); 780 } 781 else{ 782 m = getMouseCoords(3 * width/ 4, 3 * height/ 4); 783 if (dragged && inGraph(m.x, m.t) && transformLineStart != null){ 784 Point transformLineEnd = scale(m, 1/zoom); 785 Point lineStart = reverseTransform(transformLineStart); 786 Point lineEnd = reverseTransform(transformLineEnd); 787 addPoint(lineEnd); 788 addPoint(lineStart); 789 } 790 } 791 } 792 else{ 793 if (minimumTime <= millis() - menuTime){ 794 String choice = menu.getSelected(); 795 if (choice != null){ 796 if (choice.equals("Save screen image")){ 797 saveFrame = true; 798 } 799 if (choice.equals("Help")){ 800 info.setText( 801 "Click once to add a point; drag \n" + 802 "and release to draw a line.\n" + 803 "Position the mouse over a point\n" + 804 "or line to view information about it\n" + 805 "You can adjust the relative\n" + 806 "velocity between the two\n" + 807 "inertial frames by dragging the\n" + 808 "\"Beta\" slider. The scale\n" + 809 "of the spacetime diagrams can be\n" + 810 "controlled with the \"Scale\"\n" + 811 "slider. To save your work in one\n" + 812 "set of frames and move on to\n" + 813 "a new set, just click one of the\n" + 814 "\">\" buttons. At any point, \n" + 815 "you can return by clicking one of\n" + 816 "the \"<\" buttons. If you simply\n" + 817 "wish to clear the current point set,\n" + 818 "click \"Clear\" in the menu\n" 819 ); 820 } 821 if (choice.equals("About")){ 822 info.setText("Steven Herbst\n" + 823 "AP Physics, 2004 - 2005\n" + 824 "Thomas Jefferson HS for Science and Tech\n" + 825 "Instructor: Dr. John Dell\n" 826 ); 827 } 828 if (choice.equals("Clear")){ 829 points = null; 830 pslider.setStatus(0.0); 831 sslider.setStatus(0.0); 832 lineLabel.setText(""); 833 transformLineLabel.setText(""); 834 graphLabel.setText(""); 835 transformLabel.setText(""); 836 info.setText(""); 837 } 838 if (choice.equals("Restart")){ 839 setup(); 840 } 841 } 842 } 843 menu.clear(); 844 menu = null; 845 } 846 } 847 void mousePressed(){ 848 updateAll(); 849 Point m = getMouseCoords(width/ 4, height/ 4); 850 dragged = false; 851 Point tmp; 852 if (inGraph(m.x, m.t)){ 853 lineStart = scale(m, 1/zoom); 854 lineStart.condition = START_LINE; 855 pointAdded = true; 856 addPoint(scale(m, 1/zoom)); 857 } 858 else{ 859 m = getMouseCoords(3 * width/ 4, 3 * height/ 4); 860 if (inGraph(m.x, m.t)){ 861 transformLineStart = scale(m, 1/zoom); 862 transformLineStart.condition = START_LINE; 863 pointAdded = true; 864 addPoint(reverseTransform(scale(m, 1/zoom))); 865 } 866 } 867 if (pointAdded == false){ 868 menu = new Menu(mouseX, mouseY); 869 if (!online){ 870 menu.addMenuItem("Save screen image"); 871 } 872 menu.addMenuItem("Help"); 873 menu.addMenuItem("About"); 874 menu.addMenuItem("Clear"); 875 menu.addMenuItem("Restart"); 876 menuTime = millis(); 877 } 878 } 879 void updateAll(){ 880 Iterator it = items.iterator(); 881 GUI_Item tmp; 882 while(it.hasNext()){ 883 tmp = (GUI_Item)it.next(); 884 tmp.update(); 885 } 886 } 887 /* Frame methods: 888 * initStates() 889 * saveState() 890 * nextState() 891 * newState() 892 * prevState() 893 */ 894 void initStates(){ 895 layerButtons[0].setEnabled(false); 896 layerButtons[2].setEnabled(false); 897 states = new State(null); 898 states.prev = sentinel; 899 states.next = sentinel; 900 } 901 void saveState(){ 902 states.points = points; 903 states.beta = pslider.getStatus(); 904 states.zoom = sslider.getStatus(); 905 } 906 void nextState(){ 907 saveState(); 908 layerButtons[0].setEnabled(true); 909 layerButtons[2].setEnabled(true); 910 info.setText(""); 911 if (states.next != sentinel){ 912 states = states.next; 913 points = states.points; 914 pslider.setStatus(states.beta); 915 sslider.setStatus(states.zoom); 916 } 917 else{ 918 newState(); 919 } 920 } 921 void newState(){ 922 states.append(new State(null)); 923 states = states.next; 924 points = null; 925 pslider.setStatus(0.0); 926 sslider.setStatus(0.0); 927 } 928 void prevState(){ 929 saveState(); 930 if (states.prev != sentinel){ 931 states = states.prev; 932 points = states.points; 933 pslider.setStatus(states.beta); 934 sslider.setStatus(states.zoom); 935 } 936 if (states.prev == sentinel){ 937 layerButtons[0].setEnabled(false); 938 layerButtons[2].setEnabled(false); 939 } 940 } 941 /* Graph methods 942 * computeM() 943 * computeB() 944 * computeD() 945 * inGraph() 946 */ 947 float computeM(Point p1, Point p2){ 948 return (p2.t - p1.t)/ (p2.x - p1.x); 949 } 950 float computeB(Point p, float m){ 951 return p.t - m * p.x; 952 } 953 float computeD(Point p, float m, float b){ 954 return abs((p.t - m * p.x - b)/ sqrt(m * m + 1)); 955 } 956 boolean inGraph(Point p){ 957 return inGraph(p.x, p.t); 958 } 959 boolean inGraph(float x, float y){ 960 return inGraph(x, y, 0, 0); 961 } 962 boolean inGraph(float x, float y, int cx, int cy){ 963 return cx - gridX <= x && 964 x <= cx + gridX && 965 cy - gridY <= y && 966 y <= cx + gridY; 967 }