/** * Copyright (C) 2009-2014 Cars and Tracks Development Project (CTDP). * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package net.ctdp.rfdynhud.widgets.standard.temperatures; import java.awt.Color; import java.awt.Font; import java.io.IOException; import net.ctdp.rfdynhud.gamedata.LiveGameData; import net.ctdp.rfdynhud.gamedata.ProfileInfo.MeasurementUnits; import net.ctdp.rfdynhud.gamedata.TelemetryData; import net.ctdp.rfdynhud.gamedata.VehiclePhysics; import net.ctdp.rfdynhud.gamedata.VehiclePhysics.TireCompound; import net.ctdp.rfdynhud.gamedata.VehiclePhysics.TireCompound.CompoundWheel; import net.ctdp.rfdynhud.gamedata.VehicleScoringInfo; import net.ctdp.rfdynhud.gamedata.VehicleSetup; import net.ctdp.rfdynhud.gamedata.Wheel; import net.ctdp.rfdynhud.gamedata.WheelPart; import net.ctdp.rfdynhud.properties.BooleanProperty; import net.ctdp.rfdynhud.properties.DelayProperty; import net.ctdp.rfdynhud.properties.FontProperty; import net.ctdp.rfdynhud.properties.ImagePropertyWithTexture; import net.ctdp.rfdynhud.properties.PropertiesContainer; import net.ctdp.rfdynhud.properties.PropertyLoader; import net.ctdp.rfdynhud.properties.Size; import net.ctdp.rfdynhud.render.ByteOrderManager; import net.ctdp.rfdynhud.render.DrawnString; import net.ctdp.rfdynhud.render.DrawnString.Alignment; import net.ctdp.rfdynhud.render.DrawnStringFactory; import net.ctdp.rfdynhud.render.ImageTemplate; import net.ctdp.rfdynhud.render.Texture2DCanvas; import net.ctdp.rfdynhud.render.TextureImage2D; import net.ctdp.rfdynhud.util.NumberUtil; import net.ctdp.rfdynhud.util.PropertyWriter; import net.ctdp.rfdynhud.util.SubTextureCollector; import net.ctdp.rfdynhud.valuemanagers.Clock; import net.ctdp.rfdynhud.widgets.base.widget.Widget; import net.ctdp.rfdynhud.widgets.standard._util.StandardWidgetSet; /** * The {@link TemperaturesWidget} displays temperature information for the engine, tires and brakes. * * @author Marvin Froehlich (CTDP) */ public class TemperaturesWidget extends Widget { private final FontProperty font2 = new FontProperty( "font2", StandardWidgetSet.SMALLER_FONT.getKey() ); private final BooleanProperty displayEngine = new BooleanProperty( "displayEngine", true ); private final BooleanProperty displayWaterTemp = new BooleanProperty( "displayWaterTemp", true ); private final BooleanProperty displayTires = new BooleanProperty( "displayTires", true ); private final BooleanProperty displayBrakes = new BooleanProperty( "displayBrakes", true ); private final Size engineHeight = Size.newLocalSize( this, 0f, true, 10.0f, true ); private final Size tireSize = Size.newLocalSize( this, 10.0f, true, 10.0f, true ); private final Size brakeSize = Size.newLocalSize( this, 7.0f, true, 20.0f, true ); private int gap = 7; private final ImagePropertyWithTexture tireIcon = new ImagePropertyWithTexture( "tireIcon", "icon", "standard/tire_small.png", false, true ); private final ImagePropertyWithTexture brakeDiscIcon = new ImagePropertyWithTexture( "brakeDiscIcon", "icon", "standard/brake_disc_small.png", false, true ); private int maxIconWidth = 0; private final DelayProperty brakeTempsPeakDelay = new DelayProperty( "brakeTempsPeakDelay", "peakDelay", DelayProperty.DisplayUnits.MILLISECONDS, 7000, 0, 20000, false ); private DrawnString engineHeaderString = null; private DrawnString engineWaterTempString = null; private DrawnString engineOilTempString = null; private DrawnString tiresHeaderString = null; private DrawnString tireTempFLString = null; private DrawnString tireTempFRString = null; private DrawnString tireTempRLString = null; private DrawnString tireTempRRString = null; private DrawnString tireTempFLString2 = null; private DrawnString tireTempFRString2 = null; private DrawnString tireTempRLString2 = null; private DrawnString tireTempRRString2 = null; private DrawnString brakesHeaderString = null; private DrawnString brakeTempFLString = null; private DrawnString brakeTempFRString = null; private DrawnString brakeTempRLString = null; private DrawnString brakeTempRRString = null; private static final byte[] colorCold = new byte[ 4 ]; private static final byte[] colorOpt = new byte[ 4 ]; private static final byte[] colorHot = new byte[ 4 ]; static { colorCold[ByteOrderManager.RED] = (byte)0; colorCold[ByteOrderManager.GREEN] = (byte)0; colorCold[ByteOrderManager.BLUE] = (byte)255; colorCold[ByteOrderManager.ALPHA] = (byte)255; colorOpt[ByteOrderManager.RED] = (byte)255; colorOpt[ByteOrderManager.GREEN] = (byte)255; colorOpt[ByteOrderManager.BLUE] = (byte)0; colorOpt[ByteOrderManager.ALPHA] = (byte)255; colorHot[ByteOrderManager.RED] = (byte)255; colorHot[ByteOrderManager.GREEN] = (byte)0; colorHot[ByteOrderManager.BLUE] = (byte)0; colorHot[ByteOrderManager.ALPHA] = (byte)255; } private int oldWaterTemp = -1; private int oldOilTemp = -1; private int[] oldTireTemps = { -1, -1, -1, -1 }; private int[] oldBrakeTemps = { -1, -1, -1, -1 }; private long lastBrakeTempTime = -1L; public TemperaturesWidget() { super( StandardWidgetSet.INSTANCE, StandardWidgetSet.WIDGET_PACKAGE_TELEMETRY, 17.8125f, 30.416667f ); } @Override public void prepareForMenuItem() { super.prepareForMenuItem(); gap = 1; getFontProperty().setFont( "Dialog", Font.PLAIN, 5, false, true ); } /** * {@inheritDoc} */ @Override public void saveProperties( PropertyWriter writer ) throws IOException { super.saveProperties( writer ); writer.writeProperty( font2, "The used (smaller) font." ); writer.writeProperty( displayEngine, "Display the engine part of the Widget?" ); writer.writeProperty( displayWaterTemp, "Display water temperature?" ); writer.writeProperty( engineHeight.getHeightProperty( "engineHeight", "height" ), "The height of the engine bar." ); writer.writeProperty( displayTires, "Display the tire part of the Widget?" ); writer.writeProperty( tireIcon, "The image name for the tire icon." ); writer.writeProperty( tireSize.getWidthProperty( "tireWidth", "width" ), "The width of a tire image." ); writer.writeProperty( tireSize.getHeightProperty( "tireHeight", "height" ), "The height of a tire image." ); writer.writeProperty( displayBrakes, "Display the brakes of the Widget?" ); writer.writeProperty( brakeDiscIcon, "The image name for the brake disc icon." ); writer.writeProperty( brakeSize.getWidthProperty( "brakeWidth", "width" ), "The width of a brake image." ); writer.writeProperty( brakeSize.getHeightProperty( "brakeHeight", "height" ), "The height of a brake image." ); writer.writeProperty( brakeTempsPeakDelay, "(in milliseconds) If greater than 0, the brake temperatures will stay on their peek values after a turn for the chosen amount of milliseconds." ); } /** * {@inheritDoc} */ @Override public void loadProperty( PropertyLoader loader ) { super.loadProperty( loader ); if ( loader.getSourceVersion().getBuild() < 70 ) { if ( loader.getCurrentKey().equals( "brakeTempsPeekDelay" ) ) brakeTempsPeakDelay.loadValue( loader, loader.getCurrentValue() ); } if ( loader.loadProperty( font2 ) ); else if ( loader.loadProperty( displayEngine ) ); else if ( loader.loadProperty( displayWaterTemp ) ); else if ( loader.loadProperty( engineHeight.getHeightProperty( "engineHeight", "height" ) ) ); else if ( loader.loadProperty( displayTires ) ); else if ( loader.loadProperty( tireIcon ) ); else if ( loader.loadProperty( tireSize.getWidthProperty( "tireWidth", "width" ) ) ); else if ( loader.loadProperty( tireSize.getHeightProperty( "tireHeight", "height" ) ) ); else if ( loader.loadProperty( displayBrakes ) ); else if ( loader.loadProperty( brakeDiscIcon ) ); else if ( loader.loadProperty( brakeSize.getWidthProperty( "brakeWidth", "width" ) ) ); else if ( loader.loadProperty( brakeSize.getHeightProperty( "brakeHeight", "height" ) ) ); else if ( loader.loadProperty( brakeTempsPeakDelay ) ); } /** * {@inheritDoc} */ @Override protected void addFontPropertiesToContainer( PropertiesContainer propsCont, boolean forceAll ) { super.addFontPropertiesToContainer( propsCont, forceAll ); propsCont.addProperty( font2 ); } /** * {@inheritDoc} */ @Override public void getProperties( PropertiesContainer propsCont, boolean forceAll ) { super.getProperties( propsCont, forceAll ); propsCont.addGroup( "Engine" ); propsCont.addProperty( displayEngine ); propsCont.addProperty( displayWaterTemp ); propsCont.addProperty( engineHeight.getHeightProperty( "engineHeight", "height" ) ); propsCont.addGroup( "Tires" ); propsCont.addProperty( displayTires ); propsCont.addProperty( tireIcon ); propsCont.addProperty( tireSize.getWidthProperty( "tireWidth", "width" ) ); propsCont.addProperty( tireSize.getHeightProperty( "tireHeight", "height" ) ); propsCont.addGroup( "Brakes" ); propsCont.addProperty( displayBrakes ); propsCont.addProperty( brakeDiscIcon ); propsCont.addProperty( brakeSize.getWidthProperty( "brakeWidth", "width" ) ); propsCont.addProperty( brakeSize.getHeightProperty( "brakeHeight", "height" ) ); propsCont.addProperty( brakeTempsPeakDelay ); } @Override public int getNeededData() { return ( Widget.NEEDED_DATA_SCORING ); } /** * {@inheritDoc} */ @Override public void onCockpitEntered( LiveGameData gameData, boolean isEditorMode ) { super.onCockpitEntered( gameData, isEditorMode ); oldWaterTemp = -1; oldOilTemp = -1; for ( int i = 0; i < oldTireTemps.length; i++ ) oldTireTemps[i] = -1; for ( int i = 0; i < oldBrakeTemps.length; i++ ) oldBrakeTemps[i] = -1; lastBrakeTempTime = -1L; //forceReinitialization(); } /** * {@inheritDoc} */ @Override protected Boolean onVehicleControlChanged( VehicleScoringInfo viewedVSI, LiveGameData gameData, boolean isEditorMode ) { super.onVehicleControlChanged( viewedVSI, gameData, isEditorMode ); return ( viewedVSI.isPlayer() && viewedVSI.getVehicleControl().isLocalPlayer() ); } private static final String getTempUnits( MeasurementUnits measurementUnits ) { if ( measurementUnits == MeasurementUnits.IMPERIAL ) return ( Loc.temperature_units_IMPERIAL ); return ( Loc.temperature_units_METRIC ); } private void loadTireImage( boolean isEditorMode, int height ) { if ( tireIcon.isNoImage() ) return; ImageTemplate it = tireIcon.getImage(); int width = Math.round( height * it.getBaseAspect() ); tireIcon.updateSize( width, height, isEditorMode ); } private void loadBrakeDiscImage( boolean isEditorMode, int height ) { if ( brakeDiscIcon.isNoImage() ) return; ImageTemplate it = brakeDiscIcon.getImage(); int width = Math.round( height * it.getBaseAspect() ); brakeDiscIcon.updateSize( width, height, isEditorMode ); } /** * {@inheritDoc} */ @Override protected void initSubTextures( LiveGameData gameData, boolean isEditorMode, int widgetInnerWidth, int widgetInnerHeight, SubTextureCollector collector ) { } /** * {@inheritDoc} */ @Override protected void initialize( LiveGameData gameData, boolean isEditorMode, DrawnStringFactory dsf, TextureImage2D texture, int width, int height ) { final java.awt.Font font = getFont(); final boolean fontAntiAliased = isFontAntiAliased(); final java.awt.Font font2 = this.font2.getFont(); final boolean font2AntiAliased = this.font2.isAntiAliased(); final java.awt.Color fontColor = getFontColor(); final int tireWidth = tireSize.getEffectiveWidth(); final int tireHeight = tireSize.getEffectiveHeight(); final int brakeWidth = brakeSize.getEffectiveWidth(); final int brakeHeight = brakeSize.getEffectiveHeight(); maxIconWidth = 0; if ( displayTires.getBooleanValue() && !tireIcon.isNoImage() ) { int tiresHeight = Math.min( tireHeight, brakeHeight ) * 2 + gap; loadTireImage( isEditorMode, tiresHeight ); maxIconWidth = Math.max( maxIconWidth, tireIcon.getTexture().getWidth() ); } if ( displayBrakes.getBooleanValue() && !brakeDiscIcon.isNoImage() ) { int brakesHeight = Math.min( tireHeight, brakeHeight ) * 2 + gap; loadBrakeDiscImage( isEditorMode, brakesHeight ); maxIconWidth = Math.max( maxIconWidth, brakeDiscIcon.getTexture().getWidth() ); } int left0 = 2; //int left = left0 + maxIconWidth; int center = maxIconWidth + ( width - maxIconWidth ) / 2; int top = -2; DrawnString relY = null; final MeasurementUnits measurementUnits = gameData.getProfileInfo().getMeasurementUnits(); if ( displayEngine.getBooleanValue() ) { engineHeaderString = dsf.newDrawnString( "engineHeaderString", left0, top, Alignment.LEFT, false, font, fontAntiAliased, fontColor, Loc.engine_header_prefix + ": ", null ); if ( displayWaterTemp.getBooleanValue() ) { engineWaterTempString = dsf.newDrawnString( "engineWaterTempString", null, engineHeaderString, width, 2, Alignment.RIGHT, false, font2, font2AntiAliased, fontColor, "(" + Loc.engine_watertemp_prefix + ":", getTempUnits( measurementUnits ) + ")" ); engineOilTempString = dsf.newDrawnString( "engineOilTempString", null, engineWaterTempString, width, 2, Alignment.RIGHT, false, font, font2AntiAliased, fontColor, null, getTempUnits( measurementUnits ) ); } else { engineWaterTempString = null; engineOilTempString = dsf.newDrawnString( "engineOilTempString", null, engineHeaderString, width, 2, Alignment.RIGHT, false, font, fontAntiAliased, fontColor, null, getTempUnits( measurementUnits ) ); } relY = engineHeaderString; top = engineHeight.getEffectiveHeight() + 10; } int imgWidth = displayTires.getBooleanValue() && displayBrakes.getBooleanValue() ? Math.max( tireWidth, brakeWidth ) : ( displayTires.getBooleanValue() ? tireWidth : brakeWidth ); if ( displayTires.getBooleanValue() ) { tiresHeaderString = dsf.newDrawnString( "tiresHeaderString", null, relY, left0, top, Alignment.LEFT, false, font, fontAntiAliased, fontColor, "Tires: ", null ); tireTempFLString = dsf.newDrawnString( "tireTempFLString", null, tiresHeaderString, center - gap - imgWidth, 0, Alignment.RIGHT, false, font, fontAntiAliased, fontColor, null, getTempUnits( measurementUnits ) ); tireTempFRString = dsf.newDrawnString( "tireTempFRString", null, tiresHeaderString, center + gap + imgWidth, 0, Alignment.LEFT, false, font, fontAntiAliased, fontColor, null, getTempUnits( measurementUnits ) ); tireTempRLString = dsf.newDrawnString( "tireTempRLString", null, tiresHeaderString, center - gap - imgWidth, 0 + tireHeight + gap, Alignment.RIGHT, false, font, fontAntiAliased, fontColor, null, getTempUnits( measurementUnits ) ); tireTempRRString = dsf.newDrawnString( "tireTempRRString", null, tiresHeaderString, center + gap + imgWidth, 0 + tireHeight + gap, Alignment.LEFT, false, font, fontAntiAliased, fontColor, null, getTempUnits( measurementUnits ) ); tireTempFLString2 = dsf.newDrawnString( "tireTempFLString2", null, tiresHeaderString, center - gap - imgWidth, 0 + tireHeight - 2, Alignment.RIGHT, true, font2, font2AntiAliased, fontColor, "(", getTempUnits( measurementUnits ) + ")" ); tireTempFRString2 = dsf.newDrawnString( "tireTempFRString2", null, tiresHeaderString, center + gap + imgWidth, 0 + tireHeight - 2, Alignment.LEFT, true, font2, font2AntiAliased, fontColor, "(", getTempUnits( measurementUnits ) + ")" ); tireTempRLString2 = dsf.newDrawnString( "tireTempRLString2", null, tiresHeaderString, center - gap - imgWidth, 0 + tireHeight - 2 + tireHeight + gap, Alignment.RIGHT, true, font2, font2AntiAliased, fontColor, "(", getTempUnits( measurementUnits ) + ")" ); tireTempRRString2 = dsf.newDrawnString( "tireTempRRString2", null, tiresHeaderString, center + gap + imgWidth, 0 + tireHeight - 2 + tireHeight + gap, Alignment.LEFT, true, font2, font2AntiAliased, fontColor, "(", getTempUnits( measurementUnits ) + ")" ); relY = tiresHeaderString; top = tireHeight * 2 + 15; } if ( displayBrakes.getBooleanValue() ) { brakesHeaderString = dsf.newDrawnString( "brakesHeaderString", null, relY, left0, top, Alignment.LEFT, false, font, fontAntiAliased, fontColor ); brakeTempFLString = dsf.newDrawnString( "brakeTempFLString", null, brakesHeaderString, center - gap - imgWidth, 2, Alignment.RIGHT, false, font, fontAntiAliased, fontColor, null, getTempUnits( measurementUnits ) ); brakeTempFRString = dsf.newDrawnString( "brakeTempFRString", null, brakesHeaderString, center + gap + imgWidth, 2, Alignment.LEFT, false, font, fontAntiAliased, fontColor, null, getTempUnits( measurementUnits ) ); brakeTempRLString = dsf.newDrawnString( "brakeTempRLString", null, brakesHeaderString, center - gap - imgWidth, 2 + brakeHeight + gap, Alignment.RIGHT, false, font, fontAntiAliased, fontColor, null, getTempUnits( measurementUnits ) ); brakeTempRRString = dsf.newDrawnString( "brakeTempRRString", null, brakesHeaderString, center + gap + imgWidth, 2 + brakeHeight + gap, Alignment.LEFT, false, font, fontAntiAliased, fontColor, null, getTempUnits( measurementUnits ) ); } } private static void interpolateColor( byte[] color0, byte[] color1, float alpha, byte[] result ) { final float beta = 1f - alpha; result[ByteOrderManager.RED] = (byte)( ( color0[ByteOrderManager.RED] & 0xFF ) * beta + ( color1[ByteOrderManager.RED] & 0xFF ) * alpha ); result[ByteOrderManager.GREEN] = (byte)( ( color0[ByteOrderManager.GREEN] & 0xFF ) * beta + ( color1[ByteOrderManager.GREEN] & 0xFF ) * alpha ); result[ByteOrderManager.BLUE] = (byte)( ( color0[ByteOrderManager.BLUE] & 0xFF ) * beta + ( color1[ByteOrderManager.BLUE] & 0xFF ) * alpha ); } private void drawEngine( float temp, VehiclePhysics.Engine engine, TextureImage2D texture, int x, int y, int width ) { final int w = width; final int h = engineHeight.getEffectiveHeight(); final float optimum = engine.getOptimumOilTemperature(); final float lowerOptimum = optimum - 15f; final float overheating = engine.getOverheatingOilTemperature(); final float upperOptimum = optimum + ( overheating - optimum ) / 3f; final float strongOverheating = engine.getStrongOverheatingOilTemperature(); final float min = optimum - 15f; final float max = strongOverheating + ( strongOverheating - overheating ) * 3f; final float range = max - min; final float dispTemp = Math.max( min, Math.min( temp, max ) ); byte[] color = new byte[4]; color[ByteOrderManager.ALPHA] = (byte)255; if ( temp < lowerOptimum ) { System.arraycopy( colorCold, 0, color, 0, 3 ); } else if ( temp < optimum ) { //float alpha = ( dispTemp - min ) / ( optimum - min ); float alpha = ( dispTemp - lowerOptimum ) / ( optimum - lowerOptimum ); interpolateColor( colorCold, colorOpt, alpha, color ); } else if ( temp < overheating ) { float alpha = ( dispTemp - optimum ) / ( overheating - optimum ); interpolateColor( colorOpt, colorHot, alpha, color ); } else// if ( temp >= overheating ) { System.arraycopy( colorHot, 0, color, 0, 3 ); } int barWidth = Math.min( (int)( w * ( dispTemp - min ) / range ), w ); if ( temp < max ) { texture.clear( Color.BLACK, x + barWidth, y, w - barWidth, h, false, null ); } Color awtColor = new Color( color[ByteOrderManager.RED] & 0xFF, color[ByteOrderManager.GREEN] & 0xFF, color[ByteOrderManager.BLUE] & 0xFF ); texture.clear( awtColor, x, y, barWidth, h, false, null ); texture.getTextureCanvas().setColor( Color.GREEN ); texture.getTextureCanvas().drawLine( x + (int)( ( optimum - min ) * w / range ), y, x + (int)( ( optimum - min ) * w / range ), y + h - 1 ); texture.getTextureCanvas().setColor( Color.DARK_GRAY ); texture.getTextureCanvas().drawLine( x + (int)( ( upperOptimum - min ) * w / range ), y, x + (int)( ( upperOptimum - min ) * w / range ), y + h - 1 ); texture.getTextureCanvas().setColor( Color.YELLOW ); texture.getTextureCanvas().drawLine( x + (int)( ( overheating - min ) * w / range ), y, x + (int)( ( overheating - min ) * w / range ), y + h - 1 ); texture.getTextureCanvas().setColor( new Color( 255, 102, 0 ) ); texture.getTextureCanvas().drawLine( x + (int)( ( strongOverheating - min ) * w / range ), y, x + (int)( ( strongOverheating - min ) * w / range ), y + h - 1 ); texture.markDirty( x, y, w, h, null ); } private byte[] getTireColor( float actualTemp, float optimumTemp, float coldTemp, float overheatingTemp ) { if ( actualTemp == optimumTemp ) return ( colorOpt ); if ( actualTemp <= coldTemp ) return ( colorCold ); if ( actualTemp >= overheatingTemp ) return ( colorHot ); byte[] color = new byte[4]; color[ByteOrderManager.ALPHA] = (byte)255; if ( actualTemp < optimumTemp ) { float alpha = ( actualTemp - coldTemp ) / ( optimumTemp - coldTemp ); interpolateColor( colorCold, colorOpt, alpha, color ); } else// if ( actualTemp > optimumTemp ) { float alpha = ( overheatingTemp - actualTemp ) / ( overheatingTemp - optimumTemp ); interpolateColor( colorHot, colorOpt, alpha, color ); } return ( color ); } private void drawTire( TelemetryData telemData, Wheel wheel, CompoundWheel compoundWheel, TextureImage2D texture, int x, int y ) { WheelPart wpLeft = WheelPart.getLeftPart( wheel ); WheelPart wpRight = WheelPart.getRightPart( wheel ); final float optimumTemp = compoundWheel.getOptimumTemperature(); final float coldTemp = compoundWheel.getBelowTemperature( 0.95f ); final float overheatingTemp = compoundWheel.getAboveTemperature( 0.95f ); float tempLeft = telemData.getTireTemperature( wheel, wpLeft ); float tempCenter = telemData.getTireTemperature( wheel, WheelPart.CENTER ); float tempRight = telemData.getTireTemperature( wheel, wpRight ); byte[] colorLeft = getTireColor( tempLeft, optimumTemp, coldTemp, overheatingTemp ); byte[] colorCenter = getTireColor( tempCenter, optimumTemp, coldTemp, overheatingTemp ); byte[] colorRight = getTireColor( tempRight, optimumTemp, coldTemp, overheatingTemp ); byte[] color = new byte[4]; color[ByteOrderManager.ALPHA] = (byte)255; int w = tireSize.getEffectiveWidth(); int hw = w / 2; int h = tireSize.getEffectiveHeight(); int hh = h / 2; texture.markDirty( x, y, w, h, null ); byte[] pixels = new byte[ w * 4 ]; for ( int i = 0; i < hw; i++ ) { interpolateColor( colorLeft, colorCenter, (float)i / (float)hw, color ); System.arraycopy( color, 0, pixels, i * 4, 4 ); } for ( int i = hw; i < w; i++ ) { interpolateColor( colorCenter, colorRight, (float)( i - hw ) / (float)hw, color ); System.arraycopy( color, 0, pixels, i * 4, 4 ); } for ( int j = 0; j < hh; j++ ) { texture.clearPixelLine( pixels, x, y + j, w, false, null ); } for ( int j = hh + 1; j < h; j++ ) { texture.clearPixelLine( pixels, x, y + j, w, false, null ); } color[0] = (byte)0; color[1] = (byte)255; color[2] = (byte)0; for ( int i = 0; i < w; i++ ) { System.arraycopy( color, 0, pixels, i * 4, 4 ); } texture.clearPixelLine( pixels, x, y + hh, w, false, null ); Texture2DCanvas texCanvas = texture.getTextureCanvas(); final float coldTemp2 = compoundWheel.getBelowTemperature( 0.98f ); final float overheatingTemp2 = compoundWheel.getAboveTemperature( 0.98f ); int[] xPoints = { x, x + hw, x + w }; int[] yPoints = new int[ 3 ]; if ( tempLeft >= optimumTemp ) yPoints[0] = y + hh - (int)( ( tempLeft - optimumTemp ) * hh / ( overheatingTemp2 - optimumTemp ) ); else yPoints[0] = y + hh + (int)( ( optimumTemp - tempLeft ) * hh / ( optimumTemp - coldTemp2 ) ); if ( tempCenter >= optimumTemp ) yPoints[1] = y + hh - (int)( ( tempCenter - optimumTemp ) * hh / ( overheatingTemp2 - optimumTemp ) ); else yPoints[1] = y + hh + (int)( ( optimumTemp - tempCenter ) * hh / ( optimumTemp - coldTemp2 ) ); if ( tempRight >= optimumTemp ) yPoints[2] = y + hh - (int)( ( tempRight - optimumTemp ) * hh / ( overheatingTemp2 - optimumTemp ) ); else yPoints[2] = y + hh + (int)( ( optimumTemp - tempRight ) * hh / ( optimumTemp - coldTemp2 ) ); for ( int i = 0; i < 3; i++ ) { yPoints[i] = Math.min( y + h - 1, Math.max( yPoints[i], y ) ); } texCanvas.setColor( java.awt.Color.BLACK ); boolean wasAntialiasingEnabled = texCanvas.isAntialiazingEnabled(); texCanvas.setAntialiazingEnabled( true ); texCanvas.drawPolyline( xPoints, yPoints, 3 ); texCanvas.setAntialiazingEnabled( wasAntialiasingEnabled ); } private void drawBrake( float temp, VehiclePhysics.Brakes.WheelBrake brake, TextureImage2D texture, int x, int y ) { int w = brakeSize.getEffectiveWidth(); int h = brakeSize.getEffectiveHeight(); final float range = brake.getOverheatingTemperature() + 200f; byte[] color = new byte[4]; color[ByteOrderManager.ALPHA] = (byte)255; if ( temp <= brake.getColdTemperature() ) { System.arraycopy( colorCold, 0, color, 0, 3 ); } else if ( temp < brake.getOptimumTemperaturesLowerBound() ) { float alpha = ( temp - brake.getColdTemperature() ) / ( brake.getOptimumTemperaturesLowerBound() - brake.getColdTemperature() ); interpolateColor( colorCold, colorOpt, alpha, color ); } else if ( temp <= brake.getOptimumTemperaturesUpperBound() ) { System.arraycopy( colorOpt, 0, color, 0, 3 ); } else if ( temp < brake.getOverheatingTemperature() ) { float alpha = ( temp - brake.getOptimumTemperaturesUpperBound() ) / ( brake.getOverheatingTemperature() - brake.getOptimumTemperaturesUpperBound() ); interpolateColor( colorOpt, colorHot, alpha, color ); } else// if ( temp >= brakes.getOverheatingTemperature() ) { System.arraycopy( colorHot, 0, color, 0, 3 ); } Color awtColor = new Color( color[ByteOrderManager.RED] & 0xFF, color[ByteOrderManager.GREEN] & 0xFF, color[ByteOrderManager.BLUE] & 0xFF ); int barHeight = Math.min( (int)( h * temp / range ), h ); if ( temp < range ) { texture.clear( Color.BLACK, x, y, w, h - barHeight, false, null ); } texture.clear( awtColor, x, y + h - barHeight, w, barHeight, false, null ); byte[] pixels = new byte[ w * 4 ]; for ( int i = 0; i < w; i++ ) { System.arraycopy( colorHot, 0, pixels, i * 4, 4 ); } texture.clearPixelLine( pixels, x, y + (int)( ( range - brake.getOverheatingTemperature() ) * h / range ), w, false, null ); for ( int i = 0; i < w; i++ ) { //System.arraycopy( colorOpt, 0, pixels, i * 4, 4 ); pixels[i * 4 + ByteOrderManager.RED] = (byte)0; pixels[i * 4 + ByteOrderManager.GREEN] = (byte)255; pixels[i * 4 + ByteOrderManager.BLUE] = (byte)0; pixels[i * 4 + ByteOrderManager.ALPHA] = (byte)255; } texture.clearPixelLine( pixels, x, y + (int)( ( range - brake.getOptimumTemperaturesUpperBound() ) * h / range ), w, false, null ); texture.clearPixelLine( pixels, x, y + (int)( ( range - brake.getOptimumTemperaturesLowerBound() ) * h / range ), w, false, null ); for ( int i = 0; i < w; i++ ) { System.arraycopy( colorCold, 0, pixels, i * 4, 4 ); } texture.clearPixelLine( pixels, x, y + (int)( ( range - brake.getColdTemperature() ) * h / range ), w, false, null ); texture.markDirty( x, y, w, h, null ); } @Override protected void drawWidget( Clock clock, boolean needsCompleteRedraw, LiveGameData gameData, boolean isEditorMode, TextureImage2D texture, int offsetX, int offsetY, int width, int height ) { final boolean c = clock.c(); final TelemetryData telemData = gameData.getTelemetryData(); final VehiclePhysics physics = gameData.getPhysics(); final VehicleSetup setup = gameData.getSetup(); if ( needsCompleteRedraw ) { if ( displayEngine.getBooleanValue() ) engineHeaderString.draw( offsetX, offsetY, "(" + NumberUtil.formatFloat( gameData.getPhysics().getEngine().getOptimumOilTemperature(), 1, true ) + getTempUnits( gameData.getProfileInfo().getMeasurementUnits() ) + ")", texture ); // TODO: Try to find a way to detect tire compound change on pit stop! TireCompound tireCompound = setup.getGeneral().getFrontTireCompound(); if ( displayTires.getBooleanValue() ) tiresHeaderString.draw( offsetX, offsetY, tireCompound.getName() + " (" + NumberUtil.formatFloat( tireCompound.getWheel( Wheel.FRONT_LEFT ).getOptimumTemperature(), 1, true ) + getTempUnits( gameData.getProfileInfo().getMeasurementUnits() ) + ")", texture ); if ( displayBrakes.getBooleanValue() ) brakesHeaderString.draw( offsetX, offsetY, Loc.brakes_header_prefix + ":", texture ); } if ( displayEngine.getBooleanValue() ) { if ( displayWaterTemp.getBooleanValue() ) { int waterTemp = Math.round( telemData.getEngineWaterTemperature() * 10f ); if ( needsCompleteRedraw || ( c && ( waterTemp != oldWaterTemp ) ) ) { oldWaterTemp = waterTemp; String string = String.valueOf( waterTemp / 10f ); engineWaterTempString.draw( offsetX, offsetY, string, texture ); } } int oilTemp = Math.round( telemData.getEngineOilTemperature() * 10f ); if ( needsCompleteRedraw || ( c && ( oilTemp != oldOilTemp ) ) ) { oldOilTemp = oilTemp; String string = String.valueOf( oilTemp / 10f ); engineOilTempString.draw( offsetX, offsetY, string, texture ); int engineWidth; if ( displayWaterTemp.getBooleanValue() ) engineWidth = Math.max( 70, Math.max( engineWaterTempString.getLastWidth(), engineOilTempString.getLastWidth() ) ); else engineWidth = Math.max( 70, engineOilTempString.getLastWidth() ); engineWidth = engineOilTempString.getAbsX() - engineWidth - 5; int engineTop = ( engineWaterTempString != null ) ? engineWaterTempString.getAbsY() + 3 : engineOilTempString.getAbsY() + 3; drawEngine( oilTemp / 10f, physics.getEngine(), texture, offsetX + engineHeaderString.getAbsX(), offsetY + engineTop, engineWidth ); } } if ( displayTires.getBooleanValue() ) { final int tireWidth = tireSize.getEffectiveWidth(); int tireTempFL = Math.round( telemData.getTireTemperature( Wheel.FRONT_LEFT ) * 10f ); if ( needsCompleteRedraw || ( c && ( tireTempFL != oldTireTemps[0] ) ) ) { oldTireTemps[0] = tireTempFL; CompoundWheel wheel = setup.getGeneral().getFrontTireCompound().getWheel( Wheel.FRONT_LEFT ); float temp = tireTempFL / 10f; String string = String.valueOf( temp ); tireTempFLString.draw( offsetX, offsetY, string, texture ); float diff = Math.round( tireTempFL - wheel.getOptimumTemperature() * 10f ) / 10f; string = ( diff >= 0f ? "+" : "" ) + diff; tireTempFLString2.draw( offsetX, offsetY, string, texture ); drawTire( telemData, Wheel.FRONT_LEFT, wheel, texture, offsetX + tireTempFLString.getAbsX() + 3, offsetY + tireTempFLString.getAbsY() + 2 ); } if ( needsCompleteRedraw && tireIcon.hasTexture() ) { int top = tireTempFLString.getAbsY() + 2; top += ( tireSize.getEffectiveHeight() * 2 + gap - tireIcon.getTexture().getHeight() ) / 2; texture.drawImage( tireIcon.getTexture(), offsetX, offsetY + top, true, null ); } int tireTempFR = Math.round( telemData.getTireTemperature( Wheel.FRONT_RIGHT ) * 10f ); if ( needsCompleteRedraw || ( c && ( tireTempFR != oldTireTemps[1] ) ) ) { oldTireTemps[1] = tireTempFR; CompoundWheel wheel = setup.getGeneral().getFrontTireCompound().getWheel( Wheel.FRONT_RIGHT ); float temp = tireTempFR / 10f; String string = String.valueOf( temp ); tireTempFRString.draw( offsetX, offsetY, string, texture ); float diff = Math.round( tireTempFR - wheel.getOptimumTemperature() * 10f ) / 10f; string = ( diff >= 0f ? "+" : "" ) + diff; tireTempFRString2.draw( offsetX, offsetY, string, texture ); drawTire( telemData, Wheel.FRONT_RIGHT, wheel, texture, offsetX + tireTempFRString.getAbsX() - tireWidth - 3, offsetY + tireTempFRString.getAbsY() + 2 ); } int tireTempRL = Math.round( telemData.getTireTemperature( Wheel.REAR_LEFT ) * 10f ); if ( needsCompleteRedraw || ( c && ( tireTempRL != oldTireTemps[2] ) ) ) { oldTireTemps[2] = tireTempRL; CompoundWheel wheel = setup.getGeneral().getRearTireCompound().getWheel( Wheel.REAR_LEFT ); float temp = tireTempRL / 10f; String string = String.valueOf( temp ); tireTempRLString.draw( offsetX, offsetY, string, texture ); float diff = Math.round( tireTempRL - wheel.getOptimumTemperature() * 10f ) / 10f; string = ( diff >= 0f ? "+" : "" ) + diff; tireTempRLString2.draw( offsetX, offsetY, string, texture ); drawTire( telemData, Wheel.REAR_LEFT, wheel, texture, offsetX + tireTempRLString.getAbsX() + 3, offsetY + tireTempRLString.getAbsY() + 2 ); } int tireTempRR = Math.round( telemData.getTireTemperature( Wheel.REAR_RIGHT ) * 10f ); if ( needsCompleteRedraw || ( c && ( tireTempRR != oldTireTemps[3] ) ) ) { oldTireTemps[3] = tireTempRR; CompoundWheel wheel = setup.getGeneral().getRearTireCompound().getWheel( Wheel.REAR_RIGHT ); float temp = tireTempRR / 10f; String string = String.valueOf( temp ); tireTempRRString.draw( offsetX, offsetY, string, texture ); float diff = Math.round( tireTempRR - wheel.getOptimumTemperature() * 10f ) / 10f; string = ( diff >= 0f ? "+" : "" ) + diff; tireTempRRString2.draw( offsetX, offsetY, string, texture ); drawTire( telemData, Wheel.REAR_RIGHT, wheel, texture, offsetX + tireTempRRString.getAbsX() - tireWidth - 3, offsetY + tireTempRRString.getAbsY() + 2 ); } } if ( displayBrakes.getBooleanValue() ) { final int brakeWidth = brakeSize.getEffectiveWidth(); boolean brakesUpdateAllowed = false; int brakeTempFL = Math.round( telemData.getBrakeTemperature( Wheel.FRONT_LEFT ) ); int brakeTempFR = Math.round( telemData.getBrakeTemperature( Wheel.FRONT_RIGHT ) ); int brakeTempRL = Math.round( telemData.getBrakeTemperature( Wheel.REAR_LEFT ) ); int brakeTempRR = Math.round( telemData.getBrakeTemperature( Wheel.REAR_RIGHT ) ); if ( ( brakeTempFL > oldBrakeTemps[0] ) || ( brakeTempFR > oldBrakeTemps[1] ) || ( brakeTempRL > oldBrakeTemps[2] ) || ( brakeTempRR > oldBrakeTemps[3] ) ) { brakesUpdateAllowed = true; lastBrakeTempTime = gameData.getScoringInfo().getSessionNanos(); } else if ( lastBrakeTempTime + brakeTempsPeakDelay.getDelayNanos() < gameData.getScoringInfo().getSessionNanos() ) { brakesUpdateAllowed = true; } if ( needsCompleteRedraw || ( c && brakesUpdateAllowed && ( brakeTempFL != oldBrakeTemps[0] ) ) ) { oldBrakeTemps[0] = brakeTempFL; String string = String.valueOf( brakeTempFL ); brakeTempFLString.draw( offsetX, offsetY, string, texture ); drawBrake( brakeTempFL, physics.getBrakes().getBrake( Wheel.FRONT_LEFT ), texture, offsetX + brakeTempFLString.getAbsX() + 3, offsetY + brakeTempFLString.getAbsY() ); } if ( needsCompleteRedraw && brakeDiscIcon.hasTexture() ) { int top = brakeTempFLString.getAbsY(); top += ( brakeSize.getEffectiveHeight() * 2 + gap - brakeDiscIcon.getTexture().getHeight() ) / 2; texture.drawImage( brakeDiscIcon.getTexture(), offsetX, offsetY + top, true, null ); } if ( needsCompleteRedraw || ( c && brakesUpdateAllowed && ( brakeTempFR != oldBrakeTemps[1] ) ) ) { oldBrakeTemps[1] = brakeTempFR; String string = String.valueOf( brakeTempFR ); brakeTempFRString.draw( offsetX, offsetY, string, texture ); drawBrake( brakeTempFR, physics.getBrakes().getBrake( Wheel.FRONT_RIGHT ), texture, offsetX + brakeTempFRString.getAbsX() - brakeWidth - 3, offsetY + brakeTempFRString.getAbsY() ); } if ( needsCompleteRedraw || ( c && brakesUpdateAllowed && ( brakeTempRL != oldBrakeTemps[2] ) ) ) { oldBrakeTemps[2] = brakeTempRL; String string = String.valueOf( brakeTempRL ); brakeTempRLString.draw( offsetX, offsetY, string, texture ); drawBrake( brakeTempRL, physics.getBrakes().getBrake( Wheel.REAR_LEFT ), texture, offsetX + brakeTempRLString.getAbsX() + 3, offsetY + brakeTempRLString.getAbsY() ); } if ( needsCompleteRedraw || ( c && brakesUpdateAllowed && ( brakeTempRR != oldBrakeTemps[3] ) ) ) { oldBrakeTemps[3] = brakeTempRR; String string = String.valueOf( brakeTempRR ); brakeTempRRString.draw( offsetX, offsetY, string, texture ); drawBrake( brakeTempRR, physics.getBrakes().getBrake( Wheel.REAR_RIGHT ), texture, offsetX + brakeTempRRString.getAbsX() - brakeWidth - 3, offsetY + brakeTempRRString.getAbsY() ); } } } }