001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.wpilibj.smartdashboard;
006
007import static edu.wpi.first.units.Units.Meters;
008
009import edu.wpi.first.math.geometry.Pose2d;
010import edu.wpi.first.math.geometry.Rotation2d;
011import edu.wpi.first.networktables.NTSendable;
012import edu.wpi.first.networktables.NTSendableBuilder;
013import edu.wpi.first.networktables.NetworkTable;
014import edu.wpi.first.units.measure.Distance;
015import edu.wpi.first.util.sendable.SendableRegistry;
016import java.util.ArrayList;
017import java.util.List;
018
019/**
020 * 2D representation of game field for dashboards.
021 *
022 * <p>An object's pose is the location shown on the dashboard view. Note that for the robot, this
023 * may or may not match the internal odometry. For example, the robot is shown at a particular
024 * starting location, the pose in this class would represent the actual location on the field, but
025 * the robot's internal state might have a 0,0,0 pose (unless it's initialized to something
026 * different).
027 *
028 * <p>As the user is able to edit the pose, code performing updates should get the robot pose,
029 * transform it as appropriate (e.g. based on wheel odometry), and set the new pose.
030 *
031 * <p>This class provides methods to set the robot pose, but other objects can also be shown by
032 * using the getObject() function. Other objects can also have multiple poses (which will show the
033 * object at multiple locations).
034 */
035public class Field2d implements NTSendable, AutoCloseable {
036  /** Constructor. */
037  @SuppressWarnings("this-escape")
038  public Field2d() {
039    FieldObject2d obj = new FieldObject2d("Robot");
040    obj.setPose(Pose2d.kZero);
041    m_objects.add(obj);
042    SendableRegistry.add(this, "Field");
043  }
044
045  @Override
046  public void close() {
047    for (FieldObject2d obj : m_objects) {
048      obj.close();
049    }
050  }
051
052  /**
053   * Set the robot pose from a Pose object.
054   *
055   * @param pose 2D pose
056   */
057  public synchronized void setRobotPose(Pose2d pose) {
058    m_objects.get(0).setPose(pose);
059  }
060
061  /**
062   * Set the robot pose from x, y, and rotation.
063   *
064   * @param xMeters X location, in meters
065   * @param yMeters Y location, in meters
066   * @param rotation rotation
067   */
068  public synchronized void setRobotPose(double xMeters, double yMeters, Rotation2d rotation) {
069    m_objects.get(0).setPose(xMeters, yMeters, rotation);
070  }
071
072  /**
073   * Set the robot pose from x, y, and rotation.
074   *
075   * @param x X location
076   * @param y Y location
077   * @param rotation rotation
078   */
079  public synchronized void setRobotPose(Distance x, Distance y, Rotation2d rotation) {
080    m_objects.get(0).setPose(x.in(Meters), y.in(Meters), rotation);
081  }
082
083  /**
084   * Get the robot pose.
085   *
086   * @return 2D pose
087   */
088  public synchronized Pose2d getRobotPose() {
089    return m_objects.get(0).getPose();
090  }
091
092  /**
093   * Get or create a field object.
094   *
095   * @param name The field object's name.
096   * @return Field object
097   */
098  public synchronized FieldObject2d getObject(String name) {
099    for (FieldObject2d obj : m_objects) {
100      if (obj.m_name.equals(name)) {
101        return obj;
102      }
103    }
104    FieldObject2d obj = new FieldObject2d(name);
105    m_objects.add(obj);
106    if (m_table != null) {
107      synchronized (obj) {
108        obj.m_entry = m_table.getDoubleArrayTopic(name).getEntry(new double[] {});
109      }
110    }
111    return obj;
112  }
113
114  /**
115   * Get the robot object.
116   *
117   * @return Field object for robot
118   */
119  public synchronized FieldObject2d getRobotObject() {
120    return m_objects.get(0);
121  }
122
123  @Override
124  public void initSendable(NTSendableBuilder builder) {
125    builder.setSmartDashboardType("Field2d");
126
127    synchronized (this) {
128      m_table = builder.getTable();
129      for (FieldObject2d obj : m_objects) {
130        synchronized (obj) {
131          obj.m_entry = m_table.getDoubleArrayTopic(obj.m_name).getEntry(new double[] {});
132          obj.updateEntry(true);
133        }
134      }
135    }
136  }
137
138  private NetworkTable m_table;
139  private final List<FieldObject2d> m_objects = new ArrayList<>();
140}