001package jmri.jmrit.etcs.dmi.swing; 002 003import java.awt.*; 004import java.awt.image.BufferedImage; 005 006import javax.annotation.Nonnull; 007import javax.swing.*; 008 009import jmri.jmrit.etcs.ResourceUtil; 010 011/** 012 * JPanel containing ERTMS DMI Panel A, Distance to Target Bar. 013 * @author Steve Young Copyright (C) 2024 014 */ 015public class DmiPanelA extends JPanel { 016 017 private final JLabel a2Label; 018 private final JLabel a4Label; 019 020 private String speedString = ""; 021 private int distanceToTarget = -10; 022 023 private static final BufferedImage supervisionImage = ResourceUtil.readFile(ResourceUtil.getImageFile("LS_01.bmp")); 024 private static final ImageIcon adhesionIcon = ResourceUtil.getImageIcon("ST_02.bmp"); 025 026 public DmiPanelA(@Nonnull DmiPanel mainPanel){ 027 super(); 028 setLayout(null); 029 setBackground(DmiPanel.BACKGROUND_COLOUR); 030 setBounds(0, 15, 54, 300); 031 032 JPanel a1 = getSupervisionImagePanel(); 033 a2Label = new JLabel(); 034 // JPanel a2 = new JPanel(); // distance to target, nearest 10m 035 JPanel a3 = getDistanceToTargetBarPanel(); // distance to target bar 036 JPanel a4 = new JPanel(); // adhesion factor 037 038 a1.setBounds(0, 0, 54, 54); 039 a2Label.setBounds(0, 54, 54, 30); 040 a3.setBounds(0, 84, 54, 191); 041 a4.setBounds(0, 84+191, 54, 25); 042 043 a1.setLayout(null); 044 a1.setOpaque(true); 045 046 a2Label.setForeground(DmiPanel.GREY); 047 a2Label.setFont(new Font(DmiPanel.FONT_NAME, Font.PLAIN, 14)); 048 a2Label.setHorizontalAlignment(SwingConstants.RIGHT); 049 050 a4Label = new JLabel(); 051 a4.add(a4Label); 052 053 // setBg(a2); 054 setBg(a3); 055 setBg(a4); 056 057 add(a1); 058 add(a2Label); 059 add(a3); 060 add(a4); 061 062 DmiPanelA.this.setAdhesionFactorOn(false); 063 DmiPanelA.this.setLimitedSupervisionSpeed(-1); 064 } 065 066 private JPanel getDistanceToTargetBarPanel(){ 067 return new JPanel() { 068 @Override 069 protected void paintComponent(Graphics g) { 070 if (distanceToTarget < 0 ) { 071 return; 072 } 073 if (!(g instanceof Graphics2D) ) { 074 throw new IllegalArgumentException("Graphics object passed is not the correct type"); 075 } 076 Graphics2D g2 = (Graphics2D) g; 077 078 RenderingHints hints =new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 079 hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 080 g2.setRenderingHints(hints); 081 082 g2.setColor(DmiPanel.GREY); 083 drawScale(g2); 084 085 int rectHeight = (int)calculatePositionOnScale(distanceToTarget); 086 g2.fillRect(29, 188-rectHeight, 10, rectHeight); 087 } 088 }; 089 } 090 091 private static final double LINEAR_SCALE_MAX_DISTANCE = 100.0; 092 private static final double LOG_SCALE_MIN_DISTANCE = 100.0; 093 private static final double LOG_SCALE_MAX_DISTANCE = 1000.0; 094 private static final int TOTAL_PIXELS = 188; 095 private static final int FIRST_100M_PIXELS = 33; 096 private static final int LOG_SCALE_WIDTH_PIXELS = TOTAL_PIXELS-FIRST_100M_PIXELS; 097 098 // Calculate the position on the scale for a given length in meters 099 private static double calculatePositionOnScale(double lengthInMeters) { 100 double position; 101 if (lengthInMeters <= LINEAR_SCALE_MAX_DISTANCE) { 102 // Linear scale for the first 100 meters 103 position = (lengthInMeters / LINEAR_SCALE_MAX_DISTANCE) * FIRST_100M_PIXELS; 104 } else { 105 // Logarithmic scale for lengths beyond 100 meters 106 double logScaleFactor = LOG_SCALE_WIDTH_PIXELS / (Math.log(LOG_SCALE_MAX_DISTANCE) 107 - Math.log(LOG_SCALE_MIN_DISTANCE)); 108 position = FIRST_100M_PIXELS + (Math.log(lengthInMeters) 109 - Math.log(LOG_SCALE_MIN_DISTANCE)) * logScaleFactor; 110 } 111 log.debug("at distance {} px: {}",lengthInMeters,position); 112 return position; 113 } 114 115 private void drawScale(Graphics2D g2){ 116 g2.drawLine(12, 1, 25, 1); // 1000 117 g2.drawLine(12, 2, 25, 2); // 118 g2.drawLine(16, 8, 25, 8); // 900 119 g2.drawLine(16, 15, 25, 15); // 800 120 g2.drawLine(16, 24, 25, 24); // 700 121 g2.drawLine(16, 34, 25, 34); // 600 122 g2.drawLine(12, 47, 25, 47); // 500 123 g2.drawLine(12, 48, 25, 48); // 124 g2.drawLine(16, 61, 25, 61); // 400 125 g2.drawLine(16, 81, 25, 81); // 300 126 g2.drawLine(16, 107, 25, 107); // 200 127 g2.drawLine(16, 154, 25, 154); // 100 128 g2.drawLine(12, 187, 25, 187); // 0 129 g2.drawLine(12, 188, 25, 188); // 130 } 131 132 private JPanel getSupervisionImagePanel(){ 133 return new JPanel() { 134 @Override 135 protected void paintComponent(Graphics g) { 136 if (speedString.isEmpty()) { 137 return; 138 } 139 if (!(g instanceof Graphics2D) ) { 140 throw new IllegalArgumentException("Graphics object passed is not the correct type"); 141 } 142 Graphics2D g2 = (Graphics2D) g; 143 144 RenderingHints hints =new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 145 hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 146 g2.setRenderingHints(hints); 147 148 Font font = new Font(DmiPanel.FONT_NAME, Font.PLAIN, 18); 149 g2.setFont(font); 150 g2.setColor(DmiPanel.GREY); 151 152 FontMetrics fm = g2.getFontMetrics(); 153 int textWidth = fm.stringWidth(speedString); 154 int centerX = ((getWidth()-textWidth) / 2); 155 156 g2.drawImage(supervisionImage, 2, 2, 50, 157 50, this); 158 g2.drawString(speedString, centerX, 33); 159 } 160 }; 161 } 162 163 protected void setLimitedSupervisionSpeed(float spd){ 164 if ( spd < 0 ) { 165 speedString=""; 166 } else { 167 speedString = (String.valueOf(Math.round(spd))); 168 } 169 repaint(); 170 } 171 172 protected void setAdhesionFactorOn(boolean newVal) { 173 a4Label.setIcon(newVal ? adhesionIcon : null); 174 a4Label.setToolTipText(newVal ? Bundle.getMessage("AdhesionFactorOn") : null); 175 } 176 177 protected void setDistanceToTarget(float distance) { 178 distanceToTarget = Math.round(distance); 179 a2Label.setVisible(distanceToTarget >= 0 ); 180 int nearestTen = ((distanceToTarget + 5) / 10) * 10; 181 a2Label.setText(String.valueOf(nearestTen)); 182 repaint(); 183 } 184 185 private void setBg(JPanel p){ 186 p.setBackground(DmiPanel.BACKGROUND_COLOUR); 187 p.setBorder(javax.swing.BorderFactory.createLineBorder(Color.black, 1)); 188 189 } 190 191 protected void advance(int distance) { 192 setDistanceToTarget(distanceToTarget - distance); 193 } 194 195 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DmiPanelA.class); 196 197}