You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeedu/kalzium/src/element.cpp

549 lines
14 KiB

/***************************************************************************
* Copyright (C) 2003, 2004, 2005 by Carsten Niehaus *
* cniehaus@kde.org *
* *
* 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., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#include "element.h"
#include "prefs.h"
#include "spectrum.h"
#include "isotope.h"
#include "kalziumdataobject.h"
#include "kalziumutils.h"
#include "tempunit.h"
#include <tqdom.h>
#include <tqfile.h>
#include <tqpainter.h>
#include <tqregexp.h>
#include <tqfontmetrics.h>
#include <kdebug.h>
#include <klocale.h>
#include <kurl.h>
#include <kstandarddirs.h>
Element::Element()
{
m_radioactive = false;
m_artificial = false;
m_abundance = 0;
}
Isotope* Element::isotopeByNucleons( int numberOfNucleons )
{
TQValueList<Isotope*>::ConstIterator it = m_isotopeList.begin();
const TQValueList<Isotope*>::ConstIterator itEnd = m_isotopeList.end();
for ( ; it != itEnd; ++it )
{
if ( ( ( *it )->neutrons() + ( *it )->protones() ) == numberOfNucleons )
return *it;
}
return 0;
}
TQString Element::parsedOrbits( bool canBeEmpty )
{
if ( m_orbits.isEmpty() )
if ( !canBeEmpty )
return i18n( "structure means orbital configuration in this case", "Unknown structure" );
else
return "";
TQString orbits = m_orbits;
TQRegExp rxs("([a-z])([0-9]+)");
TQRegExp rxb("([a-z]{2}) ",false);
orbits.replace(rxs,"\\1<sup>\\2</sup>"); //superscript around electron number
orbits.replace(rxb,"<b>\\1</b> "); //bold around element symbols
return orbits;
}
Element::~Element()
{
}
double Element::meanmass()
{
return m_mass/m_number;
}
const TQString Element::adjustRadius( RADIUSTYPE rtype )
{
double val = 0.0;
TQString v;
switch ( rtype )
{
case ATOMIC:
val = m_RadiusAR;
break;
case IONIC:
val = m_RadiusIon;
break;
case COVALENT:
val = m_RadiusCR;
break;
case VDW:
val = m_RadiusVDW;
break;
}
if ( val <= 0 )
v = i18n( "Value unknown" );
else
v = i18n( "%1 is a length, eg: 12.3 pm", "%1 pm" ).arg( KalziumUtils::localizedValue( val, 6 ) );
return v;
}
const TQString Element::adjustUnits( const int type, double value )
{
TQString v;
if ( type == IE ) //an ionization energy
{
if ( Prefs::energies() == 0 )
{
value*=96.6;
v = KalziumUtils::localizedValue( value, 6 );
v.append( " kJ/mol" );
}
else // use electronvolt
{
v = KalziumUtils::localizedValue( value, 6 );
v.append( " eV" );
}
}
return v;
}
const TQString Element::adjustUnits( const int type )
{
TQString v = TQString();
double val = 0.0; //the value to convert
if ( type == BOILINGPOINT || type == MELTINGPOINT ) // convert a temperature
{
if ( type == BOILINGPOINT )
val = boiling();
else
val = melting();
if ( val <= 0 )
v = i18n( "Value unknown" );
else
{
double newvalue = TempUnit::convert( val, (int)TempUnit::Kelvin, Prefs::temperature() );
TQString strVal = KalziumUtils::localizedValue( newvalue, 6 );
switch (Prefs::temperature()) {
case 0: //Kelvin
v = i18n( "%1 is the temperature in Kelvin", "%1 K" ).arg( strVal );
break;
case 1://Kelvin to Celsius
v = i18n( "%1 is the temperature in Celsius", "%1 %2C" ).arg( strVal ).arg( TQChar(0xB0) );
break;
case 2: // Kelvin to Fahrenheit
v = i18n( "%1 is the temperature in Fahrenheit", "%1 %2F" ).arg( strVal ).arg( TQChar(0xB0) );
break;
case 3: // Kelvin to Rankine
v = i18n( "%1 is the temperature in Rankine", "%1 %2Ra" ).arg( strVal ).arg( TQChar(0xB0) );
break;
case 4: // Kelvin to Reaumur
v = i18n( "%1 is the temperature in Reaumur", "%1 %2R" ).arg( strVal ).arg( TQChar(0xB0) );
break;
}
}
}
else if ( type == EN ) //Electronegativity
{
val = electroneg();
if ( val <= 0 )
v = i18n( "Value not defined" );
else {
v = KalziumUtils::localizedValue( val, 6 );
}
}
else if ( type == EA ) //Electron affinity
{
val = electroaf();
if ( val == 0.0 )
v = i18n( "Value not defined" );
else
{
if ( Prefs::energies() == 0 )
{
v = i18n( "%1 kJ/mol" ).arg( KalziumUtils::localizedValue( val, 6 ) );
}
else // use electronvolt
{
val/=96.6;
v = i18n( "%1 eV" ).arg( KalziumUtils::localizedValue( val, 6 ) );
}
}
}
else if ( type == MASS ) // its a mass
{
val = mass();
if ( val <= 0 )
v = i18n( "Value unknown" );
else
v = i18n( "%1 u" ).arg( KalziumUtils::localizedValue( val, 6 ) );
}
else if ( type == DENSITY ) // its a density
{
val = density();
if ( val <= 0 )
v = i18n( "Value unknown" );
else
{
if ( boiling() < 295.0 && melting() < 295.0)//gasoline
{
v = i18n( "%1 g/L" ).arg( KalziumUtils::localizedValue( val, 6 ) );
}
else//liquid or solid
{
v = i18n( "%1 g/cm<sup>3</sup>" ).arg( KalziumUtils::localizedValue( val, 6 ));
}
}
}
else if ( type == DATE ) //its a date
{
val = date();
if ( val < 1600 )
{
v = i18n( "This element was known to ancient cultures" );
}
else
{
v = i18n( "This element was discovered in the year %1" ).arg( TQString::number( val ) );
}
}
return v;
}
void Element::drawStateOfMatter( TQPainter* p, double temp )
{
//the height of a "line" inside an element
int h_small = 15; //the size for the small units like elementnumber
//The X-coordiante
int X = xPos();
//The Y-coordinate
int Y = yPos();
TQColor color = currentColor( temp );
p->setPen( color );
p->fillRect( X, Y,ELEMENTSIZE,ELEMENTSIZE, color );
TQString text;
TQFont symbol_font = p->font();
symbol_font.setPointSize( 10 );
TQFont f = p->font();
f.setPointSize( 9 );
p->setFont( f );
//top left
p->setPen( TQt::black );
text = KalziumUtils::localizedValue( KalziumUtils::strippedValue( mass( ) ), 6 );
p->drawText( X,Y ,ELEMENTSIZE,h_small,TQt::AlignCenter, text );
text = TQString::number( number() );
p->drawText( X,Y+ELEMENTSIZE-h_small , ELEMENTSIZE, h_small,TQt::AlignCenter, text );
p->setFont( symbol_font );
p->drawText( X,Y, ELEMENTSIZE,ELEMENTSIZE,TQt::AlignCenter, symbol() );
//border
p->setPen( TQt::black );
p->drawRect( X, Y,ELEMENTSIZE+1,ELEMENTSIZE+1);
}
TQColor Element::currentColor( const double temp )
{
TQColor color;
//take the colours for the given temperature
const double iButton_melting = melting();
const double iButton_boiling = boiling();
//If either the mp or bp is not known return
//This is to avoid undefined behaviour
// if ( iButton_boiling <= 0.0 || iButton_melting <= 0.0 )
// return TQt::lightGray;
if ( temp < iButton_melting )
{ //the element is solid
color= Prefs::color_solid();
}
else if ( temp > iButton_melting &&
temp < iButton_boiling )
{ //the element is liquid
color= Prefs::color_liquid();
}
else if ( temp > iButton_boiling && iButton_boiling > 0.0 )
{ //the element is vaporous
color= Prefs::color_vapor();
}
else
color = TQt::lightGray;
return color;
}
void Element::drawGradient( TQPainter* p, const TQString& value, const TQColor& c)
{
//Set the elementColor to c to make the overviewwidget show
//the correct color
setElementColor( c );
//the height of a "line" inside an element
int h_small = 10; //the size for the small units like elementnumber
//The X-coordiante
int X = xPos();
//The Y-coordinate
int Y = yPos();
p->setPen( c );
p->fillRect( X, Y,ELEMENTSIZE,ELEMENTSIZE, c );
p->setPen( TQt::black );
TQFont symbol_font = p->font();
TQFont f = p->font();
f.setPointSize( KalziumUtils::maxSize(value, TQRect( X,Y+ELEMENTSIZE-h_small, ELEMENTSIZE, h_small ),f, p ) );
p->setFont( f );
p->drawText( X,Y+ELEMENTSIZE-h_small , ELEMENTSIZE, h_small,TQt::AlignCenter, value );
const TQRect rect = TQRect( X,Y,ELEMENTSIZE-2,ELEMENTSIZE-10 );
int goodsize = KalziumUtils::maxSize( symbol(), rect, symbol_font, p );
symbol_font.setPointSize( goodsize );
p->setFont( symbol_font );
p->drawText( X+1,Y+5, ELEMENTSIZE-2,ELEMENTSIZE-10,TQt::AlignCenter, symbol() );
//border
p->setPen( TQt::black );
p->drawRect( X, Y,ELEMENTSIZE+1,ELEMENTSIZE+1);
}
void Element::drawGrayedOut( TQPainter *p )
{
//The X-coordiante
int X = xPos();
//The Y-coordinate
int Y = yPos();
p->fillRect( X, Y,ELEMENTSIZE,ELEMENTSIZE, TQt::lightGray );
p->setPen( TQt::darkGray );
TQFont symbol_font = p->font();
const TQRect rect = TQRect( X,Y,ELEMENTSIZE-2,ELEMENTSIZE-10 );
int goodsize = KalziumUtils::maxSize( symbol(), rect, symbol_font, p );
symbol_font.setPointSize( goodsize );
p->setFont( symbol_font );
p->drawText( X+1,Y+5, ELEMENTSIZE-2,ELEMENTSIZE-10,TQt::AlignCenter, symbol() );
p->setPen( TQt::black );
p->drawRect( X, Y,ELEMENTSIZE+1,ELEMENTSIZE+1);
}
void Element::drawSelf( TQPainter* p, bool simple, bool isCrystal )
{
//the height of a "line" inside an element
int h_small = 12; //the size for the small units like elementnumber
//The X-coordiante
int X = xPos();
//The Y-coordinate
int Y = yPos();
p->setPen( elementColor() );
p->fillRect( X, Y,ELEMENTSIZE,ELEMENTSIZE, elementColor() );
p->setPen( TQt::black );
TQString text;
TQFont symbol_font = p->font();
const int max = ELEMENTSIZE-10;
const TQRect rect = TQRect( X,Y,ELEMENTSIZE-2,max );
int goodsize = KalziumUtils::maxSize( symbol(), rect, symbol_font, p );
symbol_font.setPointSize( goodsize );
p->setFont( symbol_font );
if ( !simple )
p->drawText( X+1,Y+5, ELEMENTSIZE-2,max,TQt::AlignCenter, symbol() );
else
p->drawText( X+1,Y+5, ELEMENTSIZE-2,max,TQt::AlignHCenter, symbol() );
TQFont f = p->font();
TQRect smallRect( X,Y ,ELEMENTSIZE-4,h_small );
f.setPointSize( KalziumUtils::maxSize( TQString::number( number() ), smallRect, f, p ) );
p->setFont( f );
if ( !simple )
{//the user only wants a simple periodic table, don't weight the cell
TQString text;
if ( isCrystal )
{
TQString structure = crystalstructure();
/**
* hcp: hexagonal close packed
* fcc: face centered cubic
* krz/bcc body centered cubic// kubisch raumzentriert
* kdp: kubisch dicht gepackt
* hdp: hexagonal dicht gepackt
* ccp: cubic close packed // kubisch dichteste Kugelpackung
* d : diamond
* sc : simple cubic
* tet: tetragonal
* rh : rhombohedral
* or : orthorhombic
* mono: monoclinic
*/
if ( structure == "own")
text = i18n( "this means, the element has its 'own' structur", "own" );
else if ( structure == "bcc" )
text = i18n( "Crystalsystem body centered cubic", "bcc" );
else if ( structure == "hdp" )
text = i18n( "Crystalsystem hexagonal dense packed", "hdp" );
else if ( structure == "ccp" )
text = i18n( "Crystalsystem cubic close packed", "ccp" );
}
else
text = KalziumUtils::localizedValue( KalziumUtils::strippedValue( mass( ) ), 6 );
p->drawText( X+2,Y ,ELEMENTSIZE-4 ,h_small,TQt::AlignCenter, text );
}
text = TQString::number( number() );
p->drawText( X+2,Y+ELEMENTSIZE-h_small , ELEMENTSIZE-4, h_small,TQt::AlignCenter, text );
p->drawRect( X, Y,ELEMENTSIZE+1,ELEMENTSIZE+1);
}
/*!
This looks pretty evil and it is. The problem is that a PerodicTableView is pretty
irregular and you cannot "calculate" the position.
*/
void Element::setupXY()
{
static const int posXRegular[111] = {1,18,
1,2,13,14,15,16,17,18,
1,2,13,14,15,16,17,18,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18, //Element 54 (Xe)
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17, //Element 71 (Lu)
4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17, //Element 71 (Lr)
4,5,6,7,8,9,10,11};
static const int posYRegular[111] = {1,1,
2,2, 2, 2, 2, 2, 2, 2,
3,3, 3, 3, 3, 3, 3, 3,
4,4,4,4,4,4,4,4,4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5,5,5,5,5,5,5,5,5, 5, 5, 5, 5, 5, 5, 5, 5, 5, //Element 54 (Xe)
6,6,6,8,8,8,8,8,8, 8, 8, 8, 8, 8, 8, 8, 8, //Element 71 (Lr)
6,6,6,6,6,6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7,7,7,9,9,9,9,9,9, 9, 9, 9, 9, 9, 9, 9, 9,
7,7,7,7,7,7,7,7};
x = posXRegular[m_number-1];
y = posYRegular[m_number-1];
}
void Element::setRadius( RADIUSTYPE type, double value, const TQString& name )
{
switch ( type )
{
case ATOMIC:
m_RadiusAR = value;
break;
case IONIC:
m_RadiusIon = value;
m_ionvalue = name;
break;
case COVALENT:
m_RadiusCR = value;
break;
case VDW:
m_RadiusVDW = value;
break;
}
}
double Element::radius( RADIUSTYPE type )
{
switch ( type )
{
case ATOMIC:
return m_RadiusAR;
break;
case IONIC:
return m_RadiusIon;
break;
case COVALENT:
return m_RadiusCR;
break;
case VDW:
return m_RadiusVDW;
break;
}
return 0.0;
}
int Element::xPos() const
{
return ( x-1 )*ELEMENTSIZE;
}
int Element::yPos() const
{
// mind the small gap over rare earth!
int tmp_y = ( y-1 )*ELEMENTSIZE + ELEMENTSIZE;
// 57=Lanthanum, 72=Hafnium, 89=Actinium & 104=Rutherfordium (i.e., if
// n_number is in rare earth's block)
if ( (m_number > 57 && m_number < 72) || (m_number > 89 && m_number < 104) )
tmp_y += ELEMENTSIZE/3;
return tmp_y;
}
TQPoint Element::pos() const
{
return TQPoint( xPos(), yPos() );
}
TQPoint Element::coords() const
{
return TQPoint( x, y );
}