Advertisement
bekovski

algovi_with_question

Jul 25th, 2018
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 34.27 KB | None | 0 0
  1. import java.awt.Color;
  2. import java.awt.Font;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import algoanim.animalscript.AnimalCircleGenerator;
  7. import algoanim.animalscript.AnimalPointGenerator;
  8. import algoanim.animalscript.AnimalPolylineGenerator;
  9. import algoanim.animalscript.AnimalRectGenerator;
  10. import algoanim.animalscript.AnimalScript;
  11. import algoanim.animalscript.AnimalTextGenerator;
  12. import algoanim.animalscript.AnimalTriangleGenerator;
  13. import algoanim.primitives.Circle;
  14. import algoanim.primitives.Point;
  15. import algoanim.primitives.Polyline;
  16. import algoanim.primitives.Rect;
  17. import algoanim.primitives.SourceCode;
  18. import algoanim.primitives.Text;
  19. import algoanim.primitives.Triangle;
  20. import algoanim.primitives.generators.Language;
  21. import algoanim.properties.AnimationPropertiesKeys;
  22. import algoanim.properties.CircleProperties;
  23. import algoanim.properties.PointProperties;
  24. import algoanim.properties.PolylineProperties;
  25. import algoanim.properties.RectProperties;
  26. import algoanim.properties.SourceCodeProperties;
  27. import algoanim.properties.TextProperties;
  28. import algoanim.properties.TriangleProperties;
  29. import algoanim.util.Coordinates;
  30. import algoanim.util.Node;
  31. import algoanim.util.TicksTiming;
  32. import algoanim.util.Timing;
  33. import auxiliary.Vector2f;
  34. import interactionsupport.models.MultipleChoiceQuestionModel;
  35.  
  36. public class SAT {
  37.    
  38.     public static final Color LINE_DEFAULT_COLOR    = Color.BLACK;
  39.     public static final Color VECTOR_DEFAULT_COLOR  = Color.RED;
  40.    
  41.     Language lang;
  42.    
  43.     Text text;
  44.     Point point;
  45.     Circle circle;
  46.     Polyline line;
  47.     Polyline edge;
  48.     Polyline vector;
  49.     Polyline separatingAxis;
  50.     Triangle triangle;
  51.    
  52.     // temp
  53.     Polyline[] vectors = new Polyline[3];
  54.    
  55.     SourceCode sc;
  56.     Timing timing;
  57.    
  58.     // might need these properties
  59.     TextProperties textProps;
  60.     RectProperties rectProps;
  61.     PointProperties pointProps;
  62.     CircleProperties circleProps;
  63.     PolylineProperties lineProps;
  64.     PolylineProperties vectorProps;
  65.     TriangleProperties triangleProps;
  66.    
  67.    
  68.     int x = 40;
  69.     int y = 140;
  70.     int startY;
  71.     int offsetY = 20;
  72.     String vertexName;
  73.     String neighbor1Name;
  74.     String neighbor2Name;
  75.    
  76.     Vector2f separatingAxisStart;
  77.     Vector2f separatingAxisEnd;
  78.    
  79.     List<Text> textList = new ArrayList<>();
  80.    
  81.     public void init() {
  82.         lang = new AnimalScript("Separating Axis Theorem (Circle-Triangle)", "Bekir Oezkara", 640, 480);
  83.         lang.setStepMode(true);
  84.         lang.setInteractionType(Language.INTERACTION_TYPE_AVINTERACTION);
  85.        
  86.         timing = new TicksTiming(200);
  87.        
  88.         textProps = new TextProperties();
  89.         textProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.BLACK);
  90.         textProps.set(AnimationPropertiesKeys.FONT_PROPERTY, new Font("Monospaced", Font.PLAIN, 12));
  91.        
  92.         rectProps = new RectProperties();
  93.         rectProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.DARK_GRAY);
  94.         rectProps.set(AnimationPropertiesKeys.FILLED_PROPERTY, true);
  95.         rectProps.set(AnimationPropertiesKeys.FILL_PROPERTY, Color.LIGHT_GRAY);
  96.         rectProps.set(AnimationPropertiesKeys.DEPTH_PROPERTY, 2);
  97.        
  98.         pointProps = new PointProperties();
  99.         pointProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.CYAN);
  100.        
  101.         circleProps = new CircleProperties();
  102. //      circleProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.RED);
  103.         circleProps.set(AnimationPropertiesKeys.FILL_PROPERTY, Color.WHITE);
  104.        
  105.         lineProps = new PolylineProperties();
  106.         lineProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, LINE_DEFAULT_COLOR);
  107.        
  108.         vectorProps = new PolylineProperties();
  109.         vectorProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, VECTOR_DEFAULT_COLOR);
  110.         vectorProps.set(AnimationPropertiesKeys.FWARROW_PROPERTY, true);
  111.        
  112.         triangleProps = new TriangleProperties();
  113. //      triangleProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.GREEN);
  114.         triangleProps.set(AnimationPropertiesKeys.FILL_PROPERTY, Color.WHITE);
  115.     }
  116.    
  117.     public void createScene() {
  118.         makeHeader();
  119.         makeSideBox();
  120.         drawCoordSystem();
  121.     }
  122.    
  123.     public boolean hasSA(auxiliary.Circle circle, auxiliary.Triangle triangle) {
  124.        
  125.         makeCircle(circle.center.x, circle.center.y, circle.radius);
  126.         makeTriangle(triangle.A, triangle.B, triangle.C);
  127.         lang.nextStep();
  128.        
  129.         x = 910;
  130.         startY = 160;
  131.         y = startY;
  132.        
  133.         // vertices
  134.         showSourceCodeVertexSA();
  135.         Circle vertexHighlightCircle;
  136.         boolean isFilled = true;
  137.        
  138.         makeText("A", 600 + 40 * (int)triangle.A.x + 5 , 300 - 40 * (int)triangle.A.y);
  139.         makeText("B", 600 + 40 * (int)triangle.B.x + 5 , 300 - 40 * (int)triangle.B.y);
  140.         makeText("C", 600 + 40 * (int)triangle.C.x + 5 , 300 - 40 * (int)triangle.C.y);
  141.        
  142.         vertexName = "A";
  143.         neighbor1Name = "B";
  144.         neighbor2Name = "C";
  145.         vertexHighlightCircle = makeCircle(triangle.A.x, triangle.A.y, 0.1f, Color.PINK, isFilled);
  146.         textAndStep("Vertex A");
  147.         vertexHighlightCircle.hide();
  148.         boolean separatedByVertexA = isVertexSA(circle, triangle, triangle.A);
  149.        
  150.         clearText();
  151.         y = startY;
  152.        
  153.         vertexName = "B";
  154.         neighbor1Name = "A";
  155.         neighbor2Name = "C";
  156.         vertexHighlightCircle = makeCircle(triangle.B.x, triangle.B.y, 0.1f, Color.PINK, isFilled);
  157.         textAndStep("Vertex B");
  158.         vertexHighlightCircle.hide();
  159.         boolean separatedByVertexB = isVertexSA(circle, triangle, triangle.B);
  160.        
  161.         clearText();
  162.         y = startY;
  163.        
  164.         vertexName = "C";
  165.         neighbor1Name = "A";
  166.         neighbor2Name = "B";
  167.         vertexHighlightCircle = makeCircle(triangle.C.x, triangle.C.y, 0.1f, Color.PINK, isFilled);
  168.         textAndStep("Vertex C");
  169.         vertexHighlightCircle.hide();
  170.         boolean separatedByVertexC = isVertexSA(circle, triangle, triangle.C);
  171.        
  172.         clearText();
  173.         y = startY;
  174.        
  175.         // edges
  176.         sc.changeColor("", Color.WHITE, null, null);
  177.         showSourceCodeEdgeSA();
  178.        
  179.         edge = makeLine(triangle.A, triangle.B, false, true);
  180.         edge.changeColor("", Color.PINK, null, null);
  181.         textAndStep("Edge from A to B");
  182.         boolean separatedByEdgeAB = isEdgeSA(circle, triangle, triangle.edgeAB, triangle.A, triangle.B, triangle.C);
  183.         edge.hide();
  184.        
  185.         clearText();
  186.         y = startY;
  187.        
  188.         edge = makeLine(triangle.A, triangle.C, false, true);
  189.         edge.changeColor("", Color.PINK, null, null);
  190.         textAndStep("Edge from A to C");
  191.         boolean separatedByEdgeAC = isEdgeSA(circle, triangle, triangle.edgeAC, triangle.A, triangle.C, triangle.B);
  192.         edge.changeColor("", Color.BLACK, null, null);
  193.        
  194.         clearText();
  195.         y = startY;
  196.        
  197.         edge = makeLine(triangle.B, triangle.C, false, true);
  198.         edge.changeColor("", Color.PINK, null, null);
  199.         textAndStep("Edge from B to C");
  200.         boolean separatedByEdgeBC = isEdgeSA(circle, triangle, triangle.edgeBC, triangle.B, triangle.C, triangle.A);
  201.         edge.hide();
  202.        
  203.         clearText();
  204.         y = startY;
  205.        
  206.         boolean result = separatedByVertexA || separatedByVertexB || separatedByVertexC || separatedByEdgeAB || separatedByEdgeAC || separatedByEdgeBC;
  207.         if(result) {
  208.             separatingAxis = makeLine(separatingAxisStart, separatingAxisEnd, false, true);
  209.             separatingAxis.changeColor("", Color.GREEN, null, null);
  210.         }
  211.         textAndStep("we found separating axis = " + result);
  212.         return result;
  213.     }
  214.    
  215.    
  216.     /**
  217.      *
  218.      * @param circle
  219.      * @param triangle
  220.      * @param vertex
  221.      * @return
  222.      */
  223.     private boolean isVertexSA(auxiliary.Circle circle, auxiliary.Triangle triangle, Vector2f vertex) {
  224.         line = makeLine(circle.center.x, circle.center.y, circle.center.x + circle.radius, circle.center.y, false, true);
  225.         line.changeColor("", Color.BLUE, null, null);
  226.         textAndStep("radius = " + circle.radius);
  227.         line.hide();
  228.        
  229.         sc.highlight(1);
  230.         float distToCenter = vertex.dist(circle.center);
  231.         line = makeLine(vertex, circle.center, false, true);
  232.         line.changeColor("", Color.BLUE, null, null);
  233.         textAndStep("distance to center = " + distToCenter);
  234.         line.hide();
  235.         sc.unhighlight(1);
  236.        
  237.        
  238.         sc.highlight(2);
  239.         Vector2f vertexToCenter = circle.center.sub(vertex);
  240.         vectors[0] = makeLine(vertex, circle.center, true, true);
  241.         textAndStep("vector from vertex to center = " + vertexToCenter);
  242.         sc.unhighlight(2);
  243.        
  244.         Vector2f vertexToNeighbor1;
  245.         Vector2f vertexToNeighbor2;
  246.        
  247.         if(vertex == triangle.A) {
  248.             sc.highlight(6);
  249.             sc.highlight(7);
  250.             sc.highlight(9);
  251.             vertexToNeighbor1 = triangle.B.sub(vertex); // AB
  252.             vertexToNeighbor2 = triangle.C.sub(vertex); // AC
  253.             neighbor1Name = "B";
  254.             neighbor2Name = "C";
  255.             vectors[1] = makeLine(vertex, triangle.B, true, true);
  256.             textAndStep("vector from " + vertexName + " to " + neighbor1Name + " = " + vertexToNeighbor1);
  257.             sc.unhighlight(7);
  258.            
  259.             sc.highlight(8);
  260.             vectors[2] = makeLine(vertex, triangle.C, true, true);
  261.             textAndStep("vector from " + vertexName + " to " + neighbor2Name + " = " + vertexToNeighbor2);
  262.             sc.unhighlight(6);
  263.             sc.unhighlight(8);
  264.             sc.unhighlight(9);
  265.         }
  266.         else if(vertex == triangle.B) {
  267.             sc.highlight(10);
  268.             sc.highlight(11);
  269.             sc.highlight(13);
  270.             vertexToNeighbor1 = triangle.A.sub(vertex); // BA
  271.             vertexToNeighbor2 = triangle.C.sub(vertex); // BC
  272.             neighbor1Name = "A";
  273.             neighbor2Name = "C";
  274.             vectors[1] = makeLine(vertex, triangle.A, true, true);
  275.             textAndStep("vector from " + vertexName + " to " + neighbor1Name + " = " + vertexToNeighbor1);
  276.             sc.unhighlight(11);
  277.            
  278.             sc.highlight(12);
  279.             vectors[2] = makeLine(vertex, triangle.C, true, true);
  280.             textAndStep("vector from " + vertexName + " to " + neighbor2Name + " = " + vertexToNeighbor2);
  281.             sc.unhighlight(10);
  282.             sc.unhighlight(12);
  283.             sc.unhighlight(13);
  284.         }
  285.         else if(vertex == triangle.C) {
  286.             sc.highlight(14);
  287.             sc.highlight(15);
  288.             sc.highlight(17);
  289.             vertexToNeighbor1 = triangle.A.sub(vertex); // CA
  290.             vertexToNeighbor2 = triangle.B.sub(vertex); // CB
  291.             neighbor1Name = "A";
  292.             neighbor2Name = "B";
  293.             vectors[1] = makeLine(vertex, triangle.A, true, true);
  294.             textAndStep("vector from " + vertexName + " to " + neighbor1Name + " = " + vertexToNeighbor1);
  295.             sc.unhighlight(15);
  296.            
  297.             sc.highlight(16);
  298.             vectors[2] = makeLine(vertex, triangle.B, true, true);
  299.             textAndStep("vector from " + vertexName + " to " + neighbor2Name + " = " + vertexToNeighbor2);
  300.             sc.unhighlight(14);
  301.             sc.unhighlight(16);
  302.             sc.unhighlight(17);
  303.         }
  304.         else {
  305.             throw new IllegalArgumentException("The provided vertex: " + vertex + " does not match with any of the triangle's vertices.");
  306.         }
  307.        
  308. //      textAndStep("vector from " + vertexName + " to " + neighbor1Name + " = " + vertexToNeighbor1);
  309. //      textAndStep("vector from " + vertexName + " to " + neighbor2Name + " = " + vertexToNeighbor2);
  310.  
  311.         // they are on opposite sides if the dot product is less than zero
  312.         vectors[0].changeColor("", Color.ORANGE, null, null);
  313.        
  314.         sc.highlight(21);
  315.         sc.highlight(22);
  316.         boolean isOppositeNeighbor1 = vertexToCenter.dot(vertexToNeighbor1) < 0;
  317.         vectors[1].changeColor("", Color.ORANGE, null, null);
  318.         textAndStep(neighbor1Name + " is on opposite side = " + isOppositeNeighbor1);
  319.         vectors[1].changeColor("", VECTOR_DEFAULT_COLOR, null, null);
  320.         sc.unhighlight(21);
  321.         sc.unhighlight(22);
  322.        
  323.         sc.highlight(23);
  324.         sc.highlight(24);
  325.         boolean isOppositeNeighbor2 = vertexToCenter.dot(vertexToNeighbor2) < 0;
  326.         vectors[2].changeColor("", Color.ORANGE, null, null);
  327.         textAndStep(neighbor2Name + " is on opposite side = " + isOppositeNeighbor2);
  328.         sc.unhighlight(23);
  329.         sc.unhighlight(24);
  330.        
  331.         vectors[0].hide();
  332.         vectors[1].hide();
  333.         vectors[2].hide();
  334.        
  335.         sc.highlight(26);
  336.         boolean radius_less_than_dist = circle.radius < distToCenter;
  337.         textAndStep("radius smaller than distance = " + radius_less_than_dist);
  338.        
  339.         boolean result = radius_less_than_dist && isOppositeNeighbor1 && isOppositeNeighbor2;
  340.         line = makeLine(vertex, circle.center, false, true);
  341.         line.changeColor("", result ? Color.GREEN : Color.RED, null, null);
  342.         if(result) {
  343.             separatingAxisStart = new Vector2f(vertex.x, vertex.y);
  344.             separatingAxisEnd   = new Vector2f(circle.center.x, circle.center.y);
  345. //          separatingAxis =  makeLine(vertex, circle.center, false, true);
  346.         }
  347.         sc.highlight(27);
  348.         textAndStep("vertex is separating axis = " + result);
  349.         line.hide();
  350.         sc.unhighlight(26);
  351.         sc.unhighlight(27);
  352. //      if(separatingAxis != null) {
  353. //          separatingAxis.hide(); 
  354. //      }
  355.         return result;
  356.     }
  357.    
  358.    
  359.     /**
  360.      *
  361.      * @param circle
  362.      * @param triangle
  363.      * @param edge
  364.      * @param start
  365.      * @param end
  366.      * @param other
  367.      * @return
  368.      */
  369.     private boolean isEdgeSA(auxiliary.Circle circle, auxiliary.Triangle triangle, Vector2f edge, Vector2f start, Vector2f end, Vector2f other) {
  370.         line = makeLine(circle.center.x, circle.center.y, circle.center.x + circle.radius, circle.center.y, false, true);
  371.         // line.getProperties().set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.BLUE);
  372.         line.changeColor("", Color.BLUE, null, null);
  373.         textAndStep("radius = " + circle.radius);
  374.         line.hide();
  375.        
  376.         sc.highlight(1);
  377.         Vector2f normalizedEdge = edge.normalize();
  378.         vector = makeLine(start.x, start.y, start.x + normalizedEdge.x, start.y + normalizedEdge.y, true, true);
  379.         vector.changeColor("", VECTOR_DEFAULT_COLOR, null, null);
  380.         textAndStep("normalized edge = " + normalizedEdge);
  381.         vector.hide();
  382.         sc.unhighlight(1);
  383.        
  384.         Vector2f vecToCircle;
  385.         if(edge == triangle.edgeAB) {
  386.             sc.highlight(4);
  387.             sc.highlight(5);
  388.             sc.highlight(6);
  389.             vecToCircle = circle.center.sub(triangle.A);
  390.             vector = makeLine(triangle.A, circle.center, true, true);
  391.             textAndStep("vector to circle = " + vecToCircle);
  392.             vector.hide();
  393.             sc.unhighlight(4);
  394.             sc.unhighlight(5);
  395.             sc.unhighlight(6);
  396.         }
  397.         else if(edge == triangle.edgeAC) {
  398.             sc.highlight(7);
  399.             sc.highlight(8);
  400.             sc.highlight(9);
  401.             // TODO: why not C, or: when A, when C ?
  402.             vecToCircle = circle.center.sub(triangle.A);
  403.             vector = makeLine(triangle.A, circle.center, true, true);
  404.             textAndStep("vector to circle = " + vecToCircle);
  405.             vector.hide();
  406.             sc.unhighlight(7);
  407.             sc.unhighlight(8);
  408.             sc.unhighlight(9);
  409.         }
  410.         else if(edge == triangle.edgeBC) {
  411.             sc.highlight(10);
  412.             sc.highlight(11);
  413.             sc.highlight(12);
  414.             vecToCircle = circle.center.sub(triangle.B);
  415.             vector = makeLine(triangle.B, circle.center, true, true);
  416.             textAndStep("vector to circle = " + vecToCircle);
  417.             vector.hide();
  418.             sc.unhighlight(10);
  419.             sc.unhighlight(11);
  420.             sc.unhighlight(12);
  421.         }
  422.         else {
  423.             throw new IllegalArgumentException("The provided edge: " + edge + " does not match with any of the triangle's edges.");
  424.         }
  425.        
  426.         sc.highlight(13);
  427.         float dot = normalizedEdge.dot(vecToCircle);
  428.         // line = makeLine(start.x, start.y, start.x + dot, start.y + dot, false, true);
  429.         // TODO: fix dot product (get direction right!)
  430.         line = makeLine(start, start.add(normalizedEdge.mul(dot)), false, true);        // is this right ?
  431.         line.changeColor("", Color.CYAN, null, null);
  432.         textAndStep("dot product of normalizedEdge and vectorToCircle = " + dot);
  433.         line.hide();
  434.         sc.unhighlight(13);
  435.        
  436.         Vector2f closestPointToCircle;
  437.         int codeLine1 = 0;
  438.         int codeLine2 = 0;
  439.         if(dot <= 0) {
  440.             codeLine1 = 16;
  441.             sc.highlight(codeLine1);
  442.             closestPointToCircle = start;
  443.         }
  444.         else if(dot >= edge.length()) {
  445.             codeLine1 = 19;
  446.             sc.highlight(codeLine1);
  447.             closestPointToCircle = end;
  448.         }
  449.         else {
  450.             codeLine1 = 22;
  451.             codeLine2 = 23;
  452.             sc.highlight(codeLine1);
  453.             sc.highlight(codeLine2);
  454.             closestPointToCircle = normalizedEdge.mul(dot).add(start);
  455.         }
  456.        
  457.         Circle pointToHighlight;
  458.         boolean isFilled = true;
  459.        
  460.         line = makeLine(start, closestPointToCircle, false, true);
  461.         line.changeColor("", Color.CYAN, null, null);
  462.         pointToHighlight = makeCircle(closestPointToCircle.x, closestPointToCircle.y, 0.1f, Color.PINK, isFilled);
  463.         textAndStep("closest point to circle = " + closestPointToCircle);
  464.         pointToHighlight.hide();
  465.         line.hide();
  466.         sc.unhighlight(codeLine1);
  467.         sc.unhighlight(codeLine2);
  468.        
  469.         // 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
  470.         sc.highlight(29);
  471.         sc.highlight(30);
  472.         Vector2f vecFromClosestPointToCircle        = circle.center.sub(closestPointToCircle);
  473.         vectors[0] = makeLine(closestPointToCircle, circle.center, true, true);
  474.         textAndStep("vector from closest point to circle = " + vecFromClosestPointToCircle);
  475.         sc.unhighlight(29);
  476.         sc.unhighlight(30);
  477.        
  478.         sc.highlight(31);
  479.         sc.highlight(32);
  480.         Vector2f vecFromClosestPointToOtherVertex   = other.sub(closestPointToCircle);
  481.         vectors[1] = makeLine(closestPointToCircle, other, true, true);
  482.         textAndStep("vector from closest point to other vertex = " + vecFromClosestPointToOtherVertex);
  483.         sc.unhighlight(31);
  484.         sc.unhighlight(32);
  485.        
  486.         sc.highlight(33);
  487.         boolean isOppositeOtherVertex               = vecFromClosestPointToCircle.dot(vecFromClosestPointToOtherVertex) < 0;
  488.         vectors[0].changeColor("", Color.BLUE, null, null);
  489.         vectors[1].changeColor("", Color.BLUE, null, null);
  490.         textAndStep("other vertex on opposite side of circle = " + isOppositeOtherVertex);
  491.         sc.unhighlight(33);
  492.        
  493.         vectors[0].hide();
  494.         vectors[1].hide();
  495.        
  496.         sc.highlight(35);
  497.         boolean radius_less_than_dist_to_closest_point = circle.radius < vecFromClosestPointToCircle.length();
  498.         textAndStep("radius less than distance to closest point = " + radius_less_than_dist_to_closest_point);
  499.        
  500.         boolean result = radius_less_than_dist_to_closest_point && isOppositeOtherVertex;
  501.         line = makeLine(closestPointToCircle, circle.center, false, true);
  502.         line.changeColor("", result ? Color.GREEN : Color.RED, null, null);
  503.         if(result) {
  504.             separatingAxisStart = new Vector2f(closestPointToCircle.x, closestPointToCircle.y);
  505.             separatingAxisEnd   = new Vector2f(circle.center.x, circle.center.y);
  506. //          separatingAxis = makeLine(closestPointToCircle, circle.center, false, true);
  507.         }
  508.         textAndStep("is separating axis = " + result);
  509.         line.hide();
  510.         sc.unhighlight(35);
  511. //      if(separatingAxis != null) {
  512. //          separatingAxis.hide(); 
  513. //      }
  514.         return result;
  515.     }
  516.    
  517.    
  518.     // ====================================================================================================================================
  519.     // ====================================================================================================================================
  520.    
  521.     private Circle makeCircle(float x, float y, float radius) {
  522.         return new Circle(new AnimalCircleGenerator(lang), transformCoords(x, y), transformRadius(radius), "circle", null, circleProps);
  523.     }
  524.    
  525.     private Circle makeCircle(float x, float y, float radius, Color color, boolean isFilled) {
  526.         CircleProperties props = new CircleProperties();
  527.         props.set(AnimationPropertiesKeys.FILL_PROPERTY, color);
  528.         props.set(AnimationPropertiesKeys.FILLED_PROPERTY, isFilled);
  529.         return new Circle(new AnimalCircleGenerator(lang), transformCoords(x, y), transformRadius(radius), "circle", null, props);
  530.     }
  531.    
  532.     private Triangle makeTriangle(Vector2f A, Vector2f B, Vector2f C) {
  533.         return makeTriangle(A.x, A.y, B.x, B.y, C.x, C.y);
  534.     }
  535.    
  536.     private Triangle makeTriangle(float Ax, float Ay, float Bx, float By, float Cx, float Cy) {
  537.         return new Triangle(new AnimalTriangleGenerator(lang),
  538.                 transformCoords(Ax, Ay),
  539.                 transformCoords(Bx, By),
  540.                 transformCoords(Cx, Cy),
  541.                 "triangle",
  542.                 null,
  543.                 triangleProps);
  544.     }
  545.    
  546.     private Coordinates transformCoords(float x, float y) {
  547.         float newX = 600 + 40 * x;
  548.         float newY = 300 - 40 * y;
  549.         return new Coordinates((int)newX, (int)newY);
  550.     }
  551.    
  552.     private int transformRadius(float radius) {
  553.         float newRadius = 40 * radius;
  554.         return (int)newRadius;
  555.     }
  556.    
  557.     private Rect makeRect(int upperLeftX, int upperLeftY, int lowerRightX, int lowerRightY) {
  558.         return new Rect(new AnimalRectGenerator(lang), new Coordinates(upperLeftX, upperLeftY), new Coordinates(lowerRightX, lowerRightY), "rect", null, rectProps);
  559.     }
  560.    
  561.     private Text makeText(String text, int x, int y) {
  562.         return new Text(new AnimalTextGenerator(lang), new Coordinates(x, y), text, "text", null, textProps);
  563.     }
  564.    
  565.     private void textAndStep(String text) {
  566.         textAndStep(text, this.x, this.y);
  567.     }
  568.    
  569.     private void textAndStep(String text, int x, int y) {
  570.         textList.add(makeText(text, x, y));
  571.         this.y += offsetY;
  572.         lang.nextStep();
  573.     }
  574.    
  575.     private void clearText() {
  576.         for(Text text : textList) {
  577.             text.setText("", null, null);
  578.         }
  579.        
  580.         textList = new ArrayList<>();
  581.     }
  582.    
  583.    
  584.     private Polyline makeLine(Vector2f from, Vector2f to, boolean isVector, boolean needsTransform) {
  585.         return makeLine(from.x, from.y, to.x, to.y, isVector, needsTransform);
  586.     }
  587.    
  588.     private Polyline makeLine(float startX, float startY, float endX, float endY, boolean isVector, boolean needsTransform) {
  589.         Node[] nodeArray;
  590.         if(needsTransform) {
  591.             nodeArray = new Node[] {
  592.                     transformCoords(startX, startY),
  593.                     transformCoords(endX, endY)
  594.             };
  595.         }
  596.         else {
  597.             nodeArray = new Node[] {
  598.                     new Coordinates((int)startX, (int)startY),
  599.                     new Coordinates((int)endX, (int)endY)
  600.             };
  601.         }
  602.        
  603.         return new Polyline(new AnimalPolylineGenerator(lang), nodeArray, "line", null, isVector ? vectorProps : lineProps);
  604.     }
  605.    
  606. //  private Polyline makeLine(Vector2f from, Vector2f to, boolean isVector, boolean needsTransform, Color color) {
  607. //      return makeLine(from.x, from.y, to.x, to.y, isVector, needsTransform, color);
  608. //  }
  609.    
  610. //  private Polyline makeLine(float startX, float startY, float endX, float endY, boolean isVector, boolean needsTransform, Color color) {
  611. //      Node[] nodeArray;
  612. //      if(needsTransform) {
  613. //          nodeArray = new Node[] {
  614. //                  transformCoords(startX, startY),
  615. //                  transformCoords(endX, endY)
  616. //          };
  617. //      }
  618. //      else {
  619. //          nodeArray = new Node[] {
  620. //                  new Coordinates((int)startX, (int)startY),
  621. //                  new Coordinates((int)endX, (int)endY)
  622. //          };
  623. //      }
  624. //     
  625. //      Polyline result = new Polyline(new AnimalPolylineGenerator(lang), nodeArray, "line", null, isVector ? vectorProps : lineProps);
  626. //      result.getProperties().set(AnimationPropertiesKeys.COLOR_PROPERTY, color);
  627. //     
  628. //      return result;
  629. //  }
  630.    
  631.     private void makeHeader() {
  632.         makeRect(430, 25, 790, 50);
  633.        
  634.         TextProperties titleProps = new TextProperties();
  635.         titleProps.set(AnimationPropertiesKeys.FONT_PROPERTY, new Font("Arial", Font.BOLD, 16));
  636.         new Text(new AnimalTextGenerator(lang), new Coordinates(450, 27), "Separating Axis Theorem (Circle-Triangle)", "title", null, titleProps);
  637.     }
  638.    
  639.     private void makeSideBox() {
  640.         makeRect(900, 150, 1400, 380);
  641.     }
  642.    
  643.     private void drawCoordSystem() {
  644.         int xAxis       = 300;
  645.         int xAxisStart  = 400;
  646.         int xAxisEnd    = 800;
  647.        
  648.         int yAxis       = 600;
  649.         int yAxisStart  = 500;
  650.         int yAxisEnd    = 100;
  651.        
  652.         makeLine(xAxisStart, xAxis, xAxisEnd, xAxis, false, false); // x-axis (600px)
  653.         makeLine(yAxis, yAxisStart, yAxis, yAxisEnd, false, false); // y-axis (400px)
  654.        
  655.         int offset = 40;    // 60px = 1cm
  656.        
  657.         // x-axis
  658.         for(int i=xAxisStart; i <= xAxisEnd; i+=offset) {
  659.             makeLine(i, xAxis-5, i, xAxis+5, false, false);
  660.         }
  661.        
  662.         // y-axis
  663.         for(int i=yAxisStart; i >= yAxisEnd; i-=offset) {
  664.             makeLine(yAxis-5, i, yAxis+5, i, false, false);
  665.         }
  666.     }
  667.    
  668.     // ====================================================================================================================================
  669.     // ====================================================================================================================================
  670.     // ====================================================================================================================================
  671.     // ====================================================================================================================================
  672.     // ====================================================================================================================================
  673.     // ====================================================================================================================================
  674.    
  675.     public static void main(String[] args) {
  676.         // careful: animalscript y-coord goes downwards (i.e. 110 is below 100)
  677.         SAT sat = new SAT();
  678.         sat.init();
  679.        
  680.         // TODO: handle user preferences (e.g. coordinates, color properties, etc.)
  681.        
  682.         List<Text> texts = sat.showIntroText();
  683.         sat.lang.nextStep();
  684.         for(Text t : texts) {
  685.             t.hide();
  686.         }
  687.        
  688.         sat.createScene();
  689.        
  690.        
  691.         auxiliary.Circle C = new auxiliary.Circle(new Vector2f(-2, 1), 2);
  692.         // auxiliary.Triangle T = new auxiliary.Triangle(new Vector2f(1, 1), new Vector2f(3, 1), new Vector2f(2, 4));
  693.         auxiliary.Triangle T = new auxiliary.Triangle(new Vector2f(-1, -2), new Vector2f(3, 1), new Vector2f(2, 4));
  694.         sat.hasSA(C, T);
  695.        
  696.         sat.clearScreen();
  697.         sat.showOutroText();
  698.        
  699.         sat.lang.finalizeGeneration();
  700.         System.out.println(sat.lang);
  701.     }
  702.    
  703.     public void clearScreen() {
  704.         RectProperties rectProps = new RectProperties();
  705.         rectProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.WHITE);
  706.         rectProps.set(AnimationPropertiesKeys.FILLED_PROPERTY, true);
  707.         rectProps.set(AnimationPropertiesKeys.FILL_PROPERTY, Color.WHITE);
  708.        
  709.         new Rect(new AnimalRectGenerator(lang), new Coordinates(0, 0), new Coordinates(2048, 1024), "rect", null, rectProps);
  710.     }
  711.    
  712.    
  713.     public List<Text> showIntroText() {
  714.         List<Text> texts = new ArrayList<>();
  715.        
  716.         Text header = makeText("Separating Axis Theorem (Circle - Triangle)", 100, 100);
  717.         header.changeColor("", Color.BLUE, null, null);
  718.        
  719.         String t1 = "The Separating Axis Theorem (SAT) can be used to check for collisions between two polygons or between a circle and a polygon.";
  720.         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.";
  721.         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) ";
  722.         String t4 = "which does not touch the objects -> no collision.";
  723.         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.";
  724.         String t6 = "The box on the right hand side keeps track of important values/calculations that are needed for the test.";
  725.        
  726.         texts.add(header);
  727.         texts.add(makeText(t1, 100, 150));
  728.         texts.add(makeText(t2, 100, 170));
  729.         texts.add(makeText(t3, 100, 190));
  730.         texts.add(makeText(t4, 100, 210));
  731.         texts.add(makeText(t5, 100, 250));
  732.         texts.add(makeText(t6, 100, 270));
  733.        
  734.         return texts;
  735.     }
  736.    
  737.     public void showOutroText() {
  738. //      // insert quiz 1
  739. //      MultipleChoiceQuestionModel questionModel = new MultipleChoiceQuestionModel("");
  740. //      questionModel.setPrompt("Which is a separating axis, the blue/horizontal line, or the orange/vertical line?");
  741. //      questionModel.addAnswer("blue/horizontal", 1, "Correct!");
  742. //      questionModel.addAnswer("orange/vertical", 0,
  743. //              "Wrong! This line simply goes through the objects, whereby it shows that a separating axis exists, but it itself is not one.");
  744. //     
  745. //      lang.addMCQuestion(questionModel);
  746.        
  747.         clearScreen();
  748.         // insert quiz 2.1
  749. //      MultipleChoiceQuestionModel questionModel = new MultipleChoiceQuestionModel("Q2.1");
  750. //      questionModel.setPrompt("The objects are clearly not colliding. The algorithm currently iterates over vertex A and says there is no separating axis. Why?");
  751. //      questionModel.addAnswer("The distance from A to the center of the circle is greater than the circle's radius", 0, "Wrong!");
  752. //      questionModel.addAnswer("Vertex B is not on the opposite side of the center", 1, "Correct!");
  753. //      questionModel.addAnswer("Vertex C is not on the opposite side of the center", 0, "Wrong!");
  754. //      lang.addMCQuestion(questionModel);
  755.        
  756.         // insert quiz 2.2
  757. //      MultipleChoiceQuestionModel questionModel = new MultipleChoiceQuestionModel("Q2.2");
  758. //      questionModel.setPrompt("Does this mean the algorithm has failed?");
  759. //      questionModel.addAnswer("No, it will find a separating axis when it iterates over B", 1, "Correct!");
  760. //      questionModel.addAnswer("No, it will find a separating axis when it iterates over C", 0, "Wrong!");
  761. //      questionModel.addAnswer("Yes, the algorithm cannot find a separating axis for this case", 0, "Wrong!");
  762. //      lang.addMCQuestion(questionModel);
  763.        
  764.        
  765.         // final words
  766.         Text header = makeText("Final words", 100, 100);
  767.         header.changeColor("", Color.BLUE, null, null);
  768.        
  769.         makeText("SAT for circles and triangles is a special case.", 100, 150);
  770.         makeText("The case can be generalized for circle/polygon or polygon/polygon, as long as the polygons are convex.", 100, 170);
  771.         makeText("The principles remain roughly the same, the biggest difference being the number of vertices and edges that are involved.", 100, 190);
  772.         makeText("Also, keep in mind that we could short-circuit out of the algorithm the moment we find one separating axis.", 100, 230);
  773.         makeText("I have not done so for illustration purposes, so that all cases are shown at all times.", 100, 250);
  774.     }
  775.    
  776.    
  777.     public void showSourceCodeVertexSA() {
  778.         // first set the visual properties for the source code
  779.         SourceCodeProperties scProps = new SourceCodeProperties();
  780.         scProps.set(AnimationPropertiesKeys.CONTEXTCOLOR_PROPERTY, Color.BLUE);
  781.         scProps.set(AnimationPropertiesKeys.FONT_PROPERTY, new Font("Monospaced", Font.PLAIN, 12));
  782.         scProps.set(AnimationPropertiesKeys.HIGHLIGHTCOLOR_PROPERTY, Color.RED);
  783.         scProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.BLACK);
  784.        
  785.         // now create the source code entity
  786.         sc = lang.newSourceCode(new Coordinates(10, 50), "sourceCode", null, scProps);
  787.        
  788.         // add code lines (the actual sorting algo)
  789.         // parameters: code itself, name (can be null), indention level, display options
  790.         sc.addCodeLine("boolean isVertexSA(Circle circle, Triangle triangle, Vector2f vertex) {", null, 0, null);
  791.         sc.addCodeLine("float distToCenter = vertex.dist(circle.center);", null, 1, null);
  792.         sc.addCodeLine("Vector2f vertexToCenter = circle.center.sub(vertex);", null, 1, null);
  793.         sc.addCodeLine("Vector2f vertexToNeighbor1;", null, 1, null);
  794.         sc.addCodeLine("Vector2f vertexToNeighbor2;", null, 1, null);
  795.         sc.addCodeLine("", null, 0, null);
  796.         sc.addCodeLine("if(vertex == triangle.A) {", null, 1, null);
  797.         sc.addCodeLine("vertexToNeighbor1 = triangle.B.sub(vertex); // AB", null, 2, null);
  798.         sc.addCodeLine("vertexToNeighbor2 = triangle.C.sub(vertex); // AC", null, 2, null);
  799.         sc.addCodeLine("}", null, 1, null);
  800.         sc.addCodeLine("else if(vertex == triangle.B) {", null, 1, null);
  801.         sc.addCodeLine("vertexToNeighbor1 = triangle.A.sub(vertex); // BA", null, 2, null);
  802.         sc.addCodeLine("vertexToNeighbor2 = triangle.C.sub(vertex); // BC", null, 2, null);
  803.         sc.addCodeLine("}", null, 1, null);
  804.         sc.addCodeLine("else if(vertex == triangle.C) {", null, 1, null);
  805.         sc.addCodeLine("vertexToNeighbor1 = triangle.A.sub(vertex); // CA", null, 2, null);
  806.         sc.addCodeLine("vertexToNeighbor2 = triangle.B.sub(vertex); // CB", null, 2, null);
  807.         sc.addCodeLine("}", null, 1, null);
  808.         sc.addCodeLine("", null, 0, null);
  809.         sc.addCodeLine("// they are on opposite sides", null, 1, null);
  810.         sc.addCodeLine("// if the dot product is less than zero", null, 1, null);
  811.         sc.addCodeLine("boolean isOppositeNeighbor1 = ", null, 1, null);
  812.         sc.addCodeLine("vertexToCenter.dot(vertexToNeighbor1) < 0;", null, 2, null);
  813.         sc.addCodeLine("boolean isOppositeNeighbor2 = ", null, 1, null);
  814.         sc.addCodeLine("vertexToCenter.dot(vertexToNeighbor2) < 0;", null, 2, null);
  815.         sc.addCodeLine("", null, 0, null);
  816.         sc.addCodeLine("return circle.radius < distToCenter", null, 1, null);
  817.         sc.addCodeLine("&& isOppositeNeighbor1 && isOppositeNeighbor2;", null, 2, null);
  818.     } // showSourceCodeVertexSA()
  819.    
  820.    
  821.     public void showSourceCodeEdgeSA() {
  822.         // first set the visual properties for the source code
  823.         SourceCodeProperties scProps = new SourceCodeProperties();
  824.         scProps.set(AnimationPropertiesKeys.CONTEXTCOLOR_PROPERTY, Color.BLUE);
  825.         scProps.set(AnimationPropertiesKeys.FONT_PROPERTY, new Font("Monospaced", Font.PLAIN, 12));
  826.         scProps.set(AnimationPropertiesKeys.HIGHLIGHTCOLOR_PROPERTY, Color.RED);
  827.         scProps.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.BLACK);
  828.        
  829.         // now create the source code entity
  830.         sc = lang.newSourceCode(new Coordinates(10, 50), "sourceCode", null, scProps);
  831.        
  832.         // add code lines (the actual sorting algo)
  833.         // parameters: code itself, name (can be null), indention level, display options
  834.         sc.addCodeLine("boolean isEdgeSA(Circle circle, Triangle triangle, Vector2f edge, Vector2f start, Vector2f end, Vector2f other) {", null, 0, null);
  835.         sc.addCodeLine("Vector2f normalizedEdge = edge.normalize();", null, 1, null);
  836.         sc.addCodeLine("Vector2f vecToCircle;", null, 1, null);
  837.         sc.addCodeLine("", null, 0, null);
  838.         sc.addCodeLine("if(edge == triangle.edgeAB) {", null, 1, null);
  839.         sc.addCodeLine("vecToCircle = circle.center.sub(triangle.A);", null, 2, null);
  840.         sc.addCodeLine("}", null, 1, null);
  841.         sc.addCodeLine("else if(edge == triangle.edgeAC) {", null, 1, null);
  842.         sc.addCodeLine("vecToCircle = circle.center.sub(triangle.A);", null, 2, null);
  843.         sc.addCodeLine("}", null, 1, null);
  844.         sc.addCodeLine("else if(edge == triangle.edgeBC) {", null, 1, null);
  845.         sc.addCodeLine("vecToCircle = circle.center.sub(triangle.B);", null, 2, null);
  846.         sc.addCodeLine("}", null, 1, null);
  847.         sc.addCodeLine("float dot = normalizedEdge.dot(vecToCircle);", null, 1, null);
  848.         sc.addCodeLine("Vector2f closestPointToCircle;", null, 1, null);
  849.         sc.addCodeLine("if(dot <= 0) {", null, 1, null);
  850.         sc.addCodeLine("closestPointToCircle = start;", null, 2, null);
  851.         sc.addCodeLine("}", null, 1, null);
  852.         sc.addCodeLine("else if(dot >= edge.length()) {", null, 1, null);
  853.         sc.addCodeLine("closestPointToCircle = end;", null, 2, null);
  854.         sc.addCodeLine("}", null, 1, null);
  855.         sc.addCodeLine("else {", null, 1, null);
  856.         sc.addCodeLine("closestPointToCircle = ", null, 2, null);
  857.         sc.addCodeLine("normalizedEdge.mul(dot).add(start);", null, 3, null);
  858.         sc.addCodeLine("}", null, 1, null);
  859.         sc.addCodeLine("", null, 0, null);
  860.         sc.addCodeLine("// check if the other vertex", null, 1, null);
  861.         sc.addCodeLine("// (the one that is not part of the current edge)", null, 1, null);
  862.         sc.addCodeLine("// and the circle-center lie on opposite sides of the edge", null, 1, null);
  863.         sc.addCodeLine("Vector2f vecFromClosestPointToCircle        = ", null, 1, null);
  864.         sc.addCodeLine("circle.center.sub(closestPointToCircle);", null, 2, null);
  865.         sc.addCodeLine("Vector2f vecFromClosestPointToOtherVertex   = ", null, 1, null);
  866.         sc.addCodeLine("other.sub(closestPointToCircle); ", null, 2, null);
  867.         sc.addCodeLine("boolean isOppositeOtherVertex               = vecFromClosestPointToCircle.dot(vecFromClosestPointToOtherVertex) < 0;", null, 1, null);
  868.         sc.addCodeLine("", null, 0, null);
  869.         sc.addCodeLine("return circle.radius < vecFromClosestPointToCircle.length() && isOppositeOtherVertex;", null, 1, null);
  870.     } // showSourceCodeEdgeSA()
  871. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement