Canvas HUD: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
(Created page with "{{Template:Canvas Navigation}} {{stub}} Rendering a basic HUD with a Canvas. thumb|C-130J basic HUD <syntaxhighlight lang="php"> # ==...")
 
(Add energy and acceleration cues and do some cleanup)
Line 14: Line 14:
var vec_length = func(x, y) { return math.sqrt(pow2(x) + pow2(y)); };
var vec_length = func(x, y) { return math.sqrt(pow2(x) + pow2(y)); };
var round0 = func(x) { return math.abs(x) > 0.01 ? x : 0; };
var round0 = func(x) { return math.abs(x) > 0.01 ? x : 0; };
var clamp = func(x, min, max) { return x < min ? min : (x > max ? max : x); }


var HUD = {
var HUD = {
Line 26: Line 27:
     var m = {
     var m = {
       parents: [HUD],
       parents: [HUD],
       canvas: canvas.new(HUD.canvas_settings),
       canvas: canvas.new(HUD.canvas_settings)
      text_style: {
        'font': "LiberationFonts/LiberationMono-Regular.ttf",
        'character-size': 18,
        'character-aspect-ration': 0.9
      }
     };
     };


     m.canvas.addPlacement(placement);
     m.canvas.addPlacement(placement);
     m.canvas.setColorBackground(0.36, 1, 0.3, 0.02);
     m.canvas.setColorBackground(0.36, 1, 0.3, 0.02);
     m.root = m.canvas.createGroup();
   
     m.root =
      m.canvas.createGroup()
              .setScale(1, 1/math.cos(25 * math.pi/180))
              .setTranslation(240, 180)
              .set("font", "LiberationFonts/LiberationMono-Regular.ttf")
              .setDouble("character-size", 18)
              .setDouble("character-aspect-ration", 0.9)
              .set("stroke", "rgba(0,255,0,0.9)");
    m.text =
      m.root.createChild("group")
            .set("fill", "rgba(0,255,0,0.9)");


    m.root.setScale(1, 1/math.cos(25 * math.pi/180));
    m.root.setTranslation(240, 180);
   
     # Heading
     # Heading
     m.hdg = m.root.createChild("text");
     m.hdg =
    m.hdg._node.setValues(m.text_style);
      m.text.createChild("text")
    m.hdg.setDrawMode(3);
            .setDrawMode(3)
    m.hdg.setPadding(2);
            .setPadding(2)
    m.hdg.setColor(0.36, 1, 0.3);
            .setAlignment("center-top")
    m.hdg.setColorFill(0.36, 1, 0.3);
            .setTranslation(0, -140);
    m.hdg.setAlignment("center-top");
 
    m.hdg.setTranslation(0, -140);
   
     # Airspeed
     # Airspeed
     m.airspeed = m.root.createChild("text");
     m.airspeed =
    m.airspeed._node.setValues(m.text_style);
      m.text.createChild("text")
    m.airspeed.setColor(0.36, 1, 0.3);
            .setAlignment("right-center")
    m.airspeed.setAlignment("right-center");
            .setTranslation(-180, 0);
    m.airspeed.setTranslation(-180, 0);
      
      
     # Groundspeed
     # Groundspeed
     m.groundspeed = m.root.createChild("text");
     m.groundspeed =
    m.groundspeed._node.setValues(m.text_style);
      m.text.createChild("text")
    m.groundspeed.setColor(0.36, 1, 0.3);
            .setAlignment("left-center")
    m.groundspeed.setAlignment("left-center");
            .setTranslation(-220, 90);
    m.groundspeed.setTranslation(-220, 90);
      
      
     # Vertical speed
     # Vertical speed
     m.vertical_speed = m.root.createChild("text");
     m.vertical_speed =
    m.vertical_speed._node.setValues(m.text_style);
      m.text.createChild("text")
    m.vertical_speed.setColor(0.36, 1, 0.3);
            .setFontSize(10, 0.9)
    m.vertical_speed.setFontSize(10, 0.9);
            .setAlignment("right-center")
    m.vertical_speed.setAlignment("right-center");
            .setTranslation(205, 50);
    m.vertical_speed.setTranslation(205, 50);
      
      
     # Radar altidude
     # Radar altidude
     m.rad_alt = m.root.createChild("text");
     m.rad_alt =
    m.rad_alt._node.setValues(m.text_style);
      m.text.createChild("text")
    m.rad_alt.setColor(0.36, 1, 0.3);
            .setAlignment("right-center")
    m.rad_alt.setAlignment("right-center");
            .setTranslation(220, 70);
    m.rad_alt.setTranslation(220, 70);


     # Waterline / Pitch indicator
     # Waterline / Pitch indicator
    m.root.createChild("path")
      m.root.createChild("path")
          .moveTo(-24, 0)
            .moveTo(-24, 0)
          .horizTo(-8)
            .horizTo(-8)
          .lineTo(-4, 6)
            .lineTo(-4, 6)
          .lineTo(0, 0)
            .lineTo(0, 0)
          .lineTo(4, 6)
            .lineTo(4, 6)
          .lineTo(8, 0)
            .lineTo(8, 0)
          .horizTo(24)
            .horizTo(24)
          .setStrokeLineWidth(0.9)
            .setStrokeLineWidth(0.9);
          .setColor(0.36, 1, 0.3, 0.8);
      
      
     # Flightpath/Velocity vector
     # Flightpath/Velocity vector
     m.vec_vel =
     m.fpv = m.root.createChild("group", "FPV");
      m.root.createChild("path")
    m.fpv.createChild("path")
            .moveTo(8, 0)
        .moveTo(8, 0)
            .arcSmallCCW(8, 8, 0, -16, 0)
        .arcSmallCCW(8, 8, 0, -16, 0)
            .arcSmallCCW(8, 8, 0,  16, 0)
        .arcSmallCCW(8, 8, 0,  16, 0)
            .close()
        .moveTo(-8, 0)
            .moveTo(-8, 0)
        .horiz(-16)
            .horiz(-16)
        .moveTo(8, 0)
            .moveTo(8, 0)
        .horiz(16)
            .horiz(16)
        .setStrokeLineWidth(0.9);
            .setStrokeLineWidth(0.9)
 
            .setColor(0.36, 1, 0.3, 0.8);
    # Energy/Acceleration cues
    m.energy_cue =
      m.fpv.createChild("path")
          .setStrokeLineWidth(1);
 
    m.acc =
      m.fpv.createChild("path")
          .setStrokeLineWidth(1);
      
      
     # Horizon
     # Horizon
Line 111: Line 115:
     m.h_rot  = m.horizon_group.createTransform();
     m.h_rot  = m.horizon_group.createTransform();
      
      
    # Pitch lines
     for(var i = 5; i <= 10; i += 5)
     for(var i = 5; i <= 10; i += 5)
    {
       m.horizon_group.createChild("path")
       m.horizon_group.createChild("path")
                     .moveTo(24, -i * 18)
                     .moveTo(24, -i * 18)
Line 119: Line 125:
                     .horiz(-48)
                     .horiz(-48)
                     .vert(7)
                     .vert(7)
                     .setStrokeLineWidth(1.5)
                     .setStrokeLineWidth(1.5);
                    .setColor(0,1,0, 0.65);
    }
 
   
     # Horizon line
     # Horizon line
     m.horizon =
     m.horizon_group.createChild("path")
      m.horizon_group.createChild("path")
                  .moveTo(-500, 0)
                    .moveTo(-500, 0)
                  .horizTo(500)
                    .horizTo(500)
                  .setStrokeLineWidth(1.5);
                    .setStrokeLineWidth(1.5)
                    .setColor(0,1,0, 0.65);


     m.input = {
     m.input = {
       pitch:   "/orientation/pitch-deg",
       pitch:     "/orientation/pitch-deg",
       roll:     "/orientation/roll-deg",
       roll:       "/orientation/roll-deg",
       hdg:     "/orientation/heading-deg",
       hdg:       "/orientation/heading-deg",
       speed_n: "velocities/speed-north-fps",
       speed_n:   "velocities/speed-north-fps",
       speed_e: "velocities/speed-east-fps",
       speed_e:   "velocities/speed-east-fps",
       speed_d: "velocities/speed-down-fps",
       speed_d:   "velocities/speed-down-fps",
       alpha:   "/orientation/alpha-deg",
       alpha:     "/orientation/alpha-deg",
       beta:     "/orientation/side-slip-deg",
       beta:       "/orientation/side-slip-deg",
       ias:     "/velocities/airspeed-kt",
       ias:       "/velocities/airspeed-kt",
       gs:       "/velocities/groundspeed-kt",
       gs:         "/velocities/groundspeed-kt",
       vs:       "/velocities/vertical-speed-fps",
       vs:         "/velocities/vertical-speed-fps",
       rad_alt: "/instrumentation/radar-altimeter/radar-altitude-ft",
       rad_alt:   "/instrumentation/radar-altimeter/radar-altitude-ft",
       wow_nlg: "/gear/gear[4]/wow"
       wow_nlg:   "/gear/gear[4]/wow",
      airspeed:  "/velocities/airspeed-kt",
      target_spd: "/autopilot/settings/target-speed-kt",
      acc:        "/fdm/jsbsim/accelerations/udot-ft_sec2"
     };
     };
      
      
Line 196: Line 203:
     var dir_x  = math.atan2(round0(vel_by), math.max(vel_bx, 0.01)) * 180.0 / math.pi;
     var dir_x  = math.atan2(round0(vel_by), math.max(vel_bx, 0.01)) * 180.0 / math.pi;


     me.vec_vel.setTranslation(dir_x * 18, dir_y * 18);
     me.fpv.setTranslation(dir_x * 18, dir_y * 18);
 
    var speed_error = 0;
    if( me.input.target_spd.getValue() != nil )
      speed_error = 4 * clamp(
        me.input.target_spd.getValue() - me.input.airspeed.getValue(),
        -15, 15
      );
   
    me.energy_cue.reset();
#    if( math.abs(speed_error) > 3 )
      me.energy_cue.moveTo(-22, 0)
                  .vert(speed_error)
                  .horiz(3)
                  .vertTo(0);
   
    var acc = me.input.acc.getValue() or 0;
    me.acc.reset()
          .moveTo(-34, -acc * 5 - 4)
          .line(8, 4)
          .line(-8, 4);


     settimer(func me.update(), 0);
     settimer(func me.update(), 0);

Revision as of 16:07, 5 November 2012

This article is a stub. You can help the wiki by expanding it.

Rendering a basic HUD with a Canvas.

C-130J basic HUD
# ==============================================================================
# Head up display
# ==============================================================================

var pow2 = func(x) { return x * x; };
var vec_length = func(x, y) { return math.sqrt(pow2(x) + pow2(y)); };
var round0 = func(x) { return math.abs(x) > 0.01 ? x : 0; };
var clamp = func(x, min, max) { return x < min ? min : (x > max ? max : x); }

var HUD = {
  canvas_settings: {
    "name": "HUD",
    "size": [1024, 1024],
    "view": [480, 360],
    "mipmapping": 1
  },
  new: func(placement)
  {
    var m = {
      parents: [HUD],
      canvas: canvas.new(HUD.canvas_settings)
    };

    m.canvas.addPlacement(placement);
    m.canvas.setColorBackground(0.36, 1, 0.3, 0.02);
    
    m.root =
      m.canvas.createGroup()
              .setScale(1, 1/math.cos(25 * math.pi/180))
              .setTranslation(240, 180)
              .set("font", "LiberationFonts/LiberationMono-Regular.ttf")
              .setDouble("character-size", 18)
              .setDouble("character-aspect-ration", 0.9)
              .set("stroke", "rgba(0,255,0,0.9)");
    m.text =
      m.root.createChild("group")
            .set("fill", "rgba(0,255,0,0.9)");

    # Heading
    m.hdg =
      m.text.createChild("text")
            .setDrawMode(3)
            .setPadding(2)
            .setAlignment("center-top")
            .setTranslation(0, -140);

    # Airspeed
    m.airspeed =
      m.text.createChild("text")
            .setAlignment("right-center")
            .setTranslation(-180, 0);
    
    # Groundspeed
    m.groundspeed =
      m.text.createChild("text")
            .setAlignment("left-center")
            .setTranslation(-220, 90);
    
    # Vertical speed
    m.vertical_speed =
      m.text.createChild("text")
            .setFontSize(10, 0.9)
            .setAlignment("right-center")
            .setTranslation(205, 50);
    
    # Radar altidude
    m.rad_alt =
      m.text.createChild("text")
            .setAlignment("right-center")
            .setTranslation(220, 70);

    # Waterline / Pitch indicator
      m.root.createChild("path")
            .moveTo(-24, 0)
            .horizTo(-8)
            .lineTo(-4, 6)
            .lineTo(0, 0)
            .lineTo(4, 6)
            .lineTo(8, 0)
            .horizTo(24)
            .setStrokeLineWidth(0.9);
    
    # Flightpath/Velocity vector
    m.fpv = m.root.createChild("group", "FPV");
    m.fpv.createChild("path")
         .moveTo(8, 0)
         .arcSmallCCW(8, 8, 0, -16, 0)
         .arcSmallCCW(8, 8, 0,  16, 0)
         .moveTo(-8, 0)
         .horiz(-16)
         .moveTo(8, 0)
         .horiz(16)
         .setStrokeLineWidth(0.9);

    # Energy/Acceleration cues
    m.energy_cue =
      m.fpv.createChild("path")
           .setStrokeLineWidth(1);

    m.acc =
      m.fpv.createChild("path")
           .setStrokeLineWidth(1);
    
    # Horizon
    m.horizon_group = m.root.createChild("group");
    m.h_trans = m.horizon_group.createTransform();
    m.h_rot   = m.horizon_group.createTransform();
    
    # Pitch lines
    for(var i = 5; i <= 10; i += 5)
    {
      m.horizon_group.createChild("path")
                     .moveTo(24, -i * 18)
                     .horiz(48)
                     .vert(7)
                     .moveTo(-24, -i * 18)
                     .horiz(-48)
                     .vert(7)
                     .setStrokeLineWidth(1.5);
    }
    
    # Horizon line
    m.horizon_group.createChild("path")
                   .moveTo(-500, 0)
                   .horizTo(500)
                   .setStrokeLineWidth(1.5);

    m.input = {
      pitch:      "/orientation/pitch-deg",
      roll:       "/orientation/roll-deg",
      hdg:        "/orientation/heading-deg",
      speed_n:    "velocities/speed-north-fps",
      speed_e:    "velocities/speed-east-fps",
      speed_d:    "velocities/speed-down-fps",
      alpha:      "/orientation/alpha-deg",
      beta:       "/orientation/side-slip-deg",
      ias:        "/velocities/airspeed-kt",
      gs:         "/velocities/groundspeed-kt",
      vs:         "/velocities/vertical-speed-fps",
      rad_alt:    "/instrumentation/radar-altimeter/radar-altitude-ft",
      wow_nlg:    "/gear/gear[4]/wow",
      airspeed:   "/velocities/airspeed-kt",
      target_spd: "/autopilot/settings/target-speed-kt",
      acc:        "/fdm/jsbsim/accelerations/udot-ft_sec2"
    };
    
    foreach(var name; keys(m.input))
      m.input[name] = props.globals.getNode(m.input[name], 1);
    
    return m;
  },
  update: func()
  {
    me.airspeed.setText(sprintf("%d", me.input.ias.getValue()));
    me.groundspeed.setText(sprintf("G %3d", me.input.gs.getValue()));
    me.vertical_speed.setText(sprintf("%.1f", me.input.vs.getValue() * 60.0 / 1000));
    
    var rad_alt = me.input.rad_alt.getValue();
    if( rad_alt and rad_alt < 5000 ) # Only show below 5000AGL
      rad_alt = sprintf("R %4d", rad_alt);
    else
      rad_alt = nil;
    me.rad_alt.setText(rad_alt);
    
    me.hdg.setText(sprintf("%03d", me.input.hdg.getValue()));
    me.h_trans.setTranslation(0, 18 * me.input.pitch.getValue());
    
    var rot = -me.input.roll.getValue() * math.pi / 180.0;
    me.h_rot.setRotation(rot);
    
    # flight path vector (FPV)
    var vel_gx = me.input.speed_n.getValue();
    var vel_gy = me.input.speed_e.getValue();
    var vel_gz = me.input.speed_d.getValue();
    
    var yaw = me.input.hdg.getValue() * math.pi / 180.0;
    var roll = me.input.roll.getValue() * math.pi / 180.0;
    var pitch = me.input.pitch.getValue() * math.pi / 180.0;
    
    var sy = math.sin(yaw);   var cy = math.cos(yaw);
    var sr = math.sin(roll);  var cr = math.cos(roll);
    var sp = math.sin(pitch); var cp = math.cos(pitch);

    var vel_bx = vel_gx * cy * cp
               + vel_gy * sy * cp
               + vel_gz * -sp;
    var vel_by = vel_gx * (cy * sp * sr - sy * cr)
               + vel_gy * (sy * sp * sr + cy * cr)
               + vel_gz * cp * sr;
    var vel_bz = vel_gx * (cy * sp * cr + sy * sr)
               + vel_gy * (sy * sp * cr - cy * sr)
               + vel_gz * cp * cr;

    var dir_y = math.atan2(round0(vel_bz), math.max(vel_bx, 0.01)) * 180.0 / math.pi;
    var dir_x  = math.atan2(round0(vel_by), math.max(vel_bx, 0.01)) * 180.0 / math.pi;

    me.fpv.setTranslation(dir_x * 18, dir_y * 18);

    var speed_error = 0;
    if( me.input.target_spd.getValue() != nil )
      speed_error = 4 * clamp(
        me.input.target_spd.getValue() - me.input.airspeed.getValue(),
        -15, 15
      );
    
    me.energy_cue.reset();
#    if( math.abs(speed_error) > 3 )
      me.energy_cue.moveTo(-22, 0)
                   .vert(speed_error)
                   .horiz(3)
                   .vertTo(0);
    
    var acc = me.input.acc.getValue() or 0;
    me.acc.reset()
          .moveTo(-34, -acc * 5 - 4)
          .line(8, 4)
          .line(-8, 4);

    settimer(func me.update(), 0);
  }
};

var init = setlistener("/sim/signals/fdm-initialized", func() {
  removelistener(init); # only call once
  var hud_pilot = HUD.new({"node": "HUD.l.canvas"});
  hud_pilot.update();
#  var hud_copilot = HUD.new({"node": "HUD.l.canvas.001"});
#  hud_copilot.update();
});