Indoor Navigation

edited February 2017 in Library Questions

Hello.

I asked a similar question to this a few days ago, please see: https://forum.processing.org/two/discussion/20564/indoor-navigation-using-rfid-technology#latest - but the spec of my application has now changed.

I am new to Processing and a beginner when it comes to programming in general so apologies for my lack of correct vocabulary when it comes to explaining things.

I am trying to create an application which gives the user a top-down view of a hospital building plan and allows them to choose, from a drop down menu, where they would like to go within the hospital. Each location has predefined co-ordinates and when the user chooses that location, a line will be draw from their current location to the location they have chosen. Obviously the line that is draw cannot be straight from point to point, as that would mean the line would be cutting through 'rooms' and 'walls' etc. The lines would have to follow the 'corridors' of the hospital.

Is this possible in Processing or should I be considering different Android development programs?

Any help/tutorials to set me on my way with the code will be much appreciated.

Answers

  • Well, possible

    But user has to enter his position A also in the ground floor plan since gps doesn't work in most buildings accurate enough

    But you also need a data structure that lets you solve this problem- see path finding algorithm

    Quark has a library for that iirc

  • @Chrisir thank you for you reply.

    What if the user always starts at the same point (the entrance of the hospital). This app doesn't have to be spectacular and polished, just something that will demonstrate my idea.

    path finding algorithm

    What do you mean by this? Is this something I should Google?

    Thanks

  • Google it or see Wikipedia

    Quarks library is a processing extension for this problem

  • @Chrisir

    Okay, thank you for your help :)

  • When the starting point is always the same and im your target list there are only 5 or 10 targets, you don't need a path finder really

    Just store the points of the cross roads for each target

  • A full implementation would be a list of waypoints and a path finding algorithm.

    If you are working with a simple list of paths you can save them as a list of lists of PVectors / points or save them as a list of PShapes.

    Or you could just implement each path as a simple function that calls line over and over:

    void drawPathToRoomOne(x,y){
      pushMatrix();
      translate(x,y);
      line(0,0,100,0);
      line(100,0,100,100);
      line(100,100,150,150);
      ellipse(150,150,10,10);
      popMatrix();
    }
    
  • @jeremydouglass Thank you for your reply, it has really helped me make progress with this project and allowed me to learn other things!

  • @M_Rigby -- very glad it was helpful! Good luck with your project.

    To see a PShape-based example of containing multiple child shapes -- either for storing multiple paths, or for animating a single path -- then check out the PShape example in this recent related discussion:

  • @jeremydouglass - Hello again. I have progressed quite far with my project but was wondering how the code you sent me could be modified so the lines were dotted lines? and also have the ellipse follow the the line paths?

  • edited February 2017 Answer ✓

    Use this function, replace all "line()" calls with "dottedLine()":

    void dottedLine(float x1, float y1, float x2, float y2){
      for (int i = 0; i <= 20; i+=2) {
        float x = lerp(x1, x2, i/20.0) ;
        float y = lerp(y1, y2, i/20.0);
        float px = lerp(x1, x2, (i + 1)/20.0);
        float py = lerp(y1, y2, (i + 1)/20.0);
        line(x, y, px, py);
      }
    }
    
  • Not very good, but will do the job.

  • @Lord_of_the_Galaxy - Thank you so much! This is fantastic and its exactly what I wanted :) Any ideas on how to get the ellipse to follow the path?

  • An idea will be to add all the dots of your lines to an array, then your ellipse (representing a person) will follow all the items in the array in sequence.

    Kf

  • Look at lerp () for a smooth transition between points

  • edited February 2017

    @M_Rigby --

    and also have the ellipse follow the the line paths?

    Do you mean, how can this approach be turned into an animation? It would help if you provided a simple example sketch that showed exactly what you are trying to do and where you are stuck.

    One very simple approach is to put all the animation steps in a function, then call the function with an argument telling it up-to-what-point you want it to render. Cascading animation could be done manually with a switch statement that contains no breaks between case statements.

    ...like this:

    /**
     * Path Animation 1 - switch with no breaks
     * 2017-02-07 Jeremy Douglass - Processing 3.2.3
     * https:// forum.processing.org/two/discussion/20609/indoor-navigation
     */
    void setup() {
      size(200, 200);
      frameRate(1);
    }
    void draw() {
      background(192);
      drawPathToRoomOne(10, 10, (frameCount-1)%4);
    }
    void drawPathToRoomOne(float x, float y, int step) {
      pushMatrix();
      translate(x, y);
      switch(step) {
      case 2: 
        line(100, 100, 150, 150);
        ellipse(150, 150, 10, 10);
      case 1: 
        line(100, 0, 100, 100);
      case 0: 
        line(0, 0, 100, 0);
      }
      popMatrix();
    }
    

    This works. However: trying to always add a standard element (like an ellipse) at the end of the last path gets tricky when using this approach. You could add a separate switch statement with breaks just for drawing path ends. Or you could just duplicate every animation step in every case (ugh), with a separate ellipse and a break at the each end.

    /**
     * Path Animation 2 - switch with redundant instructions in cases
     * 2017-02-07 Jeremy Douglass - Processing 3.2.3
     * https:// forum.processing.org/two/discussion/20609/indoor-navigation
     */
    void setup() {
      size(200, 200);
      frameRate(1);
    }
    void draw() {
      background(192);
      drawPathToRoomOne(10, 10, (frameCount-1)%4);
    }
    void drawPathToRoomOne(float x, float y, int step) {
      pushMatrix();
      translate(x, y);
      switch(step) {
      case 0: 
        ellipse(0, 0, 10, 10);
        break;
      case 1: 
        line(0, 0, 100, 0);
        ellipse(100, 0, 10, 10);
        break;
      case 2: 
        line(0, 0, 100, 0);
        line(100, 0, 100, 100);
        ellipse(100, 100, 10, 10);
        break;
      case 3:
        line(0, 0, 100, 0);
        line(100, 0, 100, 100);
        line(100, 100, 150, 150);
        ellipse(150, 150, 10, 10);
        break;
      }
      popMatrix();
    }
    

    This approach does work, and it supports intermediate stages that aren't cumulative if we like, and it accomplishes the goal of adding an ellipse at every step, but it has a LOT of duplicate code that makes it hard to edit and extremely error-prone!

    A more robust general solution is to draw all line segments in a relative rather than absolute style by using translate statements each step of the way.

    Using the style a final call to ellipse() drawn at 0,0 will always be a valid last step, because no matter which animation stage 0,0 will always refer to the end of the last line segment.

    /**
     * Path Animation 3 - relative steps
     * 2017-02-08 Jeremy Douglass - Processing 3.2.3
     * https:// forum.processing.org/two/discussion/20609/indoor-navigation
     */
    void setup(){
      size(200,200);
      frameRate(1);
    }
    void draw(){
      background(192);
      // animate based on frameCount
      drawPathToRoomOne(10,10,(frameCount-1)%4);
    }
    void drawPathToRoomOne(float x, float y, int stepCount){
      pushMatrix();
      translate(x,y);
      if(stepCount>0){ step(100,0); }
      if(stepCount>1){ step(0,100); }
      if(stepCount>2){ step(50,50); }
      ellipse(0,0,10,10);
      popMatrix();
    }
    
    void step(int x, int y){
      // draw a straight line and follow it :)
      line(0,0,x,y);
      translate(x,y);
    }
    

    PathAnimation3--screenshot

    This approach is an improvement. You can combine it easily with dotted line code, for example, by putting that code in step().

    Yet another approach would be to specify line components in an ArrayList (for example, as pairs of PVectors with x,y) and then loop through the array to render a certain number of components. This starts to make sense if you aren't hand-coding a small prototype, but instead scaling up to a system in which you want to load path data and then process it.

    For animation components that are more complex (like polygons, or curves), see the second link I sent you for approaches to storing animation components as child shapes in a PShape, then looping through and showing 1, 2, 3 child shapes.

    Also, for more on relative motion as a paradigm see recent related discussions of Turtle graphics.

  • Hi @jeremydouglass -

    I'm really grateful for the code examples that you have sent to me and my application is starting to take shape. As you suggested earlier I have included my sketch example and as it stands and I feel that apart from the line animation, there a few more areas that require improvement.

    Problem 1 As you will see I have a splash-screen based on a png image ("Project_SplashScreen.png") and once the splash-screen appears I really would like the ListBox not to appear on the splash-screen. I only need the ListBox to display on the second image "PS_Hospital-Floor-plan.png" (a very basic ground floor plan of the hospital), once the splash-screen disappears.

    Problem 2 You will see that the ListBox has several items which represent departments in the hospital. The objective is that depending on the item selected, an animated dotted line will direct you to the particular department on the plan (each dot moving at a very small interval until the line has completed). My problem is that once the item in the ListBox is clicked, the lines do not stay on the screen long enough. I did have some partial success by using a noLoop() in the if() statements within the controlEvent() function which retains the lines but halts the program flow at that stage.

    I really need to overcome these 2 problems soon because my next challenge will be to introduce a first floor plan and have the logic repeated if the item selected (department) is located upstairs on the first floor.

    If you could advise me on how to overcome the problems mentioned I'll be most grateful.

    The code and the png images are attached.

    Many thanks.

        import controlP5.*;
    
        PImage plan;  
        PImage nhs;
    
        ControlP5 cp5;
    
        ListBox l;
    
        ArrayList testoA = new ArrayList();
    
        int cnt = 0;
    
        void setup() {
          size(1280, 800);
    
          plan = loadImage ("PS_Hospital-Floor-plan.png");
          nhs = loadImage ("Project_SplashScreen.png");
    
              cp5 = new ControlP5(this);
          l = cp5.addListBox("myList")
                 .setPosition(600, 40)
                 .setSize(140, 350)
                 .setItemHeight(40)
                 .setBarHeight(40)
                 .setColorBackground(color(0,102,153))
                 .setColorActive(color(0, 153, 50))
                 .setColorForeground(color(255, 100,0));
    
           ControlP5.printPublicMethodsFor(ListBox.class);
    
          l.getCaptionLabel().toUpperCase(true);
          l.getCaptionLabel().set("Please choose destination: ");
          l.getCaptionLabel().setColor(255);
          l.getCaptionLabel().getStyle().marginTop = 3;
          l.getValueLabel().getStyle().marginTop = 3;
    
           String[] lines = loadStrings("list.txt");
                for( int i = 0; i < lines.length; i++){ 
                  String[] tokens  = lines[i].toLowerCase().split(" ");
                for (int k = 0; k < tokens.length; k++){
                testoA.add(tokens[k]);
                l.addItem(tokens[k], k);
                }
          }
         }
    
        void draw(){
    
            if (millis() < 3000)//in milliseconds
         {
            image(nhs, 0, 0);
          }
          else {
           image (plan, 0, 0);
    
         }
        }
    
        void controlEvent(ControlEvent theEvent) {
    
            if (theEvent.getController().getValue() == 0.0){
    
               drawPathToOutpatients();
    
            }
    
            else if (theEvent.getController().getValue() == 1.0){
    
              drawPathToRestaurant(); 
            }
    
            else if (theEvent.getController().getValue() == 2.0){
    
              drawPathToPharmacy();
            } 
    
             else if (theEvent.getController().getValue() == 3.0){
    
              drawPathToWard1A();
            } 
    
             else if (theEvent.getController().getValue() == 4.0){
    
              println("Ward1B");
            } 
    
             else if (theEvent.getController().getValue() == 5.0){
    
              println("Ward1C");
            } 
        }
    
        void dottedLine(float x1, float y1, float x2, float y2){
          for (int i = 0; i <= 10; i++) {
            float x = lerp(x1, x2, i/10.0) ;
            float y = lerp(y1, y2, i/10.0);
            point(x, y);
          }
        }
    
        void drawPathToOutpatients(){
    
          strokeWeight (3);
          dottedLine(720, 700, 720, 580);
          dottedLine(720, 580, 1080, 580);
          dottedLine(1080, 580, 1080, 670);
          dottedLine(1080, 670, 1160, 670);
          fill (0, 153, 102);
          text ("You Are Here", 690, 730);
          ellipse(720, 700, 20, 20);
          fill(0, 102, 153);
          ellipse(1160, 670, 20, 20);
          text("Outpatients", 1130, 720);
        }
    
        void drawPathToRestaurant(){
    
          strokeWeight (3);
          dottedLine(720, 700, 720, 580);
          dottedLine(720, 580, 950, 580);
          dottedLine(950, 580, 950, 420);
          fill (0, 153, 102);
          text ("You Are Here", 690, 730);
          ellipse(720, 700, 20, 20);
          fill(0, 102, 153);
          ellipse(950, 420, 20, 20);
          text("Restaurant Upstairs", 850, 360 );
        }
    
        void drawPathToPharmacy(){
    
          strokeWeight (3);
          dottedLine(720, 700, 720, 580);
          dottedLine(720, 580, 900, 580);
          dottedLine(900, 580, 900, 480);
          line(900, 480, 420, 480);
          line(420, 480, 420, 400);
          fill (0, 153, 102);
          text ("You Are Here", 690, 730);
          ellipse(720, 700, 20, 20);
          fill(0, 102, 153);
          ellipse(420, 400, 20, 20);
          text("Pharmacy", 436, 400);
        }
    
        void drawPathToWard1A(){
    
          strokeWeight (3);
          dottedLine(720, 700, 720, 580);
          dottedLine(720, 580, 1080, 580);
          dottedLine(1080, 580, 1080, 236);
          dottedLine(1080, 236, 1020, 236);
          fill (0, 153, 102);
          text ("You Are Here", 690, 730);
          ellipse(720, 700, 20, 20);
          fill(0, 102, 153);
          ellipse(1020, 236, 20, 20);
          text("Ward1A", 1000, 220);
        }
    
        void drawPathToWard1B(){
    
          strokeWeight (3);
          fill (0, 153, 102);
          text ("You Are Here", 690, 730);
          ellipse(720, 700, 20, 20);
          line(720, 700, 720, 580);
          line(720, 580, 1080, 580);
          line(1080, 580, 1080, 236);
          line(1080, 236, 1020, 236);
          ellipse(1020, 236, 20, 20);
          fill(0, 102, 153);
          text("Ward1A", 1000, 220);
        }
    
        void drawPathToWard1C(){
    
          //pushMatrix();
          //translate(0, 0);
          strokeWeight (3);
          fill (0, 153, 102);
          text ("You Are Here", 690, 730);
          ellipse(720, 700, 20, 20);
          line(720, 700, 720, 580);
          line(720, 580, 1080, 580);
          line(1080, 580, 1080, 236);
          line(1080, 236, 1020, 236);
          ellipse(1020, 236, 20, 20);
          fill(0, 102, 153);
          text("Ward1A", 1000, 220);
          //popMatrix();
    
    
    }![Project_SplashScreen](https://forum.processing.org/two/uploads/imageupload/080/3L46FLA747Y3.png "Project_SplashScreen")
    

    PS_Hospital-Floor-plan Project_SplashScreen

  • edited February 2017 Answer ✓
    1. Consider using ControlP5 "setVisible" -- make it initially invisible and set visible after millis()>x. http://www.sojamo.de/libraries/controlP5/reference/index.html
    2. Again, use mills() for timing events.

      • When the event (control click) happens
      • check the current millis()
      • and save a variable set to a time in the future (millis()+n) when the event will be over.
      • Each animation frame, check how close the current time is to the end time
      • and reveal that % of the animation accordingly (10%, 50%, 75%, 100%).

    Now you can make the animation last 3 seconds or 30 seconds just by saving a different variable when you click your button.

  • @jeremydouglass - Thank you so much, solution 1 worked perfectly.

    I am working on number 2 now.

    Thanks again!

Sign In or Register to comment.