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.

838 lines
24 KiB

/***************************************************************************
ksmatrix.cpp
-------------------
begin : 01-January-2000
copyright : (C) 2000 by Kamil Dobkowski
email : kamildobk@poczta.onet.pl
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include"ksmatrix.h"
#include"widgets/qsdata.h"
#include"widgets/qsconsole.h"
#include"formula/mpformula.h"
#include"ksworkbook.h"
#include"ksglobalmatrixlist.h"
#include<qstrlist.h>
#include<qmetaobject.h>
#include<iostream>
/**
* Reference points to QSData::channel !
*/
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
KSMatrix *KSMatrix::create( EType elementType )
{
switch( elementType ) {
case EUChar: return new KSMatrixImpl<unsigned char>(EUChar);
case EUShort: return new KSMatrixImpl<unsigned short>(EUShort);
case EShort: return new KSMatrixImpl<short>(EShort);
case ELong: return new KSMatrixImpl<long>(ELong);
case EFloat: return new KSMatrixImpl<float>(EFloat);
case EDouble: return new KSMatrixImpl<double>(EDouble);
}
return NULL;
}
//-------------------------------------------------------------//
//
// Flexible, but too much complicated
// Drop line_offset and pixel_offset and make some template specializations with '<<' instead of '*' ( used in DDE with Octave )
//
KSMatrix::KSMatrix( EType elementType )
:QSMatrix()
{
m_type = elementType;
m_rows = 0;
m_cols = 0;
m_ptr = NULL;
m_lo = 0;
m_po = 0;
m_delete = true;
}
//-------------------------------------------------------------//
EType KSMatrix::detectType()
{
double max = 0.0;
double min = 0.0;
EType type = EUChar;
for( int r=0; r<rows(); r++ )
for( int c=0; c<cols(); c++ ) {
double val = value(r,c);
if ( type == EUChar && val != double((unsigned char)val) ) type = EShort;
if ( type == EShort && val != double((short)val) ) type = ELong;
if ( type == ELong && val != double((long)val) ) type = EFloat;
if ( type == EFloat && val != double((float)val) ) type = EDouble;
if ( type == EDouble ) break;
if ( r == 0 && c == 0 ) max = min = val; else { max = max<val?val:max; min = min>val?val:min; }
}
if ( type == ELong && min >= 0x0 && max <= 0xffff ) type = EUShort;
return type;
}
//-------------------------------------------------------------//
KSMatrix::~KSMatrix()
{
if ( m_delete ) delete m_ptr;
}
//-------------------------------------------------------------//
bool KSMatrix::isEditable() const
{
return true;
}
//-------------------------------------------------------------/
EType KSMatrix::type() const
{
return m_type;
}
//-------------------------------------------------------------//
KSMatrix *KSMatrix::convertType( EType newType )
{
KSMatrix *result = KSMatrix::create( newType );
result->setName( name() );
result->setDataObject( dataObject(), channel() );
result->setRawData( new char[m_rows*m_cols*result->elementSize()], m_rows, m_cols, true );
for ( int crow=0; crow<m_rows; crow++ )
for ( int ccol=0; ccol<m_cols; ccol++ )
result->setValue( crow, ccol, value(crow,ccol) );
return result;
}
//-------------------------------------------------------------//
bool KSMatrix::resize( int rows, int cols )
{
if ( rows <= 0 || cols <= 0 ) {
setRawData( NULL, 0, 0 );
} else {
KSMatrix *new_data = KSMatrix::create( m_type );
new_data->setRawData( new char[rows*cols*new_data->elementSize()], rows, cols, false );
for ( int crow=0; crow<new_data->rows(); crow++ )
for ( int ccol=0; ccol<new_data->cols(); ccol++ )
if ( crow<m_rows && ccol<m_cols ) new_data->setValue( crow, ccol, value(crow,ccol) );
else new_data->setValue( crow, ccol, 0.0 );
setRawData( new_data->ptr(), new_data->rows(), new_data->cols(), true );
delete new_data;
}
return true;
}
//-------------------------------------------------------------//
bool KSMatrix::transpose()
{
int temp;
temp = m_rows; m_rows = m_cols; m_cols = temp;
temp = m_lo; m_lo = m_po; m_po = temp;
return true;
}
//-------------------------------------------------------------//
void KSMatrix::setRawData( void *ptr, int rows, int cols, bool deleteData, int lineOffset, int pixelOffset )
{
if ( m_delete ) delete m_ptr;
m_ptr = (char *)ptr;
m_rows = rows;
m_cols = cols;
m_delete = deleteData;
m_po = pixelOffset ? pixelOffset : elementSize();
m_lo = lineOffset ? lineOffset : m_po*cols;
}
//-------------------------------------------------------------//
void KSMatrix::setMatrix( QSMatrix *matrix )
{
setRawData( new char[matrix->rows()*matrix->cols()*elementSize()], matrix->rows(), matrix->cols(), true );
for ( int crow=0; crow<m_rows; crow++ )
for ( int ccol=0; ccol<m_cols; ccol++ )
setValue( crow, ccol, matrix->value(crow,ccol) );
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
KSMatrixString::KSMatrixString()
{
m_rows = 0;
m_cols = 0;
m_transposed = false;
m_string_table = NULL;
}
//-------------------------------------------------------------//
KSMatrixString::~KSMatrixString()
{
delete[] m_string_table;
}
//-------------------------------------------------------------//
bool KSMatrixString::isEditable() const
{
return true;
}
//-------------------------------------------------------------//
bool KSMatrixString::isString() const
{
return true;
}
//-------------------------------------------------------------//
void KSMatrixString::setValue( int row, int col, double value )
{
setString( row, col, QString::number(value,'g',9) );
}
//-------------------------------------------------------------//
double KSMatrixString::value( int row, int col )
{
return string(row,col).toDouble();
}
//-------------------------------------------------------------//
int KSMatrixString::cols() const
{
return m_transposed ? m_rows : m_cols;
}
//-------------------------------------------------------------//
int KSMatrixString::rows() const
{
return m_transposed ? m_cols : m_rows;
}
//-------------------------------------------------------------//
void KSMatrixString::setString( int row, int col, const QString& string )
{
if ( m_transposed ) m_string_table[ col*m_cols+row ] = string;
else m_string_table[ row*m_cols+col ] = string;
}
//-------------------------------------------------------------//
QString KSMatrixString::string( int row, int col )
{
return m_transposed ? m_string_table[col*m_cols+row] : m_string_table[row*m_cols+col];
}
//-------------------------------------------------------------//
bool KSMatrixString::resize( int new_rows, int new_cols )
{
QString *new_string_table = new QString[new_rows*new_cols];
for( int curr_row=0; curr_row<QMIN(new_rows,rows()); curr_row++ )
for( int curr_col=0; curr_col<QMIN(new_cols,cols()); curr_col++ ) {
new_string_table[curr_row*new_cols+curr_col] = string(curr_row,curr_col);
}
delete[] m_string_table; m_string_table = new_string_table;
m_rows = new_rows;
m_cols = new_cols;
m_transposed = false;
return true;
}
//-------------------------------------------------------------//
bool KSMatrixString::transpose()
{
m_transposed = !m_transposed;
return true;
}
//-------------------------------------------------------------//
void KSMatrixString::setMatrix( QSMatrix *matrix )
{
delete[] m_string_table;
m_rows = matrix->rows();
m_cols = matrix->cols();
m_transposed = false;
m_string_table = new QString[m_rows*m_cols];
for ( int crow=0; crow<m_rows; crow++ )
for ( int ccol=0; ccol<m_cols; ccol++ )
setString( crow, ccol, matrix->string(crow,ccol) );
}
//-------------------------------------------------------------//
void KSMatrixString::loadStateFromStream( QDataStream& stream, QSObjectFactory *factory )
{
QSMatrix::loadStateFromStream( stream, factory );
int new_rows; stream >> new_rows;
int new_cols; stream >> new_cols;
resize( new_rows, new_cols );
for( int row=0; row<rows(); row++ )
for( int col=0; col<cols(); col++ ) {
QString element; stream >> element;
setString( row, col, element );
}
}
//-------------------------------------------------------------//
void KSMatrixString::saveStateToStream( QDataStream& stream, QSObjectFactory *factory )
{
QSMatrix::saveStateToStream( stream, factory );
stream << (int )rows();
stream << (int )cols();
for( int row=0; row<rows(); row++ )
for( int col=0; col<cols(); col++ ) {
QString element = string(row,col);
stream << element;
}
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
KSMatrixRef::KSMatrixRef()
{
m_rows = 0;
m_cols = 0;
m_ref_matrix = NULL;
m_ref_object = NULL;
m_ref_channel = 0;
m_ref_col_from = 0;
m_ref_col_to = -1;
m_ref_col_step = 1;
m_ref_row_from = 0;
m_ref_row_to = -1;
m_ref_row_step = 1;
m_start_row = 0;
m_start_col = 0;
m_transposition = false;
m_ref_editable = false;
m_ref_string = false;
m_in_loop = false; // desn't allow circular references
}
//-------------------------------------------------------------//
KSMatrixRef::~KSMatrixRef()
{
}
//-------------------------------------------------------------//
void KSMatrixRef::setRefObject( QSData *dataObject, int channel,
int rowFrom, int rowStep, int rowTo,
int colFrom, int colStep, int colTo,
bool transposition )
{
if ( dataObject != m_data_object || channel != m_channel ) {
if ( channel < 0 ) dataObject = NULL;
m_ref_row_from = rowFrom;
m_ref_row_to = rowTo;
m_ref_row_step = rowStep ? rowStep : 1;
m_ref_col_from = colFrom;
m_ref_col_to = colTo;
m_ref_col_step = colStep ? colStep : 1;
m_transposition = transposition;
if ( dataObject ) {
m_ref_object = dataObject;
m_ref_channel = channel;
connect ( dataObject, SIGNAL(sigDataChanged(QSData*,int)), this, SLOT(reconnect(QSData*,int)) );
connect ( dataObject, SIGNAL(sigDeleted(QSData*)), this, SLOT(break_connection(QSData*)) );
reconnect();
} else {
break_connection( m_ref_object );
}
}
}
//-------------------------------------------------------------//
void KSMatrixRef::setValue( int row, int col, double value )
{
if ( !m_in_loop ) {
m_in_loop = true;
if ( m_transposition ) m_ref_matrix->setValue( m_start_col+col*m_ref_col_step, m_start_row+row*m_ref_row_step, value );
else m_ref_matrix->setValue( m_start_row+row*m_ref_row_step, m_start_col+col*m_ref_col_step, value );
m_in_loop = false;
}
}
//-------------------------------------------------------------//
double KSMatrixRef::value( int row, int col )
{
double result;
if ( !m_in_loop ) {
m_in_loop = true;
if ( m_transposition ) result = m_ref_matrix->value( m_start_col+col*m_ref_col_step, m_start_row+row*m_ref_row_step );
else result = m_ref_matrix->value( m_start_row+row*m_ref_row_step, m_start_col+col*m_ref_col_step );
m_in_loop = false;
} else {
result = sqrt(-1.0);
}
return result;
}
//-------------------------------------------------------------//
void KSMatrixRef::setString( int row, int col, const QString& string )
{
if ( !m_in_loop ) {
m_in_loop = true;
if ( m_transposition ) m_ref_matrix->setString( m_start_col+col*m_ref_col_step, m_start_row+row*m_ref_row_step, string );
else m_ref_matrix->setString( m_start_row+row*m_ref_row_step, m_start_col+col*m_ref_col_step, string );
m_in_loop = false;
}
}
//-------------------------------------------------------------//
QString KSMatrixRef::string( int row, int col )
{
QString result;
if ( !m_in_loop ) {
m_in_loop = true;
if ( m_transposition ) result = m_ref_matrix->string( m_start_col+col*m_ref_col_step, m_start_row+row*m_ref_row_step );
else result = m_ref_matrix->string( m_start_row+row*m_ref_row_step, m_start_col+col*m_ref_col_step );
m_in_loop = false;
}
return result;
}
//-------------------------------------------------------------//
void KSMatrixRef::reconnect( QSData *dataObject, int channel )
{
if ( m_in_loop ) QSConsole::write( "Kmatplot: Circular reference !" );
else
if ( m_ref_object && m_ref_object == dataObject && (m_ref_channel == channel || channel == -1) ) // our referenced object changed
{
m_in_loop = true;
dataChanging();
reconnect();
dataChanged();
m_in_loop = false;
}
}
//-------------------------------------------------------------//
void KSMatrixRef::reconnect()
{
m_ref_matrix = m_ref_object->matrix( m_ref_channel );
int ref_rows = m_ref_matrix ? m_ref_matrix->rows() : 0;
int ref_cols = m_ref_matrix ? m_ref_matrix->cols() : 0;
m_start_row = ( m_ref_row_from == -1 ) ? ref_rows-1 : m_ref_row_from;
m_start_col = ( m_ref_col_from == -1 ) ? ref_cols-1 : m_ref_col_from;
int end_row = ( m_ref_row_to == -1 ) ? ref_rows-1 : m_ref_row_to;
int end_col = ( m_ref_col_to == -1 ) ? ref_cols-1 : m_ref_col_to;
if ( m_ref_row_step > 0 ) {
m_start_row = QMAX( m_start_row, 0 );
end_row = QMIN( end_row, ref_rows-1 );
m_rows = ( end_row - m_start_row ) / m_ref_row_step + 1;
} else {
m_start_row = QMIN( m_start_row, ref_rows-1 );
end_row = QMAX( end_row, 0 );
m_rows = ( end_row - m_start_row ) / m_ref_row_step + 1;
}
if ( m_ref_col_step > 0 ) {
m_start_col = QMAX( m_start_col, 0 );
end_col = QMIN( end_col, ref_cols-1 );
m_cols = ( end_col - m_start_col ) / m_ref_col_step + 1;
} else {
m_start_col = QMIN( m_start_col, ref_cols-1 );
end_col = QMAX( end_col, 0 );
m_cols = ( end_col - m_start_col ) / m_ref_col_step + 1;
}
m_rows = QMAX( m_rows, 0 );
m_cols = QMAX( m_cols, 0 );
if ( m_transposition ) {
int temp = m_cols;
m_cols = m_rows;
m_rows = temp;
}
if ( m_rows*m_cols == 0 ) m_rows = m_cols = 0;
m_ref_editable = m_ref_matrix ? m_ref_matrix->isEditable() : false;
m_ref_string = m_ref_matrix ? m_ref_matrix->isString() : false;
}
//-------------------------------------------------------------//
void KSMatrixRef::break_connection( QSData *dataObject )
{
if ( m_in_loop ) QSConsole::write( "Kmatplot: Circular reference !" );
else
if ( m_ref_object && m_ref_object == dataObject ) {
m_in_loop = true;
dataChanging();
m_rows = 0;
m_cols = 0;
m_start_row = 0;
m_start_col = 0;
m_ref_matrix = NULL;
m_ref_object = NULL;
m_ref_channel = 0;
m_in_loop = false;
m_ref_editable = false;
m_ref_string = false;
dataChanged();
m_in_loop = false;
}
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
KSMatrixWorksheetCellRange::KSMatrixWorksheetCellRange( KSWorkbook *workbook )
: KSMatrixRef()
{
m_workbook = workbook;
}
//-------------------------------------------------------------//
KSMatrixWorksheetCellRange::~KSMatrixWorksheetCellRange()
{
// unregister
KSSheet *curr_sheet = dynamic_cast<KSSheet*>(refObject());
if ( curr_sheet ) curr_sheet->cellRangeRemove( this );
}
//-------------------------------------------------------------//
void KSMatrixWorksheetCellRange::setWorksheet( int index )
{
KSSheet *sheet = m_workbook->sheets()->child(index);
if ( sheet != refObject() ) {
KSSheet *curr_sheet = dynamic_cast<KSSheet*>(refObject());
KSSheet *new_sheet = m_workbook->sheets()->child(index);
if ( curr_sheet ) curr_sheet->cellRangeRemove( this );
setRefObject( new_sheet, 0,
rowFrom(), rowStep(), rowTo(),
colFrom(), colStep(), colTo(),
transposition() );
if ( new_sheet ) new_sheet->cellRangeAdd( this );
}
}
//-------------------------------------------------------------//
int KSMatrixWorksheetCellRange::worksheet() const
{
return m_workbook->sheets()->childIndex(refObject());
}
//-------------------------------------------------------------//
void KSMatrixWorksheetCellRange::setColumn( int column )
{
setRefObject( refObject(), 0,
0, 1, -1,
column, 1, column,
transposition() );
}
//-------------------------------------------------------------//
void KSMatrixWorksheetCellRange::setRowFrom( int row )
{
setRefObject( refObject(), 0,
row, rowStep(), rowTo(),
colFrom(), colStep(), colTo(),
transposition() );
}
//-------------------------------------------------------------//
void KSMatrixWorksheetCellRange::setColFrom( int col )
{
setRefObject( refObject(), 0,
rowFrom(), rowStep(), rowTo(),
col, colStep(), colTo(),
transposition() );
}
//-------------------------------------------------------------//
void KSMatrixWorksheetCellRange::setRowStep( int step )
{
setRefObject( refObject(), 0,
rowFrom(), step, rowTo(),
colFrom(), colStep(), colTo(),
transposition() );
}
//-------------------------------------------------------------//
void KSMatrixWorksheetCellRange::setColStep( int step )
{
setRefObject( refObject(), 0,
rowFrom(), rowStep(), rowTo(),
colFrom(), step, colTo(),
transposition() );
}
//-------------------------------------------------------------//
void KSMatrixWorksheetCellRange::setRowTo( int row )
{
setRefObject( refObject(), 0,
rowFrom(), rowStep(), row,
colFrom(), colStep(), colTo(),
transposition() );
}
//-------------------------------------------------------------//
void KSMatrixWorksheetCellRange::setColTo( int col )
{
setRefObject( refObject(), 0,
rowFrom(), rowStep(), rowTo(),
colFrom(), colStep(), col,
transposition() );
}
//-------------------------------------------------------------//
void KSMatrixWorksheetCellRange::setTransposition( bool enabled )
{
setRefObject( refObject(), 0,
rowFrom(), rowStep(), rowTo(),
colFrom(), colStep(), colTo(),
enabled );
}
//-------------------------------------------------------------//
void KSMatrixWorksheetCellRange::loadStateFromStream( QDataStream& stream, QSObjectFactory *factory )
{
KSMatrixRef::loadStateFromStream( stream, factory );
}
//-------------------------------------------------------------//
void KSMatrixWorksheetCellRange::saveStateToStream( QDataStream& stream, QSObjectFactory *factory )
{
KSMatrixRef::saveStateToStream( stream, factory );
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
KSMatrixFormula::KSMatrixFormula()
:QSMatrix()
{
m_formula = "0:1:10";
m_rows = 0;
m_cols = 0;
m_data = NULL;
m_row_offset = 0;
m_col_offset = 0;
m_transposition = false;
}
//-------------------------------------------------------------//
KSMatrixFormula::~KSMatrixFormula()
{
delete m_data;
}
//-------------------------------------------------------------//
void KSMatrixFormula::setFormula( const QString& formula )
{
m_formula = formula;
}
//-------------------------------------------------------------//
bool KSMatrixFormula::init( MPError& error, MPFactoryList *locals )
{
delete m_data;
m_rows = 0;
m_cols = 0;
m_data = NULL;
m_row_offset = 0;
m_col_offset = 0;
MPFormula f;
MPSymbol *s = f.parse( m_formula, error, locals );
if ( s ) {
m_cols = s->cols();
m_rows = s->rows();
m_row_offset = m_cols;
m_col_offset = 1;
m_data = new double[m_rows*m_cols];
for( int row=0; row<m_rows; row++ )
for( int col=0; col<m_cols; col++ )
m_data[row*m_cols+col] = s->value(row,col);
if ( m_transposition ) { transpose(); m_transposition = true; }
delete s;
return true;
}
return false;
}
//-------------------------------------------------------------//
double KSMatrixFormula::value( int row, int col )
{
return *(m_data + m_row_offset*row + m_col_offset*col);
}
//-------------------------------------------------------------//
bool KSMatrixFormula::transpose()
{
int temp;
temp = m_rows; m_rows = m_cols; m_cols = temp;
temp = m_row_offset; m_row_offset = m_col_offset; m_col_offset = temp;
m_transposition = !m_transposition;
return true;
}
//-------------------------------------------------------------//
void KSMatrixFormula::setTransposition( bool enabled )
{
if ( enabled != m_transposition ) {
transpose();
}
}
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
MPSymQSMatrix::MPSymQSMatrix( QSMatrix *matrix, MPSymbolList *args, int columnFrom, int columnTo, const char *id )
: MPSymbol( args, columnFrom, columnTo, id )
{
m_matrix = matrix;
m_range = QRect(0,0,-1,-1);
}
//-------------------------------------------------------------------------//
MPSymQSMatrix::MPSymQSMatrix( QSMatrix *matrix, const QRect& range, MPSymbolList *args, int columnFrom, int columnTo, const char *id )
: MPSymbol( args, columnFrom, columnTo, id )
{
m_matrix = matrix;
m_range = range;
}
//-------------------------------------------------------------------------//
MPSymQSMatrix::~MPSymQSMatrix()
{
}
//-------------------------------------------------------------------------//
void MPSymQSMatrix::checkArgs( MPError& )
{
m_matrix_row_max = m_matrix->rows()-1;
m_matrix_col_max = m_matrix->cols()-1;
if ( m_range.isEmpty() ) {
m_rows = m_matrix->rows();
m_cols = m_matrix->cols();
} else {
m_rows = m_range.height();
m_cols = m_range.width();
}
}
//-------------------------------------------------------------------------//
double MPSymQSMatrix::value( int row, int col )
{
return m_matrix->value( QMIN(row+m_range.top(), m_matrix_row_max ),
QMIN(col+m_range.left(), m_matrix_col_max ) );
}
//-------------------------------------------------------------------------//