Advertisement
bekovski

algovi_sat_final01_with_comments

Jul 29th, 2018
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 49.22 KB | None | 0 0
  1. /*
  2.  * Separating Axis Theorem (Circle - Triangle).java
  3.  * Bekir Özkara, 2018 for the Animal project at TU Darmstadt.
  4.  * Copying this file for educational purposes is permitted without further authorization.
  5.  */
  6. //package generators.misc;
  7.  
  8. import generators.framework.Generator;
  9. import generators.framework.GeneratorType;
  10. import generators.framework.ValidatingGenerator;
  11.  
  12. import java.util.Locale;
  13.  
  14. import algoanim.primitives.Circle;
  15. import algoanim.primitives.Point;
  16. import algoanim.primitives.Polyline;
  17. import algoanim.primitives.Rect;
  18. import algoanim.primitives.SourceCode;
  19. import algoanim.primitives.Text;
  20. import algoanim.primitives.Triangle;
  21. import algoanim.primitives.generators.Language;
  22.  
  23. import java.awt.Color;
  24. import java.awt.Font;
  25. import java.util.ArrayList;
  26. import java.util.Hashtable;
  27. import java.util.List;
  28.  
  29. import generators.framework.properties.AnimationPropertiesContainer;
  30. import interactionsupport.models.MultipleChoiceQuestionModel;
  31. import algoanim.animalscript.AnimalCircleGenerator;
  32. import algoanim.animalscript.AnimalPolylineGenerator;
  33. import algoanim.animalscript.AnimalRectGenerator;
  34. import algoanim.animalscript.AnimalScript;
  35. import algoanim.animalscript.AnimalTextGenerator;
  36. import algoanim.animalscript.AnimalTriangleGenerator;
  37. import algoanim.properties.AnimationPropertiesKeys;
  38. import algoanim.properties.CircleProperties;
  39. import algoanim.properties.PointProperties;
  40. import algoanim.properties.PolylineProperties;
  41. import algoanim.properties.RectProperties;
  42. import algoanim.properties.SourceCodeProperties;
  43. import algoanim.properties.TextProperties;
  44. import algoanim.properties.TriangleProperties;
  45. import algoanim.util.Coordinates;
  46. import algoanim.util.Node;
  47. import algoanim.util.TicksTiming;
  48. import algoanim.util.Timing;
  49. import animal.main.Animal;
  50. import auxiliary.Vector2f;
  51.  
  52. public class SATGenerator implements ValidatingGenerator {
  53.     // auto gen
  54.     private Language lang;
  55.    
  56.     // primitives
  57.     private double vertex_a_x;
  58.     private double vertex_a_y;
  59.     private double vertex_b_x;
  60.     private double vertex_b_y;
  61.     private double vertex_c_x;
  62.     private double vertex_c_y;
  63.     private double circle_center_x;
  64.     private double circle_center_y;
  65.     private double radius;
  66.    
  67.     // properties
  68.     private CircleProperties circleProps;
  69.     private TriangleProperties triangleProps;
  70.         // new
  71.     private RectProperties boxProps;
  72.     private PolylineProperties coordSystemProps;
  73.     private SourceCodeProperties sourceCodeProps;
  74.     private TextProperties textProps;
  75.     private TextProperties vertexNameProps;
  76.     private CircleProperties vertexHighlightProps;
  77.     private PolylineProperties edgeHighlightProps;
  78.     private PolylineProperties radiusHighlightProps;
  79.     private PolylineProperties distanceHighlightProps;
  80.     private PolylineProperties vectorProps;
  81.     private PolylineProperties vectorHighlightProps;
  82.    
  83.    
  84.     // useful vars
  85.     Color radiusColor;
  86.     Color distanceColor;
  87.     Color vectorDefaultColor;
  88.     Color vectorHighlightColor;
  89.    
  90.     // my own
  91.             public static final Color LINE_DEFAULT_COLOR    = Color.BLACK;
  92.             public static final Color VECTOR_DEFAULT_COLOR  = Color.RED;
  93.            
  94.             Text text;
  95.             Point point;
  96.             Circle circle;
  97.             Polyline line;
  98.             Polyline edge;
  99.             Polyline vector;
  100.             Polyline separatingAxis;
  101.             Triangle triangle;
  102.            
  103.             // temp
  104.             Polyline[] vectors = new Polyline[3];
  105.            
  106.             SourceCode sc;
  107.             Timing timing;
  108.            
  109.             // might need these properties
  110.             TextProperties textPropsOld;
  111.             RectProperties rectProps;
  112.             PointProperties pointProps;
  113.             // CircleProperties circleProps;
  114.             PolylineProperties lineProps;
  115.             PolylineProperties vectorPropsOld;
  116.             // TriangleProperties triangleProps;
  117.            
  118.             int x = 40;
  119.             int y = 140;
  120.             int startY;
  121.             int offsetY = 20;
  122.             String vertexName;
  123.             String neighbor1Name;
  124.             String neighbor2Name;
  125.            
  126.             Vector2f separatingAxisStart;
  127.             Vector2f separatingAxisEnd;
  128.            
  129.             List<Text> textList = new ArrayList<>();
  130.     // end my own
  131.  
  132.     public void init(){
  133. //        lang = new AnimalScript("Separating Axis Theorem (Circle - Triangle)", "Bekir Özkara", 800, 600);
  134.         lang = new AnimalScript("Separating Axis Theorem (Circle-Triangle)", "Bekir Oezkara", 640, 480);
  135.         lang.setStepMode(true);
  136.         lang.setInteractionType(Language.INTERACTION_TYPE_AVINTERACTION);
  137.        
  138.         timing = new TicksTiming(200);
  139.        
  140. //      textPropsOld = new TextProperties();
  141. //      textPropsOld.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.BLACK);
  142. //      textPropsOld.set(AnimationPropertiesKeys.FONT_PROPERTY, new Font("Monospaced", Font.PLAIN, 12));
  143.        
  144.         rectProps = new RectProperties();
  145.         rectProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.DARK_GRAY);
  146.         rectProps.set(AnimationPropertiesKeys.FILLED_PROPERTY, true);
  147.         rectProps.set(AnimationPropertiesKeys.FILL_PROPERTY, Color.LIGHT_GRAY);
  148.         rectProps.set(AnimationPropertiesKeys.DEPTH_PROPERTY, 2);
  149.        
  150.         pointProps = new PointProperties();
  151.         pointProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.CYAN);
  152.        
  153. //      circleProps = new CircleProperties();
  154. //      circleProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.RED);
  155. //      circleProps.set(AnimationPropertiesKeys.FILL_PROPERTY, Color.RED);
  156. //      circleProps.set(AnimationPropertiesKeys.FILLED_PROPERTY, true);
  157. //      circleProps.set(AnimationPropertiesKeys.DEPTH_PROPERTY, 2);
  158.        
  159.         lineProps = new PolylineProperties();
  160.         lineProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, LINE_DEFAULT_COLOR);
  161.        
  162.         vectorPropsOld = new PolylineProperties();
  163.         vectorPropsOld.set(AnimationPropertiesKeys.COLOR_PROPERTY, VECTOR_DEFAULT_COLOR);
  164.         vectorPropsOld.set(AnimationPropertiesKeys.FWARROW_PROPERTY, true);
  165.        
  166. //      triangleProps = new TriangleProperties();
  167. //      triangleProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.GREEN);
  168. //      triangleProps.set(AnimationPropertiesKeys.FILL_PROPERTY, Color.WHITE);
  169.     }
  170.    
  171.    
  172.     public static void main(String[] args) {
  173.         ValidatingGenerator generator = new SATGenerator();
  174.         Animal.startGeneratorWindow(generator);
  175.     }
  176.  
  177.     public String generate(AnimationPropertiesContainer props,Hashtable<String, Object> primitives) {
  178. //      lang.setInteractionType(Language.INTERACTION_TYPE_AVINTERACTION);
  179.        
  180.         // primitives
  181.         vertex_a_x = (double)primitives.get("vertex_a_x");
  182.         vertex_a_y = (double)primitives.get("vertex_a_y");
  183.         vertex_b_x = (double)primitives.get("vertex_b_x");
  184.         vertex_b_y = (double)primitives.get("vertex_b_y");
  185.         vertex_c_x = (double)primitives.get("vertex_c_x");
  186.         vertex_c_y = (double)primitives.get("vertex_c_y");
  187.         circle_center_x = (double)primitives.get("circle_center_x");
  188.         circle_center_y = (double)primitives.get("circle_center_y");
  189.         radius = (double)primitives.get("radius");
  190.        
  191.         // properties
  192.         circleProps = (CircleProperties)props.getPropertiesByName("circleProps");
  193.         triangleProps = (TriangleProperties)props.getPropertiesByName("triangleProps");
  194.             // new ones
  195.         boxProps = (RectProperties)props.getPropertiesByName("boxProps");
  196.         coordSystemProps = (PolylineProperties)props.getPropertiesByName("coordSystemProps");
  197.         sourceCodeProps = (SourceCodeProperties)props.getPropertiesByName("sourceCodeProps");
  198.         textProps = (TextProperties)props.getPropertiesByName("textProps");
  199.         vertexNameProps = (TextProperties)props.getPropertiesByName("vertexNameProps");
  200.         vertexHighlightProps = (CircleProperties)props.getPropertiesByName("vertexHighlightProps");
  201.         edgeHighlightProps = (PolylineProperties)props.getPropertiesByName("edgeHighlightProps");
  202.         radiusHighlightProps = (PolylineProperties)props.getPropertiesByName("radiusHighlightProps");
  203.         distanceHighlightProps = (PolylineProperties)props.getPropertiesByName("distanceHighlightProps");
  204.         vectorProps = (PolylineProperties)props.getPropertiesByName("vectorProps");
  205.         vectorHighlightProps = (PolylineProperties)props.getPropertiesByName("vectorHighlightProps");
  206.        
  207.        
  208.        
  209.         radiusColor = (Color)radiusHighlightProps.get(AnimationPropertiesKeys.COLOR_PROPERTY);
  210.         distanceColor = (Color)distanceHighlightProps.get(AnimationPropertiesKeys.COLOR_PROPERTY);
  211.         vectorDefaultColor = (Color)vectorProps.get(AnimationPropertiesKeys.COLOR_PROPERTY);
  212.         vectorHighlightColor = (Color)vectorHighlightProps.get(AnimationPropertiesKeys.COLOR_PROPERTY);
  213.        
  214.         init();
  215.        
  216.         List<Text> texts = showIntroText();
  217.         lang.nextStep();
  218.         for(Text t : texts) {
  219.             t.hide();
  220.         }
  221.        
  222.         showIntroQuestion();
  223.         lang.nextStep();
  224.        
  225.         createScene();
  226.        
  227.        
  228.         auxiliary.Circle C = new auxiliary.Circle(new Vector2f((float)circle_center_x, (float)circle_center_y), (float)radius);
  229.         // auxiliary.Triangle T = new auxiliary.Triangle(new Vector2f(1, 1), new Vector2f(3, 1), new Vector2f(2, 4));
  230.         auxiliary.Triangle T = new auxiliary.Triangle(
  231.                 new Vector2f((float)vertex_a_x, (float)vertex_a_y),
  232.                 new Vector2f((float)vertex_b_x, (float)vertex_b_y),
  233.                 new Vector2f((float)vertex_c_x, (float)vertex_c_y));
  234.         hasSA(C, T);
  235.        
  236.         clearScreen();
  237.        
  238.         circleProps.set(AnimationPropertiesKeys.DEPTH_PROPERTY, 1);
  239.         triangleProps.set(AnimationPropertiesKeys.DEPTH_PROPERTY, 1);
  240.        
  241.         showOutroQuestions();
  242.         lang.nextStep();
  243.         clearScreen();
  244.        
  245.         showOutroText();
  246.        
  247.        
  248.         lang.finalizeGeneration();
  249.         return lang.toString();
  250.     }
  251.    
  252.     // ====================================================================================================================================
  253.     // ====================================================================================================================================
  254.     // ====================================================================================================================================
  255.     // ====================================================================================================================================
  256.     // ====================================================================================================================================
  257.     // ====================================================================================================================================
  258.  
  259.     public void createScene() {
  260.         makeHeader();
  261.         makeSideBox();
  262.         drawCoordSystem();
  263.     }
  264.    
  265.     public boolean hasSA(auxiliary.Circle circle, auxiliary.Triangle triangle) {
  266.        
  267.         makeCircle(circle.center.x, circle.center.y, circle.radius);
  268.         makeTriangle(triangle.A, triangle.B, triangle.C);
  269.         lang.nextStep();
  270.        
  271.         x = 910;
  272.         startY = 160;
  273.         y = startY;
  274.        
  275.         // vertices
  276.         showSourceCodeVertexSA();
  277.         Circle vertexHighlightCircle;
  278.         Color vertexHighlightColor = (Color)vertexHighlightProps.get(AnimationPropertiesKeys.COLOR_PROPERTY);   // TODO
  279.         boolean isFilled = (boolean)vertexHighlightProps.get(AnimationPropertiesKeys.FILLED_PROPERTY);
  280.        
  281.         makeText("A", 600 + 40 * (int)triangle.A.x + 5 , 300 - 40 * (int)triangle.A.y, vertexNameProps);
  282.         makeText("B", 600 + 40 * (int)triangle.B.x + 5 , 300 - 40 * (int)triangle.B.y, vertexNameProps);
  283.         makeText("C", 600 + 40 * (int)triangle.C.x + 5 , 300 - 40 * (int)triangle.C.y, vertexNameProps);
  284.        
  285.         vertexName = "A";
  286.         neighbor1Name = "B";
  287.         neighbor2Name = "C";
  288.         vertexHighlightCircle = makeCircle(triangle.A.x, triangle.A.y, 0.1f, vertexHighlightColor, isFilled);
  289.         textAndStep("Vertex A");
  290.         vertexHighlightCircle.hide();
  291.         boolean separatedByVertexA = isVertexSA(circle, triangle, triangle.A);
  292.        
  293.         clearText();
  294.         y = startY;
  295.        
  296.         vertexName = "B";
  297.         neighbor1Name = "A";
  298.         neighbor2Name = "C";
  299.         vertexHighlightCircle = makeCircle(triangle.B.x, triangle.B.y, 0.1f, Color.PINK, isFilled);
  300.         textAndStep("Vertex B");
  301.         vertexHighlightCircle.hide();
  302.         boolean separatedByVertexB = isVertexSA(circle, triangle, triangle.B);
  303.        
  304.         clearText();
  305.         y = startY;
  306.        
  307.         vertexName = "C";
  308.         neighbor1Name = "A";
  309.         neighbor2Name = "B";
  310.         vertexHighlightCircle = makeCircle(triangle.C.x, triangle.C.y, 0.1f, Color.PINK, isFilled);
  311.         textAndStep("Vertex C");
  312.         vertexHighlightCircle.hide();
  313.         boolean separatedByVertexC = isVertexSA(circle, triangle, triangle.C);
  314.        
  315.         clearText();
  316.         y = startY;
  317.        
  318.         // edges
  319.         sc.changeColor("", Color.WHITE, null, null);
  320.         showSourceCodeEdgeSA();
  321.         Color edgeHighlightColor = (Color)edgeHighlightProps.get(AnimationPropertiesKeys.COLOR_PROPERTY);
  322.        
  323.         edge = makeLine(triangle.A, triangle.B, false, true);
  324.         edge.changeColor("", edgeHighlightColor, null, null);
  325.         textAndStep("Edge from A to B");
  326.         boolean separatedByEdgeAB = isEdgeSA(circle, triangle, triangle.edgeAB, triangle.A, triangle.B, triangle.C);
  327.         edge.hide();
  328.        
  329.         clearText();
  330.         y = startY;
  331.        
  332.         edge = makeLine(triangle.A, triangle.C, false, true);
  333.         edge.changeColor("", edgeHighlightColor, null, null);
  334.         textAndStep("Edge from A to C");
  335.         boolean separatedByEdgeAC = isEdgeSA(circle, triangle, triangle.edgeAC, triangle.A, triangle.C, triangle.B);
  336.         edge.changeColor("", Color.BLACK, null, null);
  337.        
  338.         clearText();
  339.         y = startY;
  340.        
  341.         edge = makeLine(triangle.B, triangle.C, false, true);
  342.         edge.changeColor("", edgeHighlightColor, null, null);
  343.         textAndStep("Edge from B to C");
  344.         boolean separatedByEdgeBC = isEdgeSA(circle, triangle, triangle.edgeBC, triangle.B, triangle.C, triangle.A);
  345.         edge.hide();
  346.        
  347.         clearText();
  348.         y = startY;
  349.        
  350.         boolean result = separatedByVertexA || separatedByVertexB || separatedByVertexC || separatedByEdgeAB || separatedByEdgeAC || separatedByEdgeBC;
  351.         if(result) {
  352.             separatingAxis = makeLine(separatingAxisStart, separatingAxisEnd, false, true);
  353.             separatingAxis.changeColor("", Color.GREEN, null, null);
  354.         }
  355.         textAndStep("we found separating axis = " + result);
  356.         return result;
  357.     }
  358.    
  359.    
  360.     /**
  361.      *
  362.      * @param circle
  363.      * @param triangle
  364.      * @param vertex
  365.      * @return
  366.      */
  367.     private boolean isVertexSA(auxiliary.Circle circle, auxiliary.Triangle triangle, Vector2f vertex) {
  368.         line = makeLine(circle.center.x, circle.center.y, circle.center.x + circle.radius, circle.center.y, false, true);
  369. //      Color radiusColor = (Color)radiusHighlightProps.get(AnimationPropertiesKeys.COLOR_PROPERTY);
  370.         line.changeColor("", radiusColor, null, null);
  371.         textAndStep("radius = " + circle.radius);
  372.         line.hide();
  373.        
  374.         sc.highlight(1);
  375.         float distToCenter = vertex.dist(circle.center);
  376.         line = makeLine(vertex, circle.center, false, true);
  377. //      Color distanceColor = (Color)distanceHighlightProps.get(AnimationPropertiesKeys.COLOR_PROPERTY);
  378.         line.changeColor("", distanceColor, null, null);
  379.         textAndStep("distance to center = " + distToCenter);
  380.         line.hide();
  381.         sc.unhighlight(1);
  382.        
  383.        
  384.         sc.highlight(2);
  385.         Vector2f vertexToCenter = circle.center.sub(vertex);
  386.         vectors[0] = makeLine(vertex, circle.center, true, vectorProps);
  387.         textAndStep("vector from vertex to center = " + vertexToCenter);
  388.         sc.unhighlight(2);
  389.        
  390.         Vector2f vertexToNeighbor1;
  391.         Vector2f vertexToNeighbor2;
  392.        
  393.         if(vertex == triangle.A) {
  394.             sc.highlight(6, 0, true);
  395.             sc.highlight(7);
  396.             sc.highlight(9);
  397.             vertexToNeighbor1 = triangle.B.sub(vertex); // AB
  398.             vertexToNeighbor2 = triangle.C.sub(vertex); // AC
  399.             neighbor1Name = "B";
  400.             neighbor2Name = "C";
  401. //          vectors[1] = makeLine(vertex, triangle.B, true, true);
  402.             vectors[1] = makeLine(vertex, triangle.B, true, vectorProps);
  403.             textAndStep("vector from " + vertexName + " to " + neighbor1Name + " = " + vertexToNeighbor1);
  404.             sc.unhighlight(7);
  405.            
  406.             sc.highlight(8);
  407. //          vectors[2] = makeLine(vertex, triangle.C, true, true);
  408.             vectors[2] = makeLine(vertex, triangle.C, true, vectorProps);
  409.             textAndStep("vector from " + vertexName + " to " + neighbor2Name + " = " + vertexToNeighbor2);
  410.             sc.unhighlight(6);
  411.             sc.unhighlight(8);
  412.             sc.unhighlight(9);
  413.         }
  414.         else if(vertex == triangle.B) {
  415.             sc.highlight(10, 0, true);
  416.             sc.highlight(11);
  417.             sc.highlight(13);
  418.             vertexToNeighbor1 = triangle.A.sub(vertex); // BA
  419.             vertexToNeighbor2 = triangle.C.sub(vertex); // BC
  420.             neighbor1Name = "A";
  421.             neighbor2Name = "C";
  422. //          vectors[1] = makeLine(vertex, triangle.A, true, true);
  423.             vectors[1] = makeLine(vertex, triangle.A, true, vectorProps);
  424.             textAndStep("vector from " + vertexName + " to " + neighbor1Name + " = " + vertexToNeighbor1);
  425.             sc.unhighlight(11);
  426.            
  427.             sc.highlight(12);
  428. //          vectors[2] = makeLine(vertex, triangle.C, true, true);
  429.             vectors[2] = makeLine(vertex, triangle.C, true, vectorProps);
  430.             textAndStep("vector from " + vertexName + " to " + neighbor2Name + " = " + vertexToNeighbor2);
  431.             sc.unhighlight(10);
  432.             sc.unhighlight(12);
  433.             sc.unhighlight(13);
  434.         }
  435.         else if(vertex == triangle.C) {
  436.             sc.highlight(14, 0, true);
  437.             sc.highlight(15);
  438.             sc.highlight(17);
  439.             vertexToNeighbor1 = triangle.A.sub(vertex); // CA
  440.             vertexToNeighbor2 = triangle.B.sub(vertex); // CB
  441.             neighbor1Name = "A";
  442.             neighbor2Name = "B";
  443. //          vectors[1] = makeLine(vertex, triangle.A, true, true);
  444.             vectors[1] = makeLine(vertex, triangle.A, true, vectorProps);
  445.             textAndStep("vector from " + vertexName + " to " + neighbor1Name + " = " + vertexToNeighbor1);
  446.             sc.unhighlight(15);
  447.            
  448.             sc.highlight(16);
  449. //          vectors[2] = makeLine(vertex, triangle.B, true, true);
  450.             vectors[2] = makeLine(vertex, triangle.B, true, vectorProps);
  451.             textAndStep("vector from " + vertexName + " to " + neighbor2Name + " = " + vertexToNeighbor2);
  452.             sc.unhighlight(14);
  453.             sc.unhighlight(16);
  454.             sc.unhighlight(17);
  455.         }
  456.         else {
  457.             throw new IllegalArgumentException("The provided vertex: " + vertex + " does not match with any of the triangle's vertices.");
  458.         }
  459.        
  460. //      textAndStep("vector from " + vertexName + " to " + neighbor1Name + " = " + vertexToNeighbor1);
  461. //      textAndStep("vector from " + vertexName + " to " + neighbor2Name + " = " + vertexToNeighbor2);
  462.  
  463.         // they are on opposite sides if the dot product is less than zero
  464. //      Color vectorDefaultColor = (Color)vectorProps.get(AnimationPropertiesKeys.COLOR_PROPERTY);
  465. //      Color vectorHighlightColor = (Color)vectorHighlightProps.get(AnimationPropertiesKeys.COLOR_PROPERTY);
  466. //      vectors[0].changeColor("", Color.ORANGE, null, null);
  467.         vectors[0].changeColor("", vectorHighlightColor, null, null);
  468.        
  469.         sc.highlight(21);
  470.         sc.highlight(22);
  471.         boolean isOppositeNeighbor1 = vertexToCenter.dot(vertexToNeighbor1) < 0;
  472. //      vectors[1].changeColor("", Color.ORANGE, null, null);
  473.         vectors[1].changeColor("", vectorHighlightColor, null, null);
  474.         textAndStep(neighbor1Name + " is on opposite side = " + isOppositeNeighbor1);
  475. //      vectors[1].changeColor("", VECTOR_DEFAULT_COLOR, null, null);
  476.         vectors[1].changeColor("", vectorDefaultColor, null, null);
  477.         sc.unhighlight(21);
  478.         sc.unhighlight(22);
  479.        
  480.         sc.highlight(23);
  481.         sc.highlight(24);
  482.         boolean isOppositeNeighbor2 = vertexToCenter.dot(vertexToNeighbor2) < 0;
  483. //      vectors[2].changeColor("", Color.ORANGE, null, null);
  484.         vectors[2].changeColor("", vectorHighlightColor, null, null);
  485.         textAndStep(neighbor2Name + " is on opposite side = " + isOppositeNeighbor2);
  486.         sc.unhighlight(23);
  487.         sc.unhighlight(24);
  488.        
  489.         vectors[0].hide();
  490.         vectors[1].hide();
  491.         vectors[2].hide();
  492.        
  493.         sc.highlight(26);
  494.         boolean radius_less_than_dist = circle.radius < distToCenter;
  495.         textAndStep("radius smaller than distance = " + radius_less_than_dist);
  496.        
  497.         boolean result = radius_less_than_dist && isOppositeNeighbor1 && isOppositeNeighbor2;
  498.         line = makeLine(vertex, circle.center, false, true);
  499.         line.changeColor("", result ? Color.GREEN : Color.RED, null, null);
  500.         if(result) {
  501.             separatingAxisStart = new Vector2f(vertex.x, vertex.y);
  502.             separatingAxisEnd   = new Vector2f(circle.center.x, circle.center.y);
  503. //          separatingAxis =  makeLine(vertex, circle.center, false, true);
  504.         }
  505.         sc.highlight(27);
  506.         textAndStep("vertex is separating axis = " + result);
  507.         line.hide();
  508.         sc.unhighlight(26);
  509.         sc.unhighlight(27);
  510. //      if(separatingAxis != null) {
  511. //          separatingAxis.hide(); 
  512. //      }
  513.         return result;
  514.     }
  515.    
  516.    
  517.     /**
  518.      *
  519.      * @param circle
  520.      * @param triangle
  521.      * @param edge
  522.      * @param start
  523.      * @param end
  524.      * @param other
  525.      * @return
  526.      */
  527.     private boolean isEdgeSA(auxiliary.Circle circle, auxiliary.Triangle triangle, Vector2f edge, Vector2f start, Vector2f end, Vector2f other) {
  528.         line = makeLine(circle.center.x, circle.center.y, circle.center.x + circle.radius, circle.center.y, false, true);
  529.         // line.getProperties().set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.BLUE);
  530. //      line.changeColor("", Color.BLUE, null, null);
  531.         line.changeColor("", radiusColor, null, null);
  532.         textAndStep("radius = " + circle.radius);
  533.         line.hide();
  534.        
  535.         sc.highlight(1);
  536.         Vector2f normalizedEdge = edge.normalize();
  537.         vector = makeLine(start.x, start.y, start.x + normalizedEdge.x, start.y + normalizedEdge.y, true, vectorProps);
  538. //      vector.changeColor("", VECTOR_DEFAULT_COLOR, null, null);
  539.         vector.changeColor("", vectorDefaultColor, null, null);
  540.         textAndStep("normalized edge = " + normalizedEdge);
  541.         vector.hide();
  542.         sc.unhighlight(1);
  543.        
  544.         Vector2f vecToCircle;
  545.         if(edge == triangle.edgeAB) {
  546.             sc.highlight(4, 0, true);
  547.             sc.highlight(5);
  548.             sc.highlight(6);
  549.             vecToCircle = circle.center.sub(triangle.A);
  550.             vector = makeLine(triangle.A, circle.center, true, vectorProps);
  551.             textAndStep("vector to circle = " + vecToCircle);
  552.             vector.hide();
  553.             sc.unhighlight(4);
  554.             sc.unhighlight(5);
  555.             sc.unhighlight(6);
  556.         }
  557.         else if(edge == triangle.edgeAC) {
  558.             sc.highlight(7, 0, true);
  559.             sc.highlight(8);
  560.             sc.highlight(9);
  561.             // TODO: why not C, or: when A, when C ?
  562.             vecToCircle = circle.center.sub(triangle.A);
  563.             vector = makeLine(triangle.A, circle.center, true, vectorProps);
  564.             textAndStep("vector to circle = " + vecToCircle);
  565.             vector.hide();
  566.             sc.unhighlight(7);
  567.             sc.unhighlight(8);
  568.             sc.unhighlight(9);
  569.         }
  570.         else if(edge == triangle.edgeBC) {
  571.             sc.highlight(10, 0, true);
  572.             sc.highlight(11);
  573.             sc.highlight(12);
  574.             vecToCircle = circle.center.sub(triangle.B);
  575.             vector = makeLine(triangle.B, circle.center, true, vectorProps);
  576.             textAndStep("vector to circle = " + vecToCircle);
  577.             vector.hide();
  578.             sc.unhighlight(10);
  579.             sc.unhighlight(11);
  580.             sc.unhighlight(12);
  581.         }
  582.         else {
  583.             throw new IllegalArgumentException("The provided edge: " + edge + " does not match with any of the triangle's edges.");
  584.         }
  585.        
  586.         sc.highlight(13);
  587.         float dot = normalizedEdge.dot(vecToCircle);
  588.         // line = makeLine(start.x, start.y, start.x + dot, start.y + dot, false, true);
  589.         // TODO: fix dot product (get direction right!)
  590.         line = makeLine(start, start.add(normalizedEdge.mul(dot)), false, true);        // is this right ?
  591.         line.changeColor("", Color.CYAN, null, null);
  592.         textAndStep("dot product of normalizedEdge and vectorToCircle = " + dot);
  593.         line.hide();
  594.         sc.unhighlight(13);
  595.        
  596.         Vector2f closestPointToCircle;
  597.         int codeLine1 = 0;
  598.         int codeLine2 = 0;
  599.         if(dot <= 0) {
  600.             codeLine1 = 16;
  601.             sc.highlight(codeLine1 - 1, 0, true);
  602.             sc.highlight(codeLine1);
  603.             closestPointToCircle = start;
  604.         }
  605.         else if(dot >= edge.length()) {
  606.             codeLine1 = 19;
  607.             sc.highlight(codeLine1 - 1, 0, true);
  608.             sc.highlight(codeLine1);
  609.             closestPointToCircle = end;
  610.         }
  611.         else {
  612.             codeLine1 = 22;
  613.             codeLine2 = 23;
  614.             sc.highlight(codeLine1 - 1, 0, true);
  615.             sc.highlight(codeLine1);
  616.             sc.highlight(codeLine2);
  617.             closestPointToCircle = normalizedEdge.mul(dot).add(start);
  618.         }
  619.        
  620.         Circle pointToHighlight;
  621.         boolean isFilled = true;
  622.        
  623.         line = makeLine(start, closestPointToCircle, false, true);
  624.         line.changeColor("", Color.CYAN, null, null);
  625.         pointToHighlight = makeCircle(closestPointToCircle.x, closestPointToCircle.y, 0.1f, Color.PINK, isFilled);
  626.         textAndStep("closest point to circle = " + closestPointToCircle);
  627.         pointToHighlight.hide();
  628.         line.hide();
  629.         sc.unhighlight(codeLine1 - 1);
  630.         sc.unhighlight(codeLine1);
  631.         sc.unhighlight(codeLine2);
  632.        
  633.         // check if the other vertex (the one that is not part of the current edge) and the circle-center lie on opposite sides of the edge
  634.         sc.highlight(29);
  635.         sc.highlight(30);
  636.         Vector2f vecFromClosestPointToCircle        = circle.center.sub(closestPointToCircle);
  637.         vectors[0] = makeLine(closestPointToCircle, circle.center, true, vectorProps);
  638.         textAndStep("vector from closest point to circle = " + vecFromClosestPointToCircle);
  639.         sc.unhighlight(29);
  640.         sc.unhighlight(30);
  641.        
  642.         sc.highlight(31);
  643.         sc.highlight(32);
  644.         Vector2f vecFromClosestPointToOtherVertex   = other.sub(closestPointToCircle);
  645.         vectors[1] = makeLine(closestPointToCircle, other, true, vectorProps);
  646.         textAndStep("vector from closest point to other vertex = " + vecFromClosestPointToOtherVertex);
  647.         sc.unhighlight(31);
  648.         sc.unhighlight(32);
  649.        
  650.         sc.highlight(33);
  651.         boolean isOppositeOtherVertex               = vecFromClosestPointToCircle.dot(vecFromClosestPointToOtherVertex) < 0;
  652.         Color clr = vectorDefaultColor != Color.BLUE ? Color.BLUE : Color.ORANGE;
  653.         vectors[0].changeColor("", clr, null, null);
  654.         vectors[1].changeColor("", clr, null, null);
  655.         textAndStep("other vertex on opposite side of circle = " + isOppositeOtherVertex);
  656.         sc.unhighlight(33);
  657.        
  658.         vectors[0].hide();
  659.         vectors[1].hide();
  660.        
  661.         sc.highlight(35);
  662.         boolean radius_less_than_dist_to_closest_point = circle.radius < vecFromClosestPointToCircle.length();
  663.         textAndStep("radius less than distance to closest point = " + radius_less_than_dist_to_closest_point);
  664.        
  665.         boolean result = radius_less_than_dist_to_closest_point && isOppositeOtherVertex;
  666.         line = makeLine(closestPointToCircle, circle.center, false, true);
  667.         line.changeColor("", result ? Color.GREEN : Color.RED, null, null);
  668.         if(result) {
  669.             separatingAxisStart = new Vector2f(closestPointToCircle.x, closestPointToCircle.y);
  670.             separatingAxisEnd   = new Vector2f(circle.center.x, circle.center.y);
  671. //          separatingAxis = makeLine(closestPointToCircle, circle.center, false, true);
  672.         }
  673.         textAndStep("is separating axis = " + result);
  674.         line.hide();
  675.         sc.unhighlight(35);
  676. //      if(separatingAxis != null) {
  677. //          separatingAxis.hide(); 
  678. //      }
  679.         return result;
  680.     }
  681.    
  682.    
  683.     // ====================================================================================================================================
  684.     // ====================================================================================================================================
  685.    
  686.     private Circle makeCircle(float x, float y, float radius) {
  687.         return new Circle(new AnimalCircleGenerator(lang), transformCoords(x, y), transformRadius(radius), "circle", null, circleProps);
  688.     }
  689.    
  690.     private Circle makeCircle(float x, float y, float radius, Color color, boolean isFilled) {
  691.         CircleProperties props = new CircleProperties();
  692.         props.set(AnimationPropertiesKeys.FILL_PROPERTY, color);
  693.         props.set(AnimationPropertiesKeys.FILLED_PROPERTY, isFilled);
  694.         return new Circle(new AnimalCircleGenerator(lang), transformCoords(x, y), transformRadius(radius), "circle", null, props);
  695.     }
  696.    
  697.     private Triangle makeTriangle(Vector2f A, Vector2f B, Vector2f C) {
  698.         return makeTriangle(A.x, A.y, B.x, B.y, C.x, C.y);
  699.     }
  700.    
  701.     private Triangle makeTriangle(float Ax, float Ay, float Bx, float By, float Cx, float Cy) {
  702.         return new Triangle(new AnimalTriangleGenerator(lang),
  703.                 transformCoords(Ax, Ay),
  704.                 transformCoords(Bx, By),
  705.                 transformCoords(Cx, Cy),
  706.                 "triangle",
  707.                 null,
  708.                 triangleProps);
  709.     }
  710.    
  711.     private Coordinates transformCoords(float x, float y) {
  712.         float newX = 600 + 40 * x;
  713.         float newY = 300 - 40 * y;
  714.         return new Coordinates((int)newX, (int)newY);
  715.     }
  716.    
  717.     private int transformRadius(float radius) {
  718.         float newRadius = 40 * radius;
  719.         return (int)newRadius;
  720.     }
  721.    
  722.     private Rect makeRect(int upperLeftX, int upperLeftY, int lowerRightX, int lowerRightY) {
  723.         return new Rect(new AnimalRectGenerator(lang), new Coordinates(upperLeftX, upperLeftY), new Coordinates(lowerRightX, lowerRightY), "rect", null, rectProps);
  724.     }
  725.    
  726.     private Rect makeRect(int upperLeftX, int upperLeftY, int lowerRightX, int lowerRightY, RectProperties rectProps) {
  727.         return new Rect(new AnimalRectGenerator(lang), new Coordinates(upperLeftX, upperLeftY), new Coordinates(lowerRightX, lowerRightY), "rect", null, rectProps);
  728.     }
  729.    
  730.     private Text makeText(String text, int x, int y) {
  731.         return new Text(new AnimalTextGenerator(lang), new Coordinates(x, y), text, "text", null, textProps);
  732.     }
  733.    
  734.     private Text makeText(String text, int x, int y, TextProperties textProps) {
  735.         return new Text(new AnimalTextGenerator(lang), new Coordinates(x, y), text, "text", null, textProps);
  736.     }
  737.    
  738.     private void textAndStep(String text) {
  739.         textAndStep(text, this.x, this.y);
  740.     }
  741.    
  742.     private void textAndStep(String text, int x, int y) {
  743.         textList.add(makeText(text, x, y));
  744.         this.y += offsetY;
  745.         lang.nextStep();
  746.     }
  747.    
  748.     private void clearText() {
  749.         for(Text text : textList) {
  750.             text.setText("", null, null);
  751.         }
  752.        
  753.         textList = new ArrayList<>();
  754.     }
  755.    
  756.    
  757.     private Polyline makeLine(Vector2f from, Vector2f to, boolean isVector, boolean needsTransform) {
  758.         return makeLine(from.x, from.y, to.x, to.y, isVector, needsTransform);
  759.     }
  760.    
  761.     private Polyline makeLine(float startX, float startY, float endX, float endY, boolean isVector, boolean needsTransform) {
  762.         Node[] nodeArray;
  763.         if(needsTransform) {
  764.             nodeArray = new Node[] {
  765.                     transformCoords(startX, startY),
  766.                     transformCoords(endX, endY)
  767.             };
  768.         }
  769.         else {
  770.             nodeArray = new Node[] {
  771.                     new Coordinates((int)startX, (int)startY),
  772.                     new Coordinates((int)endX, (int)endY)
  773.             };
  774.         }
  775.        
  776.         return new Polyline(new AnimalPolylineGenerator(lang), nodeArray, "line", null, isVector ? vectorPropsOld : lineProps);
  777.     }
  778.    
  779.     private Polyline makeLine(Vector2f from, Vector2f to, boolean needsTransform, PolylineProperties lineProps) {
  780.         return makeLine(from.x, from.y, to.x, to.y, needsTransform, lineProps);
  781.     }
  782.    
  783.     private Polyline makeLine(float startX, float startY, float endX, float endY, boolean needsTransform, PolylineProperties lineProps) {
  784.         Node[] nodeArray;
  785.         if(needsTransform) {
  786.             nodeArray = new Node[] {
  787.                     transformCoords(startX, startY),
  788.                     transformCoords(endX, endY)
  789.             };
  790.         }
  791.         else {
  792.             nodeArray = new Node[] {
  793.                     new Coordinates((int)startX, (int)startY),
  794.                     new Coordinates((int)endX, (int)endY)
  795.             };
  796.         }
  797.        
  798.         return new Polyline(new AnimalPolylineGenerator(lang), nodeArray, "line", null, lineProps);
  799.     }
  800.    
  801.    
  802.     private void makeHeader() {
  803.         makeRect(430, 25, 790, 50);
  804.        
  805.         TextProperties titleProps = new TextProperties();
  806.         titleProps.set(AnimationPropertiesKeys.FONT_PROPERTY, new Font("SansSerif", Font.BOLD, 16));
  807.         new Text(new AnimalTextGenerator(lang), new Coordinates(450, 27), "Separating Axis Theorem (Circle-Triangle)", "title", null, titleProps);
  808.     }
  809.    
  810.     private void makeSideBox() {
  811.         makeRect(900, 150, 1400, 380, boxProps);
  812.     }
  813.    
  814.     private void drawCoordSystem() {
  815.         int xAxis       = 300;
  816.         int xAxisStart  = 400;
  817.         int xAxisEnd    = 800;
  818.        
  819.         int yAxis       = 600;
  820.         int yAxisStart  = 500;
  821.         int yAxisEnd    = 100;
  822.        
  823.         makeLine(xAxisStart, xAxis, xAxisEnd, xAxis, false, coordSystemProps);  // x-axis (600px)
  824.         makeLine(yAxis, yAxisStart, yAxis, yAxisEnd, false, coordSystemProps);  // y-axis (400px)
  825.        
  826.         int offset = 40;    // 60px = 1cm
  827.        
  828.         // x-axis
  829.         for(int i=xAxisStart; i <= xAxisEnd; i+=offset) {
  830.             makeLine(i, xAxis-5, i, xAxis+5, false, false);
  831.         }
  832.        
  833.         // y-axis
  834.         for(int i=yAxisStart; i >= yAxisEnd; i-=offset) {
  835.             makeLine(yAxis-5, i, yAxis+5, i, false, false);
  836.         }
  837.     }
  838.    
  839.    
  840.     // ====================================================================================================================================
  841.     // ====================================================================================================================================
  842.     // ====================================================================================================================================
  843.     // ====================================================================================================================================
  844.     // ====================================================================================================================================
  845.     // ====================================================================================================================================
  846.    
  847.     public void clearScreen() {
  848.         RectProperties rectProps = new RectProperties();
  849.         rectProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.WHITE);
  850.         rectProps.set(AnimationPropertiesKeys.FILLED_PROPERTY, true);
  851.         rectProps.set(AnimationPropertiesKeys.FILL_PROPERTY, Color.WHITE);
  852.        
  853.         new Rect(new AnimalRectGenerator(lang), new Coordinates(0, 0), new Coordinates(2048, 1024), "rect", null, rectProps);
  854.     }
  855.    
  856.    
  857.     public List<Text> showIntroText() {
  858.         List<Text> texts = new ArrayList<>();
  859.        
  860.         Text header = makeText("Separating Axis Theorem (Circle - Triangle)", 100, 100);
  861.         header.changeColor("", Color.BLUE, null, null);
  862.        
  863.         String t1 = "The Separating Axis Theorem (SAT) can be used to check for collisions between two polygons or between a circle and a polygon.";
  864.         String t2 = "The polygons have to be convex, which, roughly speaking, means that any line drawn through the shape crosses it twice, and no more.";
  865.         String t3 = "To help visualizing it in your head: if there is a separating axis, you can draw a line (perpendicular to that separating axis) ";
  866.         String t4 = "which does not touch the objects -> no collision.";
  867.         String t5 = "In the animation there is a small coordinate system. A smaller range was needed because otherwise normalized vectors wouldn't be drawn correctly.";
  868.         String t6 = "The box on the right hand side keeps track of important values/calculations that are needed for the test.";
  869.        
  870.         texts.add(header);
  871.         texts.add(makeText(t1, 100, 150));
  872.         texts.add(makeText(t2, 100, 170));
  873.         texts.add(makeText(t3, 100, 190));
  874.         texts.add(makeText(t4, 100, 210));
  875.         texts.add(makeText(t5, 100, 250));
  876.         texts.add(makeText(t6, 100, 270));
  877.        
  878.         return texts;
  879.     }
  880.    
  881.     public void showIntroQuestion() {
  882.         MultipleChoiceQuestionModel introQuestion = new MultipleChoiceQuestionModel("Intro");
  883.         introQuestion.setPrompt("The dot product of two vectors is less than zero. What can we infer from this?");
  884.         introQuestion.addAnswer("The angle between them is exactly 90 degrees (~they are perpendicular to each other)", 0,
  885.                 "Wrong! That would be the case if their dot product was equal to zero.");
  886.         introQuestion.addAnswer("The angle between them is less than 90 degrees (~ they point in the same direction)", 0,
  887.                 "Wrong! That would be the case if the dot product was greater than zero.");
  888.         introQuestion.addAnswer("The angle between them is greater than 90 degrees (~ they point in opposite directions)", 0, "Correct!");
  889.         lang.addMCQuestion(introQuestion);
  890.     }
  891.    
  892.     public void showOutroQuestions() {
  893.         Text q11 = makeText("Which is a separating axis, the blue/horizontal line, ", 50, 50);
  894.         q11.changeColor("", Color.BLUE, null, null);
  895.         Text q12 = makeText("or the orange/vertical line?", 50, 70);
  896.         q12.changeColor("", Color.BLUE, null, null);
  897.        
  898.         makeCircle(-5, 3, 1, Color.BLACK, false);
  899.         makeTriangle(1, 2, 3, 2, 2, 5);
  900.         Polyline lineHorizontal = makeLine(-10, 0, 10, 0, false, true);
  901.         lineHorizontal.changeColor("", Color.BLUE, null, null);
  902.         Polyline lineVertical = makeLine(-1.5f, 10, -1.5f, -10, false, true);
  903.         lineVertical.changeColor("", Color.ORANGE, null, null);
  904.        
  905.         // insert quiz 1
  906.         MultipleChoiceQuestionModel axisQuestion = new MultipleChoiceQuestionModel("Axis");
  907.         axisQuestion.setPrompt("Which is a separating axis, the blue/horizontal line, or the orange/vertical line?");
  908.         axisQuestion.addAnswer("blue/horizontal", 1, "Correct!");
  909.         axisQuestion.addAnswer("orange/vertical", 0,
  910.                 "Wrong! This line simply goes through the objects, whereby it shows that a separating axis exists, but it itself is not one.");
  911.         lang.addMCQuestion(axisQuestion);
  912.        
  913.         lang.nextStep();
  914.         clearScreen();
  915.        
  916.         Text q21 = makeText("The objects are clearly not colliding. The algorithm currently iterates over vertex A and says there is no separating axis. Why?", 50, 50);
  917.         q21.changeColor("", Color.BLUE, null, null);
  918.         Text q22 = makeText("Does this mean the algorithm has failed?", 50, 70);
  919.         q22.changeColor("", Color.BLUE, null, null);
  920.        
  921.         makeCircle(2, 2, 1, Color.BLACK, false);
  922.         makeTriangle(-2, 1, 0, 2, -3, 3);
  923.         makeText("A", 600 + 40 * (-2) + 5 , 300 - 40 * 1);
  924.         makeText("B", 600 + 40 * 0 + 5 , 300 - 40 * 2);
  925.         makeText("C", 600 + 40 * (-3) + 5 , 300 - 40 * 3);
  926.        
  927.         // insert quiz 2.1
  928.         MultipleChoiceQuestionModel question21 = new MultipleChoiceQuestionModel("Q2.1");
  929.         question21.setPrompt("The objects are clearly not colliding. The algorithm currently iterates over vertex A and says there is no separating axis. Why?");
  930.         question21.addAnswer("The distance from A to the center of the circle is greater than the circle's radius", 0, "Wrong!");
  931.         question21.addAnswer("Vertex B is not on the opposite side of the center", 1, "Correct!");
  932.         question21.addAnswer("Vertex C is not on the opposite side of the center", 0, "Wrong!");
  933.         lang.addMCQuestion(question21);
  934.        
  935.         lang.nextStep();
  936.         // insert quiz 2.2
  937.         MultipleChoiceQuestionModel question22 = new MultipleChoiceQuestionModel("Q2.2");
  938.         question22.setPrompt("Does this mean the algorithm has failed?");
  939.         question22.addAnswer("No, it will find a separating axis when it iterates over B", 1, "Correct!");
  940.         question22.addAnswer("No, it will find a separating axis when it iterates over C", 0, "Wrong!");
  941.         question22.addAnswer("Yes, the algorithm cannot find a separating axis for this case", 0, "Wrong!");
  942.         lang.addMCQuestion(question22);
  943.     }
  944.    
  945.     public void showOutroText() {
  946.         // final words
  947.         Text header = makeText("Final words", 100, 100);
  948.         header.changeColor("", Color.BLUE, null, null);
  949.        
  950.         makeText("SAT for circles and triangles is a special case.", 100, 150);
  951.         makeText("The case can be generalized for circle/polygon or polygon/polygon, as long as the polygons are convex.", 100, 170);
  952.         makeText("The principles remain roughly the same, the biggest difference being the number of vertices and edges that are involved.", 100, 190);
  953.         makeText("Also, keep in mind that we could short-circuit out of the algorithm the moment we find one separating axis.", 100, 230);
  954.         makeText("I have not done so for illustration purposes, so that all cases are shown at all times.", 100, 250);
  955.     }
  956.    
  957.    
  958.     public void showSourceCodeVertexSA() {
  959.         // first set the visual properties for the source code
  960.         SourceCodeProperties scProps = new SourceCodeProperties();
  961.         scProps.set(AnimationPropertiesKeys.CONTEXTCOLOR_PROPERTY, Color.BLUE);
  962.         scProps.set(AnimationPropertiesKeys.FONT_PROPERTY, new Font("Monospaced", Font.PLAIN, 12));
  963.         scProps.set(AnimationPropertiesKeys.HIGHLIGHTCOLOR_PROPERTY, Color.RED);
  964.         scProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.BLACK);
  965.        
  966.         // now create the source code entity
  967.         sc = lang.newSourceCode(new Coordinates(10, 50), "sourceCode", null, sourceCodeProps);
  968.        
  969.         // add code lines (the actual sorting algo)
  970.         // parameters: code itself, name (can be null), indention level, display options
  971.         sc.addCodeLine("boolean isVertexSA(Circle circle, Triangle triangle, Vector2f vertex) {", null, 0, null);
  972.         sc.addCodeLine("float distToCenter = vertex.dist(circle.center);", null, 1, null);
  973.         sc.addCodeLine("Vector2f vertexToCenter = circle.center.sub(vertex);", null, 1, null);
  974.         sc.addCodeLine("Vector2f vertexToNeighbor1;", null, 1, null);
  975.         sc.addCodeLine("Vector2f vertexToNeighbor2;", null, 1, null);
  976.         sc.addCodeLine("", null, 0, null);
  977.         sc.addCodeLine("if(vertex == triangle.A) {", null, 1, null);
  978.         sc.addCodeLine("vertexToNeighbor1 = triangle.B.sub(vertex); // AB", null, 2, null);
  979.         sc.addCodeLine("vertexToNeighbor2 = triangle.C.sub(vertex); // AC", null, 2, null);
  980.         sc.addCodeLine("}", null, 1, null);
  981.         sc.addCodeLine("else if(vertex == triangle.B) {", null, 1, null);
  982.         sc.addCodeLine("vertexToNeighbor1 = triangle.A.sub(vertex); // BA", null, 2, null);
  983.         sc.addCodeLine("vertexToNeighbor2 = triangle.C.sub(vertex); // BC", null, 2, null);
  984.         sc.addCodeLine("}", null, 1, null);
  985.         sc.addCodeLine("else if(vertex == triangle.C) {", null, 1, null);
  986.         sc.addCodeLine("vertexToNeighbor1 = triangle.A.sub(vertex); // CA", null, 2, null);
  987.         sc.addCodeLine("vertexToNeighbor2 = triangle.B.sub(vertex); // CB", null, 2, null);
  988.         sc.addCodeLine("}", null, 1, null);
  989.         sc.addCodeLine("", null, 0, null);
  990.         sc.addCodeLine("// they are on opposite sides", null, 1, null);
  991.         sc.addCodeLine("// if the dot product is less than zero", null, 1, null);
  992.         sc.addCodeLine("boolean isOppositeNeighbor1 = ", null, 1, null);
  993.         sc.addCodeLine("vertexToCenter.dot(vertexToNeighbor1) < 0;", null, 2, null);
  994.         sc.addCodeLine("boolean isOppositeNeighbor2 = ", null, 1, null);
  995.         sc.addCodeLine("vertexToCenter.dot(vertexToNeighbor2) < 0;", null, 2, null);
  996.         sc.addCodeLine("", null, 0, null);
  997.         sc.addCodeLine("return circle.radius < distToCenter", null, 1, null);
  998.         sc.addCodeLine("&& isOppositeNeighbor1 && isOppositeNeighbor2;", null, 2, null);
  999.     } // showSourceCodeVertexSA()
  1000.    
  1001.    
  1002.     public void showSourceCodeEdgeSA() {
  1003.         // first set the visual properties for the source code
  1004.         SourceCodeProperties scProps = new SourceCodeProperties();
  1005.         scProps.set(AnimationPropertiesKeys.CONTEXTCOLOR_PROPERTY, Color.BLUE);
  1006.         scProps.set(AnimationPropertiesKeys.FONT_PROPERTY, new Font("Monospaced", Font.PLAIN, 12));
  1007.         scProps.set(AnimationPropertiesKeys.HIGHLIGHTCOLOR_PROPERTY, Color.RED);
  1008.         scProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.BLACK);
  1009.        
  1010.         // now create the source code entity
  1011.         sc = lang.newSourceCode(new Coordinates(10, 50), "sourceCode", null, sourceCodeProps);
  1012.        
  1013.         // add code lines (the actual sorting algo)
  1014.         // parameters: code itself, name (can be null), indention level, display options
  1015.         sc.addCodeLine("boolean isEdgeSA(Circle circle, Triangle triangle, Vector2f edge, Vector2f start, Vector2f end, Vector2f other) {", null, 0, null);
  1016.         sc.addCodeLine("Vector2f normalizedEdge = edge.normalize();", null, 1, null);
  1017.         sc.addCodeLine("Vector2f vecToCircle;", null, 1, null);
  1018.         sc.addCodeLine("", null, 0, null);
  1019.         sc.addCodeLine("if(edge == triangle.edgeAB) {", null, 1, null);
  1020.         sc.addCodeLine("vecToCircle = circle.center.sub(triangle.A);", null, 2, null);
  1021.         sc.addCodeLine("}", null, 1, null);
  1022.         sc.addCodeLine("else if(edge == triangle.edgeAC) {", null, 1, null);
  1023.         sc.addCodeLine("vecToCircle = circle.center.sub(triangle.A);", null, 2, null);
  1024.         sc.addCodeLine("}", null, 1, null);
  1025.         sc.addCodeLine("else if(edge == triangle.edgeBC) {", null, 1, null);
  1026.         sc.addCodeLine("vecToCircle = circle.center.sub(triangle.B);", null, 2, null);
  1027.         sc.addCodeLine("}", null, 1, null);
  1028.         sc.addCodeLine("float dot = normalizedEdge.dot(vecToCircle);", null, 1, null);
  1029.         sc.addCodeLine("Vector2f closestPointToCircle;", null, 1, null);
  1030.         sc.addCodeLine("if(dot <= 0) {", null, 1, null);
  1031.         sc.addCodeLine("closestPointToCircle = start;", null, 2, null);
  1032.         sc.addCodeLine("}", null, 1, null);
  1033.         sc.addCodeLine("else if(dot >= edge.length()) {", null, 1, null);
  1034.         sc.addCodeLine("closestPointToCircle = end;", null, 2, null);
  1035.         sc.addCodeLine("}", null, 1, null);
  1036.         sc.addCodeLine("else {", null, 1, null);
  1037.         sc.addCodeLine("closestPointToCircle = ", null, 2, null);
  1038.         sc.addCodeLine("normalizedEdge.mul(dot).add(start);", null, 3, null);
  1039.         sc.addCodeLine("}", null, 1, null);
  1040.         sc.addCodeLine("", null, 0, null);
  1041.         sc.addCodeLine("// check if the other vertex", null, 1, null);
  1042.         sc.addCodeLine("// (the one that is not part of the current edge)", null, 1, null);
  1043.         sc.addCodeLine("// and the circle-center lie on opposite sides of the edge", null, 1, null);
  1044.         sc.addCodeLine("Vector2f vecFromClosestPointToCircle        = ", null, 1, null);
  1045.         sc.addCodeLine("circle.center.sub(closestPointToCircle);", null, 2, null);
  1046.         sc.addCodeLine("Vector2f vecFromClosestPointToOtherVertex   = ", null, 1, null);
  1047.         sc.addCodeLine("other.sub(closestPointToCircle); ", null, 2, null);
  1048.         sc.addCodeLine("boolean isOppositeOtherVertex               = vecFromClosestPointToCircle.dot(vecFromClosestPointToOtherVertex) < 0;", null, 1, null);
  1049.         sc.addCodeLine("", null, 0, null);
  1050.         sc.addCodeLine("return circle.radius < vecFromClosestPointToCircle.length() && isOppositeOtherVertex;", null, 1, null);
  1051.     } // showSourceCodeEdgeSA()
  1052.    
  1053.    
  1054.    
  1055.    
  1056.    
  1057.    
  1058.    
  1059.    
  1060.     // ================================================================================================================================
  1061.     // ================================================================================================================================
  1062.     // ================================================================================================================================
  1063.     // ================================================================================================================================
  1064.     // ================================================================================================================================
  1065.     // ================================================================================================================================
  1066.     // ================================================================================================================================
  1067.     // ================================================================================================================================
  1068.    
  1069.    
  1070.  
  1071.     public String getName() {
  1072.         return "Separating Axis Theorem (Circle - Triangle)";
  1073.     }
  1074.  
  1075.     public String getAlgorithmName() {
  1076.         return "Separating Axis Theorem";
  1077.     }
  1078.  
  1079.     public String getAnimationAuthor() {
  1080.         return "Bekir Özkara";
  1081.     }
  1082.  
  1083.     public String getDescription(){
  1084.         return " The Separating Axis Theorem (SAT) can be used to check for collisions between two polygons or"
  1085.  +"\n"
  1086.  +" between a circle and a polygon. The polygons have to be convex, which, roughly speaking, means that "
  1087.  +"\n"
  1088.  +"any line drawn through the shape crosses it twice, and no more. "
  1089.  +"\n"
  1090.  +"To help visualizing it in your head: if there is a separating axis, you can draw a line (perpendicular to that "
  1091.  +"\n"
  1092.  +"separating axis) which does not touch the objects -> no collision."
  1093.  +"\n"
  1094.  +"\n"
  1095.  +"In the animation there is a small coordinate system. A smaller range was needed because otherwise "
  1096.  +"\n"
  1097.  +"normalized vectors wouldn't be drawn correctly."
  1098.  +"\n"
  1099.  +"The box on the right hand side keeps track of important values/calculations that are needed for the test.";
  1100.     }
  1101.  
  1102.     public String getCodeExample(){
  1103.         return "// the implementations of isVertexSA() and isEdgeSA() are shown in the animation"
  1104.  +"\n"
  1105.  +"public static boolean hasSA(Circle circle, Triangle triangle) {"
  1106.  +"\n"
  1107.  +"  boolean separatedByVertexA = isVertexSA(circle, triangle, triangle.A);"
  1108.  +"\n"
  1109.  +"  boolean separatedByVertexB = isVertexSA(circle, triangle, triangle.B);"
  1110.  +"\n"
  1111.  +"  boolean separatedByVertexC = isVertexSA(circle, triangle, triangle.C);"
  1112.  +"\n"
  1113.  +"     "
  1114.  +"\n"
  1115.  +"  boolean separatedByEdgeAB = isEdgeSA(circle, triangle, triangle.edgeAB, triangle.A, triangle.B, triangle.C);"
  1116.  +"\n"
  1117.  +"  boolean separatedByEdgeAC = isEdgeSA(circle, triangle, triangle.edgeAC, triangle.A, triangle.C, triangle.B);"
  1118.  +"\n"
  1119.  +"  boolean separatedByEdgeBC = isEdgeSA(circle, triangle, triangle.edgeBC, triangle.B, triangle.C, triangle.A);"
  1120.  +"\n"
  1121.  +"     "
  1122.  +"\n"
  1123.  +"  return separatedByVertexA || separatedByVertexB || separatedByVertexC || separatedByEdgeAB || separatedByEdgeAC || separatedByEdgeBC;"
  1124.  +"\n"
  1125.  +"}";
  1126.     }
  1127.  
  1128.     public String getFileExtension(){
  1129.         return "asu";
  1130.     }
  1131.  
  1132.     public Locale getContentLocale() {
  1133.         return Locale.ENGLISH;
  1134.     }
  1135.  
  1136.     public GeneratorType getGeneratorType() {
  1137.         return new GeneratorType(GeneratorType.GENERATOR_TYPE_MORE);
  1138.     }
  1139.  
  1140.     public String getOutputLanguage() {
  1141.         return Generator.JAVA_OUTPUT;
  1142.     }
  1143.  
  1144.     @Override
  1145.     public boolean validateInput(AnimationPropertiesContainer props, Hashtable<String, Object> primitives) throws IllegalArgumentException {
  1146.         // TODO
  1147.         // coord-system from -5 to 5 on both axes
  1148.        
  1149.         vertex_c_y = (double)primitives.get("vertex_c_y");
  1150.         vertex_b_y = (double)primitives.get("vertex_b_y");
  1151.         vertex_c_x = (double)primitives.get("vertex_c_x");
  1152.         vertex_a_y = (double)primitives.get("vertex_a_y");
  1153.         vertex_b_x = (double)primitives.get("vertex_b_x");
  1154.         circle_center_x = (double)primitives.get("circle_center_x");
  1155.         circle_center_y = (double)primitives.get("circle_center_y");
  1156.         radius = (double)primitives.get("radius");
  1157.         vertex_a_x = (double)primitives.get("vertex_a_x");
  1158.        
  1159.         boolean inBoundsVertexA = vertexWithinBounds(vertex_a_x) && vertexWithinBounds(vertex_a_y);
  1160.         boolean inBoundsVertexB = vertexWithinBounds(vertex_b_x) && vertexWithinBounds(vertex_b_y);
  1161.         boolean inBoundsVertexC = vertexWithinBounds(vertex_c_x) && vertexWithinBounds(vertex_c_y);
  1162.        
  1163.         return radius > 0 && inBoundsVertexA && inBoundsVertexB && inBoundsVertexC && circleWithinBounds(circle_center_x, circle_center_y, radius) ;
  1164.     }
  1165.    
  1166.     private boolean vertexWithinBounds(double vertex) {
  1167.         return vertex >= -5 && vertex <= 5;
  1168.     }
  1169.    
  1170.     private boolean circleWithinBounds(double center_x, double center_y, double radius) {
  1171.         // TODO: maybe y-coord must be inverted ??
  1172. //      return radius + Math.abs(center_x) <= 5 && radius + Math.abs(center_y) <= 5;
  1173.         return center_x - radius >= -5 && center_x + radius <= 5 && center_y - radius >= -5 && center_y + radius <= 5;
  1174.     }
  1175. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement