diff options
author | Constantin Jucovschi <cj@ubuntu.localdomain> | 2009-04-24 07:20:22 -0400 |
---|---|---|
committer | Constantin Jucovschi <cj@ubuntu.localdomain> | 2009-04-24 07:20:22 -0400 |
commit | 8f27e65bddd7d4b8515ce620fb485fdd78fcdf89 (patch) | |
tree | bd328a4dd4f92d32202241b5e3a7f36177792c5f /applications | |
download | rasdaman-upstream-8f27e65bddd7d4b8515ce620fb485fdd78fcdf89.tar.gz rasdaman-upstream-8f27e65bddd7d4b8515ce620fb485fdd78fcdf89.tar.xz rasdaman-upstream-8f27e65bddd7d4b8515ce620fb485fdd78fcdf89.zip |
Initial commitv8.0
Diffstat (limited to 'applications')
63 files changed, 42028 insertions, 0 deletions
diff --git a/applications/Makefile.am b/applications/Makefile.am new file mode 100644 index 0000000..831ab57 --- /dev/null +++ b/applications/Makefile.am @@ -0,0 +1,32 @@ +# -*-Makefile-*- +# This file is part of rasdaman community. +# +# Rasdaman community 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 3 of the License, or +# (at your option) any later version. +# +# Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +# +# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +# rasdaman GmbH. +# +# For more information please see <http://www.rasdaman.org> +# or contact Peter Baumann via <baumann@rasdaman.com>. +# +# MAKEFILE FOR: +# rasdaman applications +# does nothing but distribute any make call into all subdirs +# +# COMMENTS: +# +################################################################## + +SUBDIRS=rasql +# should also contain rview and directql diff --git a/applications/directql/Makefile b/applications/directql/Makefile new file mode 100644 index 0000000..8852a61 --- /dev/null +++ b/applications/directql/Makefile @@ -0,0 +1,89 @@ +# -*-Makefile-*- +# +# This file is part of rasdaman community. +# +# Rasdaman community 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 3 of the License, or +# (at your option) any later version. +# +# Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +# +# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +# rasdaman GmbH. +# +# For more information please see <http://www.rasdaman.org> +# or contact Peter Baumann via <baumann@rasdaman.com>. # Top Level makefile. This points to the various modules that have to be build +# and/or deployed +# +# MAKEFILE FOR: +# test programs of module servercomm +# +# COMMENTS: +# +################################################################## +# +# This is just an example Makefile for a test program. +# The dependency of the test program on the lib of the +# corresponding module is in the Makefile of the module. +# + +######################### Definitions ############################ + +# standard include with general options +include $(RMANBASE)/Makefile.inc + +# all test programs +SRCCXX= directql.cc directql_error.cc directql_signal.cc +OBJS = ${SRCCXX:%.cc=%.o} +ALLTESTS = ${SRCCXX:%.cc=%} + +# some additional flags for compiling and linking + +CXXFLAGS += -I$(RMANBASE)/servercomm -I$(RMANBASE)/commline +LDFLAGS := -I$(RMANBASE)/servercomm $(LDFLAGS) + +# add communication flags +CXXFLAGS += $(COMMCXXFLAGS) +LDFLAGS += $(COMMLDFLAGS) + +# add compile and link options for STL +CXXFLAGS += $(STLCXXFLAGS) +LDFLAGS += $(STLLDFLAGS) -L$(RMANBASE)/lib + +########################### Targets ############################## + +# test target for servercomm +.PHONY : directql + +directql: $(OBJS) \ + $(QLPARSER) \ + $(SERVERCOMM) \ + $(CLIENTCOMM) \ + $(CACHETAMGR) \ + $(MDDIF) \ + $(CATALOGIF) \ + $(INDEXIF) \ + $(INDEXMGR) \ + $(BLOBIF) \ + $(ADMINIF) \ + $(PREPROCESSOR) \ + $(STORAGEMGR) \ + $(TILEMGR) \ + $(RASLIB) \ + $(RELINDEX) \ + $(INDEXIF) + $(CXX) $(LDFLAGS) -o $@ $^ -lrasodmg $(CLIENTCOMM) -lnetwork $(QLPARSER) $(INDEXMGR) $(CACHETAMGR) $(RELINDEX) $(RELSTORAGEIF) $(RELADMINIF) $(STORAGEMGR) $(CONVERSION) $(INDEXIF) $(RELCATALOGIF) -lcommline -lpq -lecpg -lssl -lcrypto -lcrypt -lpgtypes -lm -ljpeg -ltiff -lpng -lmfhdf -ldf -lz -ldl -rdynamic -Xlinker --export-dynamic + + +# general rules +include $(RMANBASE)/Makefile.rel + +# automatically created dependencies +include Makefile.dep diff --git a/applications/directql/directql.cc b/applications/directql/directql.cc new file mode 100644 index 0000000..98bd3ba --- /dev/null +++ b/applications/directql/directql.cc @@ -0,0 +1,993 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** +* rasql +* +* PURPOSE: +* Provides a command line interpretter for rasql queries, with +* options for displaying results or storing them to file(s) +* +* COMMENTS: +* +* BUGS: +* - query filename "" is interpreted as stdin +*/ + + +static const char rasql_rcsid[] = "@(#)rasql,rasql.cc: $Id: rasql.cc,v 1.3 2006/11/06 21:59:01 rasdev Exp $"; + +#ifndef RMANVERSION +#error "Please specify RMANVERSION variable!" +#endif + +#ifndef COMPDATE +#error "Please specify the COMPDATE variable!" +/* +COMPDATE=`date +"%d.%m.%Y %H:%M:%S"` +and -DCOMPDATE="\"$(COMPDATE)\"" when compiling +*/ +#endif + +#define __EXECUTABLE__ +#define EARLY_TEMPLATE +#define DEBUG_MAIN +#define DEBUG +#define RMANDEBUG + +#include "debug.hh" +#include "template_inst.hh" +#include "raslib/template_inst.hh" + +extern char* myExecArgv0 = ""; +extern int tiling = 1; +extern unsigned long maxTransferBufferSize = 4000000; +extern int globalOptimizationLevel = 4; +extern char* dbSchema = 0; +extern int noTimeOut = 0; +bool udfEnabled = true; + +//RMINITGLOBALS('C'); + + +#ifdef __VISUALC__ +#include <strstrea.h> +#else +#include <strstream> +#endif + +#include <stdio.h> +#include <string.h> +#include <sstream> +#include <fstream> + +using namespace std; + +#ifdef __VISUALC__ + #define __EXECUTABLE__ +#endif + +#include "rasodmg/ref.hh" +#include "raslib/marraytype.hh" +#include "rasodmg/set.hh" +#include "rasodmg/marray.hh" +#include "rasodmg/iterator.hh" +#include "rasodmg/oqlquery.hh" + +#include "raslib/type.hh" + +#include "raslib/minterval.hh" + +#include "raslib/primitive.hh" +#include "raslib/complex.hh" +#include "raslib/structure.hh" + +#include "raslib/structuretype.hh" +#include "raslib/primitivetype.hh" + +#include "cmlparser.hh" + +#include "directql_error.hh" + +#include "servercomm/servercomm.hh" + + +#ifdef __VISUALC__ + #undef __EXECUTABLE__ +#endif + +// debug facility; relies on -DDEBUG at compile time +// tell debug that here is the place for the variables (to be done in the main() src file) +#define DEBUG_MAIN +#include "debug-clt.hh" + +const int MAX_STR_LEN = 255; +const int MAX_QUERY_LEN = 10240; + +// possible types of output +typedef enum +{ OUT_UNDEF, + OUT_FILE, + OUT_NONE, + OUT_STRING, + OUT_HEX, + OUT_FORMATTED +} OUTPUT_TYPE; + +// rasdaman MDD type for byte strings (default type used for file format reading) +#define MDD_STRINGTYPE "GreyString" + +/// program exit codes +#define EXIT_SUCCESS 0 +#define EXIT_USAGE 2 +#define EXIT_FAILURE -1 + +// parameter names, defaults, and help texts + +#define PARAM_HELP_FLAG 'h' +#define PARAM_HELP "help" +#define HELP_HELP "show command line switches" + +#define PARAM_SERV_FLAG 's' +#define PARAM_SERV "server" +#define HELP_SERV "<host-name> rasdaman server" +#define DEFAULT_SERV "localhost" + +#define PARAM_PORT_FLAG 'p' +#define PARAM_PORT "port" +#define HELP_PORT "<p> rasmgr port number" +#define DEFAULT_PORT 5432 +#define DEFAULT_PORT_STR "5432" + +#define PARAM_DB_FLAG 'd' +#define PARAM_DB "database" +#define HELP_DB "<db-name> name of database" +#define DEFAULT_DB "RASBASE" + +#define PARAM_USER "user" +#define HELP_USER "<user-name> name of user" +#define DEFAULT_USER "rasguest" + +#define PARAM_PASSWD "passwd" +#define HELP_PASSWD "<user-passwd> password of user" +#define DEFAULT_PASSWD "rasguest" + +#define PARAM_FILE_FLAG 'f' +#define PARAM_FILE "file" +#define HELP_FILE "<f> file name for upload through $i parameters within queries; each $i needs its own file parameter, in proper sequence. Requires --mdddomain and --mddtype" + +#define PARAM_DOMAIN "mdddomain" +#define HELP_DOMAIN "<mdd-domain> domain of marray, format: \'[x0:x1,y0:y1]\' (required only if --file specified and file is in data format r_Array)" + +#define PARAM_MDDTYPE "mddtype" +// this is for display only; internally MDD_STRINGTYPE is used +#define DEFAULT_MDDTYPE "byte string" +#define HELP_MDDTYPE "<mdd-type> type of marray (required only if --file specified and file is in data format r_Array)" + +#define PARAM_QUERY_FLAG 'q' +#define PARAM_QUERY "query" +#define HELP_QUERY "<q> query string to be sent to the rasdaman server for execution" + +#define PARAM_OUT "out" +#define HELP_OUT "<t> use display method t for cell values of result MDDs where t is one of none, file, formatted, string, hex. Implies --content" +#define DEFAULT_OUT OUT_NONE +#define PARAM_OUT_FILE "file" +#define PARAM_OUT_STRING "string" +#define PARAM_OUT_HEX "hex" +#define PARAM_OUT_FORMATTED "formatted" +#define PARAM_OUT_NONE "none" +#define DEFAULT_OUT_STR PARAM_OUT_NONE + +#define PARAM_CONTENT "content" +#define HELP_CONTENT "display result, if any (see also --out and --type for output formatting)" + +#define PARAM_TYPE "type" +#define HELP_TYPE "display type information for results" + +#define PARAM_OUTFILE_FLAG 'o' +#define PARAM_OUTFILE "outfile" +#define HELP_OUTFILE "<of> file name template for storing result images (ignored for scalar results). Use '%d' to indicate auto numbering position, like with printf(1). For well-known file types, a proper suffix is appended to the resulting file name. Implies --out file." +#define DEFAULT_OUTFILE "rasql_%d" + +#define PARAM_QUIET "quiet" +#define HELP_QUIET "print no ornament messages, only results and errors" + +#define PARAM_DEBUG "debug" +#define HELP_DEBUG "generate diagnostic output" + + +char globalConnectId[255] = {0}; +ServerComm *server; + +// global variables and default settings +// ------------------------------------- + +bool dbIsOpen = false; +bool taIsOpen = false; + +DatabaseIf database; +TransactionIf ta; + +// suppress regular messages in log? (cmd line parameter '--quiet') +bool quietLog = false; +// logging mechanism that respects 'quiet' flag: +#define LOG(a) { if (!quietLog) std::cout << a; } + +int optionValueIndex=0; + +const char *serverName = DEFAULT_SERV; +r_ULong serverPort = DEFAULT_PORT; +const char *baseName = DEFAULT_DB; + +const char *user = DEFAULT_USER; +const char *passwd = DEFAULT_PASSWD; + +const char *fileName = NULL; +const char *queryString=NULL; + +bool output = false; +bool displayType = false; + +OUTPUT_TYPE outputType = DEFAULT_OUT; + +const char *outFileMask = DEFAULT_OUTFILE; + +r_Minterval mddDomain; +bool mddDomainDef = false; + +const char* mddTypeName = NULL; +bool mddTypeNameDef = false; + +// query result set. +// we define it here because on empty results the set seems to be corrupt which kills the default destructor +r_Set< r_Ref_Any > result_set; + +// end of globals + +void +parseParams(int argc, char** argv) throw (RasqlError, r_Error) +{ + CommandLineParser &cmlInter = CommandLineParser::getInstance(); + + CommandLineParameter &clp_help = cmlInter.addFlagParameter( PARAM_HELP_FLAG, PARAM_HELP, HELP_HELP ); + + CommandLineParameter &clp_query = cmlInter.addStringParameter(PARAM_QUERY_FLAG, PARAM_QUERY, HELP_QUERY ); + CommandLineParameter &clp_file = cmlInter.addStringParameter(PARAM_FILE_FLAG, PARAM_FILE, HELP_FILE ); + + CommandLineParameter &clp_content = cmlInter.addFlagParameter( CommandLineParser::noShortName, PARAM_CONTENT, HELP_CONTENT ); + CommandLineParameter &clp_out = cmlInter.addStringParameter( CommandLineParser::noShortName, PARAM_OUT, HELP_OUT, DEFAULT_OUT_STR ); + CommandLineParameter &clp_outfile = cmlInter.addStringParameter( CommandLineParser::noShortName, PARAM_OUTFILE, HELP_OUTFILE, DEFAULT_OUTFILE ); + CommandLineParameter &clp_mddDomain = cmlInter.addStringParameter( CommandLineParser::noShortName, PARAM_DOMAIN, HELP_DOMAIN ); + CommandLineParameter &clp_mddType = cmlInter.addStringParameter( CommandLineParser::noShortName, PARAM_MDDTYPE, HELP_MDDTYPE, DEFAULT_MDDTYPE ); + CommandLineParameter &clp_type = cmlInter.addFlagParameter( CommandLineParser::noShortName, PARAM_TYPE, HELP_TYPE ); + + CommandLineParameter &clp_server = cmlInter.addStringParameter( PARAM_SERV_FLAG, PARAM_SERV, HELP_SERV, DEFAULT_SERV ); + CommandLineParameter &clp_port = cmlInter.addStringParameter( PARAM_PORT_FLAG, PARAM_PORT, HELP_PORT, DEFAULT_PORT_STR); + CommandLineParameter &clp_database = cmlInter.addStringParameter( PARAM_DB_FLAG, PARAM_DB, HELP_DB, DEFAULT_DB ); + CommandLineParameter &clp_user = cmlInter.addStringParameter(CommandLineParser::noShortName, PARAM_USER, HELP_USER, DEFAULT_USER ); + CommandLineParameter &clp_passwd = cmlInter.addStringParameter(CommandLineParser::noShortName, PARAM_PASSWD, HELP_PASSWD, DEFAULT_PASSWD ); + CommandLineParameter &clp_quiet = cmlInter.addFlagParameter(CommandLineParser::noShortName, PARAM_QUIET, HELP_QUIET ); + +#ifdef DEBUG + CommandLineParameter &clp_debug = cmlInter.addFlagParameter( CommandLineParser::noShortName, PARAM_DEBUG, HELP_DEBUG ); +#endif + + try + { + cmlInter.processCommandLine(argc, argv); + + if (cmlInter.isPresent( PARAM_HELP_FLAG ) || argc == 1) + { + cout << "usage: " << argv[0] << " [--query querystring|-q querystring] [options]" << endl; + cout << "options:" << endl; + cmlInter.printHelp(); + exit( EXIT_USAGE ); // FIXME: exit no good style!! + } + + // check mandatory parameters ==================================================== + + // evaluate mandatory parameter collection -------------------------------------- + if (cmlInter.isPresent( PARAM_QUERY )) + queryString = cmlInter.getValueAsString( PARAM_QUERY ); + else + throw RasqlError( NOQUERY ); + + // check optional parameters ==================================================== + + // evaluate optional parameter file -------------------------------------- + if (cmlInter.isPresent( PARAM_FILE )) + fileName = cmlInter.getValueAsString( PARAM_FILE ); + + // evaluate optional parameter server -------------------------------------- + if (cmlInter.isPresent( PARAM_SERV )) + serverName = cmlInter.getValueAsString( PARAM_SERV ); + + // evaluate optional parameter port -------------------------------------- + if (cmlInter.isPresent( PARAM_PORT )) + serverPort = cmlInter.getValueAsLong( PARAM_PORT ); + + // evaluate optional parameter database -------------------------------------- + if (cmlInter.isPresent( PARAM_DB )) + baseName = cmlInter.getValueAsString( PARAM_DB ); + + // evaluate optional parameter user -------------------------------------- + if (cmlInter.isPresent( PARAM_USER )) + user = cmlInter.getValueAsString( PARAM_USER ); + + // evaluate optional parameter passwd -------------------------------------- + if (cmlInter.isPresent( PARAM_PASSWD )) + passwd = cmlInter.getValueAsString( PARAM_PASSWD ); + + // evaluate optional parameter content -------------------------------------- + if (cmlInter.isPresent( PARAM_CONTENT )) + output = true; + + // evaluate optional parameter type -------------------------------------- + if (cmlInter.isPresent( PARAM_TYPE )) + displayType = true; + + // evaluate optional parameter hex -------------------------------------- + if (cmlInter.isPresent( PARAM_OUT )) + { + output = true; + const char* val = cmlInter.getValueAsString( PARAM_OUT ); + if (val !=0 && strcmp( val, PARAM_OUT_STRING ) == 0) + outputType = OUT_STRING; + else if (val !=0 && strcmp( val, PARAM_OUT_FILE ) == 0) + outputType = OUT_FILE; + else if (val !=0 && strcmp( val, PARAM_OUT_FORMATTED ) == 0) + outputType = OUT_FORMATTED; + else if (val !=0 && strcmp( val, PARAM_OUT_HEX ) == 0) + outputType = OUT_HEX; + else if (val !=0 && strcmp( val, PARAM_OUT_NONE ) == 0) + outputType = OUT_NONE; + else + throw RasqlError( ILLEGALOUTPUTTYPE ); + } + + // evaluate optional parameter outfile -------------------------------------- + if (cmlInter.isPresent( PARAM_OUTFILE )) + { + outFileMask = cmlInter.getValueAsString( PARAM_OUTFILE ); + outputType = OUT_FILE; + } + + // evaluate optional parameter domain -------------------------------------- + if ( cmlInter.isPresent( PARAM_DOMAIN ) ) + { + try + { + mddDomain = r_Minterval(cmlInter.getValueAsString( PARAM_DOMAIN )); + mddDomainDef = true; + } + catch ( r_Error& e ) // Minterval constructor had syntax problems + { + throw RasqlError( NOVALIDDOMAIN ); + } + } + + // evaluate optional parameter MDD type name -------------------------------------- + if (cmlInter.isPresent( PARAM_MDDTYPE )) + { + mddTypeName = cmlInter.getValueAsString( PARAM_MDDTYPE ); + mddTypeNameDef = true; + } + + // evaluate optional parameter 'quiet' -------------------------------------------- + if (cmlInter.isPresent( PARAM_QUIET )) + { + quietLog = true; + } + +#ifdef DEBUG + // evaluate optional parameter MDD type name -------------------------------------- + SET_OUTPUT( cmlInter.isPresent( PARAM_DEBUG ) ); +#endif + + } + catch(CmlException& err) + { + cerr << err.what() << endl; + throw RasqlError( ERRORPARSINGCOMMANDLINE ); + } +} // parseParams() + + +void +openDatabase() throw (r_Error) +{ + ENTER( "openDatabase" ); + + if (! dbIsOpen) + { + LOG( "opening database " << baseName << " at " << serverName << ":" << serverPort << "..." << flush ); + // + sprintf(globalConnectId, "tcp:postgresql://%s:%d/%s", serverName, serverPort, baseName); + //strcpy(globalConnectId, "tcp:postgresql://$serverName:$serverPort/$baseName"); + printf("conn = %s\n", globalConnectId); + + server = new ServerComm(300, 120, 7013, "rasmgr", 7001, "N1"); + + + AdminIf* myAdmin = AdminIf::instance(); + database.open( "RASSERVICE"); + + //ta.begin( &database ); + + ServerComm::ClientTblElt *r = new ServerComm::ClientTblElt("testclient", 2); + server->addClientTblEntry (r); + accessControl.setServerName("NT1"); + accessControl.crunchCapability("$I1$ER.$BRASBASE$T1:3:2008:23:39:24$NNT1$D983893f406445a922cba0301bc5a85ec$K"); + server->openDB(2, "RASBASE", "costea"); + SET_OUTPUT(TRUE); + + TALK( "ok" ); + dbIsOpen = true; + LOG( "ok" << endl << flush ); + } + + LEAVE( "openDatabase" ); +} // openDatabase() + +void +closeDatabase() throw (r_Error) +{ + ENTER( "closeDatabase" ); + + if (dbIsOpen) + { + TALK( "database was open, closing it" ); + dbIsOpen = false; + } + + LEAVE( "closeDatabase" ); + return; +} // closeDatabase() + +void +openTransaction(bool readwrite) throw (r_Error) +{ + ENTER( "openTransaction, readwrite=" << (readwrite ? "rw" : "ro" ) ); + + if (! taIsOpen) + { + if (readwrite) + { + TALK( "transaction was closed, opening rw..." ); + ta.begin(&database); + TALK( "ok" ); + } + else + { + TALK( "transaction was closed, opening ro..." ); + ta.begin(&database); + TALK( "ok" ); + } + + taIsOpen = true; + } + + LEAVE( "openTransaction" ); +} // openTransaction() + +void +closeTransaction( bool doCommit ) throw (r_Error) +{ + ENTER( "closeTransaction, doCommit=" << doCommit ); + + if (taIsOpen) + { + if (doCommit) + { + TALK( "transaction was open, committing it..." ); + ta.commit(); + TALK( "ok" ); + } + else + { + TALK( "transaction was open, aborting it..." ); + ta.abort(); + TALK( "ok" ); + } + taIsOpen = false; + } + + LEAVE( "closeTransaction" ); + return; +} // closeTransaction() + +void printScalar( const r_Scalar& scalar ) +{ + ENTER( "printScalar" ); + + switch( scalar.get_type()->type_id() ) + { + case r_Type::BOOL: + LOG( ( ((r_Primitive*)&scalar)->get_boolean() ? "t" : "f" ) << flush ); + break; + + case r_Type::CHAR: + LOG( (int)((r_Primitive*)&scalar)->get_char() << flush ); + break; + + case r_Type::OCTET: + LOG( (int)((r_Primitive*)&scalar)->get_octet() << flush ); + break; + + case r_Type::SHORT: + LOG( ((r_Primitive*)&scalar)->get_short() << flush ); + break; + + case r_Type::USHORT: + LOG( ((r_Primitive*)&scalar)->get_ushort() << flush ); + break; + + case r_Type::LONG: + LOG( ((r_Primitive*)&scalar)->get_long() << flush ); + break; + + case r_Type::ULONG: + LOG( ((r_Primitive*)&scalar)->get_ulong() << flush ); + break; + + case r_Type::FLOAT: + LOG( ((r_Primitive*)&scalar)->get_float() << flush ); + break; + + case r_Type::DOUBLE: + LOG( ((r_Primitive*)&scalar)->get_double() << flush ); + break; + + case r_Type::COMPLEXTYPE1: + case r_Type::COMPLEXTYPE2: + LOG( "(" << ((r_Complex*)&scalar)->get_re() << "," << ((r_Complex*)&scalar)->get_im() << ")" << flush ); + break; + + case r_Type::STRUCTURETYPE: + { + r_Structure* structValue = (r_Structure*)&scalar; + LOG( "{ " << flush ); + for( int i=0; i<structValue->count_elements(); i++ ) + { + printScalar( (*structValue)[i] ); + if( i < structValue->count_elements()-1 ) + LOG( ", " << flush ); + } + LOG( " }" << endl ); + } + break; + default: + LOG( "scalar type " << scalar.get_type()->type_id() << " not supported!" << endl ); + break; + } + LEAVE( "printScalar" ); +} // printScalar() + + +// result_set should be parameter, but is global -- see def for reason +void printResult( r_Minterval &mddDomain, char* &typeName, + char* &typeStructure, + r_OId &oid, + unsigned short ¤tFormat) +{ + ENTER( "printResult" ); + + if (displayType) + { + cout << " Oid...................: " << oid << endl; + cout << " Type Structure........: " + << ( typeStructure ? typeStructure : "<nn>" ) << endl; + cout << endl; + } + + /* The following can be used if the type is known and the element type is not atomic. + + r_Set< r_Ref< r_Point > >* set2 = (r_Set< r_Ref< r_Point > >*)&result_set; + r_Iterator< r_Ref<r_Point> > iter2 = set2->create_iterator(); + for( iter2.reset(); iter2.not_done(); iter2++ ) + cout << **iter2 << endl; + */ + + r_Iterator< r_Ref_Any > iter = result_set.create_iterator(); + // iter.not_done() seems to behave wrongly on empty set, therefore this additional check -- PB 2003-aug-16 + for ( int i=1 ; i<=result_set.cardinality() && iter.not_done(); iter++, i++ ) + { + switch( result_set.get_element_type_schema()->type_id() ) + { + case r_Type::MARRAYTYPE: + switch ( outputType ) + { + case OUT_NONE: + break; + case OUT_STRING: + { + int numCells = r_Ref<r_GMarray>(*iter)->get_array_size(); + const char* theStuff = r_Ref<r_GMarray>(*iter)->get_array(); + LOG( " Result object " << i << ": " ); + for (int cnt = 0; cnt < numCells; cnt++) + cout << theStuff[cnt]; + cout << endl; + } + break; + case OUT_HEX: + { + int numCells = r_Ref<r_GMarray>(*iter)->get_array_size(); + const char* theStuff = r_Ref<r_GMarray>(*iter)->get_array(); + LOG( " Result object " << i << ": " ); + cout << hex; + for (int cnt = 0; cnt < numCells; cnt++) + cout << setw(2) << (unsigned short) (0xff & theStuff[cnt]) << " "; + cout << dec << endl; + } + break; + case OUT_FORMATTED: + LOG( " Result object " << i << ":" << endl ); + // for (int cnt = 0; cnt < numCells; cnt++) + printScalar( *(r_Ref<r_Scalar>(*iter)) ); + cout << endl; + break; + case OUT_FILE: + { + char defFileName[FILENAME_MAX]; + (void) snprintf( defFileName, sizeof(defFileName)-1, outFileMask, i ); + TALK( "filename for #" << i << " is " << defFileName ); + + // special treatment only for DEFs + r_Data_Format mafmt = r_Ref<r_GMarray>(*iter)->get_current_format(); + switch (mafmt) + { + case r_TIFF: + strcat( defFileName, ".tif" ); break; + case r_JPEG: + strcat( defFileName, ".jpg" ); break; + case r_HDF: + strcat( defFileName, ".hdf" ); break; + case r_PNG: + strcat( defFileName, ".png" ); break; + case r_BMP: + strcat( defFileName, ".bmp" ); break; + case r_VFF: + strcat( defFileName, ".vff" ); break; + default: + strcat( defFileName, ".unknown" ); break; + break; + } + + LOG( " Result object " << i << ": going into file " << defFileName << "..." << flush ); + FILE *tfile = fopen( defFileName, "wb" ); + fwrite((void*)r_Ref<r_GMarray>(*iter)->get_array(), 1, r_Ref<r_GMarray>(*iter)->get_array_size(), tfile ); + fclose(tfile); + LOG( "ok." << endl ); + } + break; + default: + cerr << "Internal error: unknown output type, ignoring action: " << outputType << endl; + break; + } // switch(outputType) + break; + + case r_Type::POINTTYPE: + LOG( " Result element " << i << ": " ); + cout << *(r_Ref<r_Point>(*iter)) << endl; + break; + + case r_Type::SINTERVALTYPE: + LOG( " Result element " << i << ": " ); + cout << *(r_Ref<r_Sinterval>(*iter)) << endl; + break; + + case r_Type::MINTERVALTYPE: + LOG( " Result element " << i << ": " ); + cout << *(r_Ref<r_Minterval>(*iter)) << endl; + break; + + case r_Type::OIDTYPE: + LOG( " Result element " << i << ": " ); + cout << *(r_Ref<r_OId>(*iter)) << endl; + break; + + default: + LOG( " Result element " << i << ": " << flush ); + printScalar( *(r_Ref<r_Scalar>(*iter)) ); + cout << endl; + // or simply + // r_Ref<r_Scalar>(*iter)->print_status( cout ); + } // switch + } // for(...) + + LEAVE( "printResult" ); +} // printResult() + + +/* + * get database type structure from type name + * returns ptr if an MDD type with the given name exists in the database, NULL otherwise + * throws r_Error upon general database comm error + * needs an open transaction + */ +r_Marray_Type * getTypeFromDatabase( const char *mddTypeName ) throw(RasqlError, r_Error) +{ + ENTER( "getTypeFromDatabase, mddTypeName=" << mddTypeName ); + r_Marray_Type *retval = NULL; + char* typeStructure = NULL; + + // first, try to get type structure from database using a separate r/o transaction + try + { + server->getTypeStructure(2, mddTypeName, ClientComm::r_MDDType_Type, typeStructure); + TALK( "type structure is " << typeStructure ); + } + catch (r_Error& err) + { + if (err.get_kind() == r_Error::r_Error_DatabaseClassUndefined) + { + TALK( "Type is not a well known type: " << typeStructure ); + typeStructure = new char[strlen(mddTypeName) + 1]; + // earlier code tried this one below, but I feel we better are strict -- PB 2003-jul-06 + // strcpy(typeStructure, mddTypeName); + // TALK( "using instead: " << typeStructure ); + throw RasqlError( MDDTYPEINVALID ); + } + else // unanticipated error + { + TALK( "Error during type retrieval from database: " << err.get_errorno() << " " << err.what() ); + throw; + } + } + + // next, find out whether it is an MDD type (and not a base or set type, eg) + try + { + r_Type* tempType = r_Type::get_any_type(typeStructure); + TALK( "get_any_type() for this type returns: " << tempType ); + if (tempType->isMarrayType()) + { + retval = (r_Marray_Type*)tempType; + tempType = NULL; + TALK( "found MDD type: " << retval ); + } + else + { + TALK( "type is not an marray type: " << typeStructure ); + delete tempType; + tempType = NULL; + retval = NULL; + throw RasqlError( MDDTYPEINVALID ); + } + } + catch (r_Error& err) + { + TALK( "Error during retrieval of MDD type structure (" << typeStructure << "): " << err.get_errorno() << " " << err.what() ); + throw; + } + + delete [] typeStructure; + typeStructure = NULL; + + LEAVE( "getTypeFromDatabase, retval=" << retval ); + return retval; +} // getTypeFromDatabase() + +void doStuff( int argc, char** argv ) throw (r_Error) +{ + char *fileContents = NULL; // contents of file satisfying "$1" parameter in query + r_Ref<r_GMarray> fileMDD = NULL; // MDD to satisfy a "$1" parameter + r_Marray_Type *mddType = NULL; // this MDD's type + + ENTER( "doStuff" ); + + r_OQL_Query query( queryString ); + TALK( "query is: " << query.get_query() ); + + if ( fileName != NULL ) + { + openTransaction( false ); + + // if no type name was specified then assume byte string (for encoded files) + if ( ! mddTypeNameDef ) + mddTypeName = MDD_STRINGTYPE; + + LOG( "fetching type information for " << mddTypeName << " from database, using readonly transaction..." << flush ); + mddType = getTypeFromDatabase( mddTypeName ); + closeTransaction( true ); + LOG( "ok" << endl ); + + LOG( "reading file " << fileName << "..." << flush ); + FILE* fileD = fopen( fileName, "r" ); + if (fileD == NULL) + throw RasqlError( FILEINACCESSIBLE ); + + fseek( fileD, 0, SEEK_END ); + int size = ftell( fileD ); + TALK( "file size is " << size << " bytes" ); + try + { + fileContents = new char[size]; + } + catch(std::bad_alloc) + { + TALK( "Unable to claim memory: " << size << " Bytes" ); + throw RasqlError( UNABLETOCLAIMRESOURCEFORFILE ); + } + + fseek( fileD, 0, SEEK_SET ); + fread( fileContents, 1, size, fileD ); + fclose( fileD ); + + // if no domain specified (this is the case with encoded files), then set to byte stream: + if ( ! mddDomainDef ) + { + mddDomain = r_Minterval( 1 ) << r_Sinterval ( 0, size-1 ); + TALK( "domain set to " << mddDomain ); + } + + if (size != mddDomain.cell_count() * mddType->base_type().size()) + throw RasqlError( FILESIZEMISMATCH ); + LOG( "ok" << endl ); + + TALK( "setting up MDD with domain " << mddDomain << " and base type " << mddTypeName ); + fileMDD = new (mddTypeName) r_GMarray( mddDomain, mddType->base_type().size() ); + fileMDD->set_type_schema( mddType ); + fileMDD->set_array_size( mddDomain.cell_count() * mddType->base_type().size() ); + fileMDD->set_array( fileContents ); + + query << *fileMDD; + + TALK( "constants are:" ); + r_Set<r_GMarray *> * myConstSet = (r_Set<r_GMarray *> *) query.get_constants(); + r_Iterator< r_GMarray *> iter = myConstSet->create_iterator(); + int i; + for ( i=1, iter.reset(); iter.not_done(); iter++, i++ ) + { + r_Ref< r_GMarray > myConstant = *iter; + LOG( " constant " << i << ": " ); + myConstant->print_status( cout ); +// the following can be used for sporadic debugging of input files, but beware: is very verbose! +#if 0 + cout << " Contents: " << hex; + const char *a = myConstant->get_array(); + for (int m=0; m < myConstant->get_array_size(); m++) + cout << (unsigned short) (a[m] & 0xFF) << " "; + cout << dec << endl; +#endif + } + } + + ExecuteQueryRes result; + + if( query.is_update_query() ) + { + openTransaction( true ); + + r_Marray<r_ULong>* mddConst = NULL; + + LOG( "Executing update query..." << flush ); + server->executeQuery(2, queryString, result ); + LOG( "ok" << endl ); + + if( mddConst ) + delete mddConst; + + closeTransaction( true ); + } + else + { + openTransaction( false ); + + // should be defined here, but is global; see def for reason + // r_Set< r_Ref_Any > result_set; + + LOG( "Executing retrieval query..." << flush ); + int status; + status = server->executeQuery(2, queryString, result ); + switch (status) + { + case 0: LOG("holds MDD elements" << endl); + break; + case 1: LOG("holds non-MDD elements" << endl); + break; + case 2: LOG("holds no elements" << endl); + break; + }; + + r_Minterval mddDomain; + char* typeName; + char* typeStructure; + r_OId oid; + unsigned short currentFormat; + + LOG( "Getting result..." << flush ); + if( output ) { + while (true) { + LOG( "Getting mdd..." << endl << flush ); + status = server->getNextMDD(2, mddDomain, typeName, typeStructure, oid, currentFormat); + if (status==2) { + printf("Empty MDD\n"); + break; + } + + } + } + //result_set = result; + LOG( "ok" << endl ); + + closeTransaction( true ); + } + + if (fileContents != NULL) + delete [] fileContents; + + LEAVE( "doStuff" ); +} + +/* + * returns 0 on success, -1 on error + */ +int main(int argc, char** argv) +{ + SET_OUTPUT( true ); // inhibit unconditional debug output, await cmd line evaluation + + int retval = EXIT_SUCCESS; // overall result status + + try + { + parseParams( argc, argv ); + + // put LOG after parsing parameters to respect a '--quiet' + LOG( argv[0] << ": rasdaman query tool v1.0, rasdaman v" << RMANVERSION/1000 << " -- generated on " << COMPDATE << "." << endl ); + + openDatabase(); + doStuff( argc, argv ); + closeDatabase(); + retval = EXIT_SUCCESS; + } + catch (RasqlError& e) + { + cerr << argv[0] << ": " << e.what() << endl; + retval = EXIT_FAILURE; + } + catch (const r_Error& e) + { + cerr << "rasdaman error " << e.get_errorno() << ": " << e.what() << endl; + retval = EXIT_FAILURE; + } + catch (...) + { + cerr << argv[0] << ": panic: unexpected internal exception." << endl; + retval = EXIT_FAILURE; + } + + if (retval != EXIT_SUCCESS && (dbIsOpen || taIsOpen) ) + { + LOG( "aborting transaction..." << flush ); + closeTransaction( false ); // abort transaction and close database, ignore any further exceptions + LOG( "ok" << endl ); + closeDatabase(); + } + + LOG( argv[0] << " done." << endl ); + return retval; +} // main() + +// end of rasql.cc + diff --git a/applications/directql/directql_error.cc b/applications/directql/directql_error.cc new file mode 100644 index 0000000..cba8c5e --- /dev/null +++ b/applications/directql/directql_error.cc @@ -0,0 +1,119 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** +* SOURCE: rasql_error.cc +* +* CLASS: RasqlError +* +* COMMENTS: +* No comments +*/ + + +using namespace std; + +static const char rcsid[] = "@(#)raslib, RasqlError: $Id: rasql_error.cc,v 1.1 2003/12/27 19:30:23 rasdev Exp $"; + +#include <exception> +#include <string> + +// for sprintf(): +#include <stdio.h> + +#include "directql_error.hh" + +// debug facility; relies on -DDEBUG at compile time +#include "debug-clt.hh" + +/// error object, carrying int error code +RasqlError::RasqlError( unsigned int e ) +{ + TALK( "Exception: " << e ); + errno = e; +} + +/// default destructor +RasqlError::~RasqlError() +{ +} + +/// print error message (including error code) +/// NB: not all messages can occur +const char* +RasqlError::what() +{ + char *errorMsg; + switch (errno) + { + case NOQUERY: + errorMsg = "Mandatory parameter '--query' missing."; + break; + case ERRORPARSINGCOMMANDLINE: + errorMsg = "Command line syntax error."; + break; + case ILLEGALOUTPUTTYPE: + errorMsg = "Illegal output type specifier, must be one of none, file, formatted, string, hex."; + break; + case FILEINACCESSIBLE: + errorMsg = "Cannot read input file."; + break; + case UNABLETOCLAIMRESOURCEFORFILE: + errorMsg = "Cannot allocate memory for file read."; + break; + case NOVALIDDOMAIN: + errorMsg = "Syntax error in mdddomain specification, must be [x0:x1,y0:y1] (forgot to quote or escape?)"; + break; + case MDDTYPEINVALID: + errorMsg = "MDD type invalid."; + break; + case FILESIZEMISMATCH: + errorMsg = "Input file size does not correspond with MDD domain specified."; + break; + default : + errorMsg = "Unknown error code."; + break; + case ALLDONE: + case 0: + errorMsg = "No errors."; + } + +// size of error text buffer below +#define ERRTEXT_BUFSIZ 200 + + static char errorText[ERRTEXT_BUFSIZ]; + +// text constants for error msg +#define MODULE_TAG "IO" +#define ERROR_TEXT " Error: " + + // check for buffer overflow + if (strlen(MODULE_TAG) + 3 + strlen(ERROR_TEXT) + strlen(errorMsg) + 1 > ERRTEXT_BUFSIZ) + sprintf( errorText, "%s%03d%s", MODULE_TAG, errno, "(error message too long, cannot display)" ); + else + sprintf( errorText, "%s%03d%s%s", MODULE_TAG, errno, ERROR_TEXT, errorMsg ); + + return errorText; +} // what() + + diff --git a/applications/directql/directql_error.hh b/applications/directql/directql_error.hh new file mode 100644 index 0000000..99d7c97 --- /dev/null +++ b/applications/directql/directql_error.hh @@ -0,0 +1,76 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * + * COMMENTS: + * + * No Comments +*/ + +#ifndef _RASQL_ERROR_HH_ +#define _RASQL_ERROR_HH_ + +#ifdef __VISUALC__ +#pragma warning( disable : 4290 ) +#endif + +//@ManMemo: Module: {\bf raslib} + +/*@Doc: + + This class ... +*/ + + + /// valid error codes: +#define ALLDONE -1 +#define OK 0 +#define NOQUERY 1 +#define ERRORPARSINGCOMMANDLINE 2 +#define ILLEGALOUTPUTTYPE 3 +#define FILEINACCESSIBLE 4 +#define UNABLETOCLAIMRESOURCEFORFILE 5 +#define NOVALIDDOMAIN 6 +#define MDDTYPEINVALID 7 +#define FILESIZEMISMATCH 8 + +class RasqlError // : public std::exception +{ + public: + + /// constructor receiving an error number + RasqlError( unsigned int e ); + + /// destructor + virtual ~RasqlError(); + + /// get an error description + virtual const char * what(); + + private: + /// error information + unsigned int errno; +}; + +#endif // _RASQL_ERROR_HH_ diff --git a/applications/directql/directql_signal.cc b/applications/directql/directql_signal.cc new file mode 100644 index 0000000..3ccdb97 --- /dev/null +++ b/applications/directql/directql_signal.cc @@ -0,0 +1,246 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ + +/* * + * SOURCE: rasql_globals.cc + * + * MODULE: applications/rasql + * + * PURPOSE: + * provide signal handling + * + * COMMENTS: + * + * No comments +*/ + +static const char rcsid[] = "@(#)rasodmg/test,ImportOrthoUtil: $Id: rasql_signal.cc,v 1.1 2003/12/27 19:30:23 rasdev Exp $"; + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#include "raslib/template_inst.hh" +#endif + +#include <iostream> +#include <string> +#include <iostream> +#include <signal.h> +#ifdef SOLARIS +#include <strings.h> +#endif + +#include "directql_error.hh" +#include "directql_signal.hh" + +// debug facility; relies on -DDEBUG at compile time +#include "debug-clt.hh" + +//signalCleanup function is called when a signal is received by the program. +//You should write your function in order to have signal management +void signalCleanup(); + +//signalHandler function is called when a signal occurs +void +signalHandler(int sig); + +//installSignalHandlers function should be called first in main function +//in order to receive a signal in your program +void +signalHandler(int sig) +{ + static bool handleSignal = true; // sema to prevent nested signals + + cout << "Caught signal " << sig << ": "; + switch (sig) + { + case SIGHUP: + cout << "Hangup (POSIX). "; + break; + case SIGINT: + cout << "Interrupt (ANSI)."; + break; + case SIGQUIT: + cout << "Quit (POSIX)."; + break; + case SIGILL: + cout << "Illegal instruction (ANSI)."; + break; + case SIGTRAP: + cout << "Trace trap (POSIX)."; + break; + case SIGABRT: + cout << "Abort (ANSI) or IOT trap (4.2 BSD)."; + break; + case SIGBUS: + cout << "BUS error (4.2 BSD)."; + break; + case SIGFPE: + cout << "Floating-point exception (ANSI)."; + break; + case SIGKILL: + cout << "Kill, unblockable (POSIX)."; + break; + case SIGUSR1: + cout << "User-defined signal 1 (POSIX)."; + break; + case SIGSEGV: + cout << "Segmentation violation (ANSI)."; + break; + case SIGUSR2: + cout << "User-defined signal 2 (POSIX)."; + break; + case SIGPIPE: + cout << "Broken pipe (POSIX)."; + break; + case SIGALRM: + cout << "Alarm clock (POSIX)."; + break; + case SIGTERM: + cout << "Termination (ANSI)."; + break; +#ifndef SOLARIS +#ifndef DECALPHA + case SIGSTKFLT: + cout << "Stack fault."; + break; +#endif +#endif + case SIGCLD: + cout << "SIGCHLD (System V) or child status has changed (POSIX)."; + break; + case SIGCONT: + cout << "Continue (POSIX)."; + break; + case SIGSTOP: + cout << "Stop, unblockable (POSIX)."; + break; + case SIGTSTP: + cout << "Keyboard stop (POSIX). Continuing operation."; + break; + case SIGTTIN: + cout << "Background read from tty (POSIX)."; + break; + case SIGTTOU: + cout << "Background write to tty (POSIX). Continuing operation"; + break; + case SIGURG: + cout << "Urgent condition on socket (4.2 BSD)."; + break; + case SIGXCPU: + cout << "CPU limit exceeded (4.2 BSD)."; + break; + case SIGXFSZ: + cout << "File size limit exceeded (4.2 BSD)."; + break; + case SIGVTALRM: + cout << "Virtual alarm clock (4.2 BSD)."; + break; + case SIGPROF: + cout << "Profiling alarm clock (4.2 BSD)."; + break; + case SIGWINCH: + cout << "Window size change (4.3 BSD, Sun). Continuing operation."; + break; + case SIGPOLL: + cout << "Pollable event occurred (System V) or I/O now possible (4.2 BSD)."; + break; + case SIGPWR: + cout << "Power failure restart (System V)."; + break; + case SIGSYS: + cout << "Bad system call."; + break; + default: + cout << "Unknown signal."; + break; + } + cout << endl << flush; + + // no repeated signals + if (handleSignal) + handleSignal = false; + + if (sig == SIGCONT || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU || sig == SIGWINCH) + return; + else + { + TALK( "fatal signal, exiting." << flush ); + exit(sig); + } +} + +void +installSignalHandlers() +{ + ENTER( "installSignalHandlers" ); + + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + signal(SIGHUP, signalHandler); + signal(SIGPIPE, signalHandler); + signal(SIGHUP, signalHandler); + signal(SIGINT, signalHandler); + signal(SIGQUIT, signalHandler); + signal(SIGILL, signalHandler); + signal(SIGTRAP, signalHandler); + signal(SIGABRT, signalHandler); + signal(SIGIOT, signalHandler); + signal(SIGBUS, signalHandler); + signal(SIGFPE, signalHandler); + signal(SIGKILL, signalHandler); + signal(SIGUSR1, signalHandler); + signal(SIGSEGV, signalHandler); + signal(SIGUSR2, signalHandler); + signal(SIGPIPE, signalHandler); + signal(SIGALRM, signalHandler); + signal(SIGTERM, signalHandler); +#ifndef SOLARIS +#ifndef DECALPHA + signal(SIGSTKFLT, signalHandler); +#endif +#endif + signal(SIGCLD, signalHandler); + signal(SIGCHLD, signalHandler); + signal(SIGCONT, signalHandler); + signal(SIGSTOP, signalHandler); + signal(SIGTSTP, signalHandler); + signal(SIGTTIN, signalHandler); + signal(SIGTTOU, signalHandler); + signal(SIGURG, signalHandler); + signal(SIGXCPU, signalHandler); + signal(SIGXFSZ, signalHandler); + signal(SIGVTALRM, signalHandler); + signal(SIGPROF, signalHandler); + signal(SIGWINCH, signalHandler); + signal(SIGPOLL, signalHandler); + signal(SIGIO, signalHandler); + signal(SIGPWR, signalHandler); + signal(SIGSYS, signalHandler); +#if !defined SOLARIS +#if !defined DECALPHA + signal(SIGUNUSED, signalHandler); +#endif +#endif + LEAVE( "installSignalHandlers" ); +} + diff --git a/applications/directql/directql_signal.hh b/applications/directql/directql_signal.hh new file mode 100644 index 0000000..360804e --- /dev/null +++ b/applications/directql/directql_signal.hh @@ -0,0 +1,50 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ + +/* * + * COMMENTS: + * + * No comments +*/ + +#ifndef _RASQL_SIGNAL_HH_ +#define _RASQL_SIGNAL_HH_ + +#include <signal.h> + +//signalCleanup function is called when a signal is received by the program. +//You should write your function in order to have signal management +void signalCleanup(); + +//signalHandler function is called when a signal occurs +void +signalHandler(int sig); + +//installSignalHandlers function should be called first in main function +//in order to receive signal in your program + +void +installSignalHandlers(); + +#endif _RASQL_SIGNAL_HH_ + diff --git a/applications/directql/template_inst.hh b/applications/directql/template_inst.hh new file mode 100644 index 0000000..bed46ab --- /dev/null +++ b/applications/directql/template_inst.hh @@ -0,0 +1,149 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +*/ + +/* * +* COMENTS +* +* No comments +*/ + +//for rb_tree, select1st +#include <function.h> +#include <tree.h> +#include <vector> +#include <utility> +#include <memory> + +#if(__GNUC__==2 &&__GNUC_MINOR__==95) +using std::rb_tree; +using std::select1st; +#else +using __gnu_cxx::rb_tree; +using __gnu_cxx::select1st; +#endif + +using std::vector; +using std::pair; + +// commented by Constantin Jucovschi (gcc 3.4+ no longer supports __default_alloc_template) +//using std::__default_alloc_template; +using std::fill_n; + +#include "qlparser/symtab.hh" + +#include "raslib/attribute.hh" +#include "raslib/itertype.hh" +#include "raslib/dlist.hh" + +#include "tile.hh" + +#include "indexmgr/keyobject.hh" + +#include "rasodmg/ref.hh" + +#include "reladminif/dbref.hh" +#include "reladminif/dbobjectiditerator.hh" + +#include "relblobif/blobtile.hh" +#include "relblobif/dbtile.hh" +#include "relblobif/inlinetile.hh" + +#include "relcatalogif/typeiterator.hh" +#include "relcatalogif/settype.hh" +#include "relcatalogif/structtype.hh" +#include "relcatalogif/mddtype.hh" +#include "relcatalogif/inlineminterval.hh" +#include "relcatalogif/dbminterval.hh" + +#include "relindexif/dbtcindex.hh" +#include "relindexif/hierindex.hh" +#include "relindexif/dbrcindexds.hh" + +#include "relmddif/dbmddobj.hh" +#include "relmddif/dbmddset.hh" + +#include "relstorageif/dbudfds.hh" +#include "relstorageif/dbudfpackageds.hh" +#include "relstorageif/dbstoragelayout.hh" + +template class SymbolTable<int>; + +//template class r_IterType<r_Attribute>; + +template class r_Ref<r_Scalar>; +template class r_Ref<r_OId>; + +template class DBRef<DBHierIndex>; +template class DBRef<DBRCIndexDS>; +template class DBRef<DBTCIndex>; +template class DBRef<BLOBTile>; +template class DBRef<DBTile>; +template class DBRef<InlineTile>; +template class DBRef<DBMDDSet>; +template class DBRef<DBMinterval>; +template class DBRef<DBStorageLayout>; +template class DBRef<DBUDFDS>; +template class DBRef<DBUDFPackageDS>; +//template class DBRef<DBMDDObj>; +// template bool operator< (const DBRef<DBMDDObj>&, const DBRef<DBMDDObj>&); + +//template TypeIterator<StructType>; +//template TypeIterator<SetType>; +template class TypeIterator<MDDType>; +template class DBRef<DBMDDObj>; +template class DBRef<DBObject>; + +template class DBObjectIdIterator<DBMDDObj>; +template class DBObjectIterator<DBMDDObj>; +template class DBObjectIterator<DBMDDSet>; +template class DBObjectIterator<StructType>; +template class DBObjectIterator<SetType>; +template class DBRef<StructType>; +template class DBRef<SetType>; +template class DBRef<MDDType>; + +template std::ostream& operator<< (const vector<KeyObject>&, std::ostream&); +template std::ostream& operator<< (std::ostream &, const vector<KeyObject>&); +//template std::ostream& operator << (std::ostream& os, const std::vector<double>& list); +//template std::ostream& operator << (std::ostream& os, const std::vector<r_Minterval>& list); + +template class rb_tree<OId, pair<OId const, DBMDDObj *>, select1st<pair<OId const, DBMDDObj *> >, less<OId> >; +template class rb_tree<OId, pair<OId const, DBMinterval *>, select1st<pair<OId const, DBMinterval *> >, less<OId> >; +template class rb_tree<OId, pair<OId const, DBRef<DBMDDObj> >, select1st<pair<OId const, DBRef<DBMDDObj> > >, less<OId> >; +template class rb_tree<OId, pair<OId const, DBMDDSet *>, select1st<pair<OId const, DBMDDSet *> >, less<OId> >; +template class rb_tree<OId, pair<OId const, MDDType *>, select1st<pair<OId const, MDDType *> >, less<OId> >; +template class rb_tree<OId, pair<OId const, SetType *>, select1st<pair<OId const, SetType *> >, less<OId> >; +template class rb_tree<OId, pair<OId const, StructType *>, select1st<pair<OId const, StructType *> >, less<OId> >; +template class rb_tree<long, pair<long const, BLOBTile *>, select1st<pair<long const, BLOBTile *> >, less<long> >; +template class rb_tree<long, pair<long const, InlineTile *>, select1st<pair<long const, InlineTile *> >, less<long> >; +template class vector<BaseType const * >; +template class vector<OId >; +template class vector<Tile * >; +template class vector<Type * >; +template class vector<char * >; +template class vector<char >; +template class vector<r_Data_Format >; +template class vector<unsigned int >; + +template class Tile ** fill_n<Tile **, unsigned int, Tile *>(Tile **, unsigned int, Tile * const &); + diff --git a/applications/rasql/Makefile.am b/applications/rasql/Makefile.am new file mode 100644 index 0000000..819816f --- /dev/null +++ b/applications/rasql/Makefile.am @@ -0,0 +1,70 @@ +# -*-Makefile-*- +# This file is part of rasdaman community. +# +# Rasdaman community 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 3 of the License, or +# (at your option) any later version. +# +# Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +# +# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +# rasdaman GmbH. +# +# For more information please see <http://www.rasdaman.org> +# or contact Peter Baumann via <baumann@rasdaman.com>. +# +# MAKEFILE FOR: +# rasql query utility +# +# +# COMMENTS: +# +################################################################## + +AM_CXXFLAGS=@CLIENTCXXFLAGS@ +AM_LDFLAGS=@CLIENTLDFLAGS@ + +bin_PROGRAMS=rasql +rasql_SOURCES= rasql.cc rasql_error.cc rasql_error.hh \ + rasql_signal.cc rasql_signal.hh +rasql_LDADD = ../../rasodmg/librasodmg.a ../../clientcomm/libclientcomm.a \ + ../../rnprotocol/libclientcomm.a\ + ../../compression/libcompression.a ../../raslib/libraslib.a \ + ../../conversion/libconversion.a ../../commline/libcommline.a \ + ../../network/libnetwork.a + +SUBDIRS= ../../rasodmg ../../clientcomm ../../rnprotocol ../../compression \ + ../../raslib ../../conversion ../../commline ../../network + +$(RECURSIVE_CLEAN_TARGETS): + @$(MAKE) $(AM_MAKEFLAGS) `echo $@ | sed s/-recursive/-am/` + +#rasql: rasql.o rasql_error.o rasql_signal.cc # $(LIBS) +# $(PURIFY) $(CXX) $(LDFLAGS) -o rasql $^ $(LIBS) $(IMGLIBS) -lm + +#LIBS = $(l_SYM)rasodmg $(l_SYM)clientcomm $(l_SYM)compression $(l_SYM)raslib \ +# $(l_SYM)conversion $(l_SYM)commline $(l_SYM)network + +#IMGLIBS = $(l_SYM)tiff $(l_SYM)jpeg $(l_SYM)png $(l_SYM)crypto $(l_SYM)z \ +# $(l_SYM)mfhdf $(l_SYM)df $(l_SYM)netpbm + +#LDFLAGS += $(L_SYM)$(SUPPORT_BASE)/lib + +# COMPDATE must be made more general at the final destination +# -- now done at central Makefile.inc, after integration into rasdaman +# COMPDATE = `date +\"%d.%m.%Y %H:%M:%S\"` +# CXXFLAGS += -DCOMPDATE="\"$(COMPDATE)\"" + +#SRCCXX= rasql.cc rasql_error.cc rasql_signal.cc + + + + + diff --git a/applications/rasql/rasql.cc b/applications/rasql/rasql.cc new file mode 100644 index 0000000..197e9fd --- /dev/null +++ b/applications/rasql/rasql.cc @@ -0,0 +1,946 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** +* rasql +* +* PURPOSE: +* Provides a command line interpretter for rasql queries, with +* options for displaying results or storing them to file(s) +* +* COMMENTS: +* +* BUGS: +* - query filename "" is interpreted as stdin +*/ + + +static const char rasql_rcsid[] = "@(#)rasql,rasql.cc: $Id: rasql.cc,v 1.3 2006/11/06 21:59:01 rasdev Exp $"; + +#ifndef RMANVERSION +#error "Please specify RMANVERSION variable!" +#endif + +#ifndef COMPDATE +#error "Please specify the COMPDATE variable!" +/* +COMPDATE=`date +"%d.%m.%Y %H:%M:%S"` +and -DCOMPDATE="\"$(COMPDATE)\"" when compiling +*/ +#endif + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#include "raslib/template_inst.hh" +#endif + +#ifdef __VISUALC__ +#include <strstrea.h> +#else +#include <strstream> +#endif + +#include <stdio.h> +#include <string.h> +#include <sstream> +#include <fstream> + +using namespace std; + +#ifdef __VISUALC__ + #define __EXECUTABLE__ +#endif + +#include "rasodmg/transaction.hh" +#include "rasodmg/database.hh" + +#include "rasodmg/ref.hh" +#include "raslib/marraytype.hh" +#include "rasodmg/set.hh" +#include "rasodmg/marray.hh" +#include "rasodmg/iterator.hh" +#include "rasodmg/oqlquery.hh" + +#include "raslib/type.hh" + +#include "raslib/minterval.hh" + +#include "raslib/primitive.hh" +#include "raslib/complex.hh" +#include "raslib/structure.hh" + +#include "raslib/structuretype.hh" +#include "raslib/primitivetype.hh" + +#include "../../commline/cmlparser.hh" + +#include "rasql_error.hh" + +#ifdef __VISUALC__ + #undef __EXECUTABLE__ +#endif + +// debug facility; relies on -DDEBUG at compile time +// tell debug that here is the place for the variables (to be done in the main() src file) +#define DEBUG_MAIN +#include "debug-clt.hh" + +const int MAX_STR_LEN = 255; +const int MAX_QUERY_LEN = 10240; + +// possible types of output +typedef enum +{ OUT_UNDEF, + OUT_FILE, + OUT_NONE, + OUT_STRING, + OUT_HEX, + OUT_FORMATTED +} OUTPUT_TYPE; + +// rasdaman MDD type for byte strings (default type used for file format reading) +#define MDD_STRINGTYPE "GreyString" + +#ifdef EXIT_FAILURE + #undef EXIT_FAILURE +#endif +/// program exit codes +#define EXIT_SUCCESS 0 +#define EXIT_USAGE 2 +#define EXIT_FAILURE -1 + + +// parameter names, defaults, and help texts + +#define PARAM_HELP_FLAG 'h' +#define PARAM_HELP "help" +#define HELP_HELP "show command line switches" + +#define PARAM_SERV_FLAG 's' +#define PARAM_SERV "server" +#define HELP_SERV "<host-name> rasdaman server" +#define DEFAULT_SERV "localhost" + +#define PARAM_PORT_FLAG 'p' +#define PARAM_PORT "port" +#define HELP_PORT "<p> rasmgr port number" +#define DEFAULT_PORT 7001 +#define DEFAULT_PORT_STR "7001" + +#define PARAM_DB_FLAG 'd' +#define PARAM_DB "database" +#define HELP_DB "<db-name> name of database" +#define DEFAULT_DB "RASBASE" + +#define PARAM_USER "user" +#define HELP_USER "<user-name> name of user" +#define DEFAULT_USER "rasguest" + +#define PARAM_PASSWD "passwd" +#define HELP_PASSWD "<user-passwd> password of user" +#define DEFAULT_PASSWD "rasguest" + +#define PARAM_FILE_FLAG 'f' +#define PARAM_FILE "file" +#define HELP_FILE "<f> file name for upload through $i parameters within queries; each $i needs its own file parameter, in proper sequence. Requires --mdddomain and --mddtype" + +#define PARAM_DOMAIN "mdddomain" +#define HELP_DOMAIN "<mdd-domain> domain of marray, format: \'[x0:x1,y0:y1]\' (required only if --file specified and file is in data format r_Array)" + +#define PARAM_MDDTYPE "mddtype" +// this is for display only; internally MDD_STRINGTYPE is used +#define DEFAULT_MDDTYPE "byte string" +#define HELP_MDDTYPE "<mdd-type> type of marray (required only if --file specified and file is in data format r_Array)" + +#define PARAM_QUERY_FLAG 'q' +#define PARAM_QUERY "query" +#define HELP_QUERY "<q> query string to be sent to the rasdaman server for execution" + +#define PARAM_OUT "out" +#define HELP_OUT "<t> use display method t for cell values of result MDDs where t is one of none, file, formatted, string, hex. Implies --content" +#define DEFAULT_OUT OUT_NONE +#define PARAM_OUT_FILE "file" +#define PARAM_OUT_STRING "string" +#define PARAM_OUT_HEX "hex" +#define PARAM_OUT_FORMATTED "formatted" +#define PARAM_OUT_NONE "none" +#define DEFAULT_OUT_STR PARAM_OUT_NONE + +#define PARAM_CONTENT "content" +#define HELP_CONTENT "display result, if any (see also --out and --type for output formatting)" + +#define PARAM_TYPE "type" +#define HELP_TYPE "display type information for results" + +#define PARAM_OUTFILE_FLAG 'o' +#define PARAM_OUTFILE "outfile" +#define HELP_OUTFILE "<of> file name template for storing result images (ignored for scalar results). Use '%d' to indicate auto numbering position, like with printf(1). For well-known file types, a proper suffix is appended to the resulting file name. Implies --out file." +#define DEFAULT_OUTFILE "rasql_%d" + +#define PARAM_QUIET "quiet" +#define HELP_QUIET "print no ornament messages, only results and errors" + +#define PARAM_DEBUG "debug" +#define HELP_DEBUG "generate diagnostic output" + + +// global variables and default settings +// ------------------------------------- + +r_Database db; +r_Transaction ta; + +bool dbIsOpen = false; +bool taIsOpen = false; + +// suppress regular messages in log? (cmd line parameter '--quiet') +bool quietLog = false; +// logging mechanism that respects 'quiet' flag: +#define LOG(a) { if (!quietLog) std::cout << a; } + +int optionValueIndex=0; + +const char *serverName = DEFAULT_SERV; +r_ULong serverPort = DEFAULT_PORT; +const char *baseName = DEFAULT_DB; + +const char *user = DEFAULT_USER; +const char *passwd = DEFAULT_PASSWD; + +const char *fileName = NULL; +const char *queryString=NULL; + +bool output = false; +bool displayType = false; + +OUTPUT_TYPE outputType = DEFAULT_OUT; + +const char *outFileMask = DEFAULT_OUTFILE; + +r_Minterval mddDomain; +bool mddDomainDef = false; + +const char* mddTypeName = NULL; +bool mddTypeNameDef = false; + +// query result set. +// we define it here because on empty results the set seems to be corrupt which kills the default destructor +r_Set< r_Ref_Any > result_set; + +// end of globals + +void +parseParams(int argc, char** argv) throw (RasqlError, r_Error) +{ + CommandLineParser &cmlInter = CommandLineParser::getInstance(); + + CommandLineParameter &clp_help = cmlInter.addFlagParameter( PARAM_HELP_FLAG, PARAM_HELP, HELP_HELP ); + + CommandLineParameter &clp_query = cmlInter.addStringParameter(PARAM_QUERY_FLAG, PARAM_QUERY, HELP_QUERY ); + CommandLineParameter &clp_file = cmlInter.addStringParameter(PARAM_FILE_FLAG, PARAM_FILE, HELP_FILE ); + + CommandLineParameter &clp_content = cmlInter.addFlagParameter( CommandLineParser::noShortName, PARAM_CONTENT, HELP_CONTENT ); + CommandLineParameter &clp_out = cmlInter.addStringParameter( CommandLineParser::noShortName, PARAM_OUT, HELP_OUT, DEFAULT_OUT_STR ); + CommandLineParameter &clp_outfile = cmlInter.addStringParameter( CommandLineParser::noShortName, PARAM_OUTFILE, HELP_OUTFILE, DEFAULT_OUTFILE ); + CommandLineParameter &clp_mddDomain = cmlInter.addStringParameter( CommandLineParser::noShortName, PARAM_DOMAIN, HELP_DOMAIN ); + CommandLineParameter &clp_mddType = cmlInter.addStringParameter( CommandLineParser::noShortName, PARAM_MDDTYPE, HELP_MDDTYPE, DEFAULT_MDDTYPE ); + CommandLineParameter &clp_type = cmlInter.addFlagParameter( CommandLineParser::noShortName, PARAM_TYPE, HELP_TYPE ); + + CommandLineParameter &clp_server = cmlInter.addStringParameter( PARAM_SERV_FLAG, PARAM_SERV, HELP_SERV, DEFAULT_SERV ); + CommandLineParameter &clp_port = cmlInter.addStringParameter( PARAM_PORT_FLAG, PARAM_PORT, HELP_PORT, DEFAULT_PORT_STR); + CommandLineParameter &clp_database = cmlInter.addStringParameter( PARAM_DB_FLAG, PARAM_DB, HELP_DB, DEFAULT_DB ); + CommandLineParameter &clp_user = cmlInter.addStringParameter(CommandLineParser::noShortName, PARAM_USER, HELP_USER, DEFAULT_USER ); + CommandLineParameter &clp_passwd = cmlInter.addStringParameter(CommandLineParser::noShortName, PARAM_PASSWD, HELP_PASSWD, DEFAULT_PASSWD ); + CommandLineParameter &clp_quiet = cmlInter.addFlagParameter(CommandLineParser::noShortName, PARAM_QUIET, HELP_QUIET ); + +#ifdef DEBUG + CommandLineParameter &clp_debug = cmlInter.addFlagParameter( CommandLineParser::noShortName, PARAM_DEBUG, HELP_DEBUG ); +#endif + + try + { + cmlInter.processCommandLine(argc, argv); + + if (cmlInter.isPresent( PARAM_HELP_FLAG ) || argc == 1) + { + cout << "usage: " << argv[0] << " [--query querystring|-q querystring] [options]" << endl; + cout << "options:" << endl; + cmlInter.printHelp(); + exit( EXIT_USAGE ); // FIXME: exit no good style!! + } + + // check mandatory parameters ==================================================== + + // evaluate mandatory parameter collection -------------------------------------- + if (cmlInter.isPresent( PARAM_QUERY )) + queryString = cmlInter.getValueAsString( PARAM_QUERY ); + else + throw RasqlError( NOQUERY ); + + // check optional parameters ==================================================== + + // evaluate optional parameter file -------------------------------------- + if (cmlInter.isPresent( PARAM_FILE )) + fileName = cmlInter.getValueAsString( PARAM_FILE ); + + // evaluate optional parameter server -------------------------------------- + if (cmlInter.isPresent( PARAM_SERV )) + serverName = cmlInter.getValueAsString( PARAM_SERV ); + + // evaluate optional parameter port -------------------------------------- + if (cmlInter.isPresent( PARAM_PORT )) + serverPort = cmlInter.getValueAsLong( PARAM_PORT ); + + // evaluate optional parameter database -------------------------------------- + if (cmlInter.isPresent( PARAM_DB )) + baseName = cmlInter.getValueAsString( PARAM_DB ); + + // evaluate optional parameter user -------------------------------------- + if (cmlInter.isPresent( PARAM_USER )) + user = cmlInter.getValueAsString( PARAM_USER ); + + // evaluate optional parameter passwd -------------------------------------- + if (cmlInter.isPresent( PARAM_PASSWD )) + passwd = cmlInter.getValueAsString( PARAM_PASSWD ); + + // evaluate optional parameter content -------------------------------------- + if (cmlInter.isPresent( PARAM_CONTENT )) + output = true; + + // evaluate optional parameter type -------------------------------------- + if (cmlInter.isPresent( PARAM_TYPE )) + displayType = true; + + // evaluate optional parameter hex -------------------------------------- + if (cmlInter.isPresent( PARAM_OUT )) + { + output = true; + const char* val = cmlInter.getValueAsString( PARAM_OUT ); + if (val !=0 && strcmp( val, PARAM_OUT_STRING ) == 0) + outputType = OUT_STRING; + else if (val !=0 && strcmp( val, PARAM_OUT_FILE ) == 0) + outputType = OUT_FILE; + else if (val !=0 && strcmp( val, PARAM_OUT_FORMATTED ) == 0) + outputType = OUT_FORMATTED; + else if (val !=0 && strcmp( val, PARAM_OUT_HEX ) == 0) + outputType = OUT_HEX; + else if (val !=0 && strcmp( val, PARAM_OUT_NONE ) == 0) + outputType = OUT_NONE; + else + throw RasqlError( ILLEGALOUTPUTTYPE ); + } + + // evaluate optional parameter outfile -------------------------------------- + if (cmlInter.isPresent( PARAM_OUTFILE )) + { + outFileMask = cmlInter.getValueAsString( PARAM_OUTFILE ); + outputType = OUT_FILE; + } + + // evaluate optional parameter domain -------------------------------------- + if ( cmlInter.isPresent( PARAM_DOMAIN ) ) + { + try + { + mddDomain = r_Minterval(cmlInter.getValueAsString( PARAM_DOMAIN )); + mddDomainDef = true; + } + catch ( r_Error& e ) // Minterval constructor had syntax problems + { + throw RasqlError( NOVALIDDOMAIN ); + } + } + + // evaluate optional parameter MDD type name -------------------------------------- + if (cmlInter.isPresent( PARAM_MDDTYPE )) + { + mddTypeName = cmlInter.getValueAsString( PARAM_MDDTYPE ); + mddTypeNameDef = true; + } + + // evaluate optional parameter 'quiet' -------------------------------------------- + if (cmlInter.isPresent( PARAM_QUIET )) + { + quietLog = true; + } + +#ifdef DEBUG + // evaluate optional parameter MDD type name -------------------------------------- + SET_OUTPUT( cmlInter.isPresent( PARAM_DEBUG ) ); +#endif + + } + catch(CmlException& err) + { + cerr << err.what() << endl; + throw RasqlError( ERRORPARSINGCOMMANDLINE ); + } +} // parseParams() + + +void +openDatabase() throw (r_Error) +{ + ENTER( "openDatabase" ); + + if (! dbIsOpen) + { + LOG( "opening database " << baseName << " at " << serverName << ":" << serverPort << "..." << flush ); + db.set_servername(serverName, serverPort); + db.set_useridentification(user, passwd); + TALK( "database was closed, opening database=" << baseName << ", server=" << serverName << ", port=" << serverPort << ", user=" << user << ", passwd=" << passwd << "..." ); + db.open(baseName); + TALK( "ok" ); + dbIsOpen = true; + LOG( "ok" << endl << flush ); + } + + LEAVE( "openDatabase" ); +} // openDatabase() + +void +closeDatabase() throw (r_Error) +{ + ENTER( "closeDatabase" ); + + if (dbIsOpen) + { + TALK( "database was open, closing it" ); + db.close(); + dbIsOpen = false; + } + + LEAVE( "closeDatabase" ); + return; +} // closeDatabase() + +void +openTransaction(bool readwrite) throw (r_Error) +{ + ENTER( "openTransaction, readwrite=" << (readwrite ? "rw" : "ro" ) ); + + if (! taIsOpen) + { + if (readwrite) + { + TALK( "transaction was closed, opening rw..." ); + ta.begin(r_Transaction::read_write); + TALK( "ok" ); + } + else + { + TALK( "transaction was closed, opening ro..." ); + ta.begin(r_Transaction::read_only); + TALK( "ok" ); + } + + taIsOpen = true; + } + + LEAVE( "openTransaction" ); +} // openTransaction() + +void +closeTransaction( bool doCommit ) throw (r_Error) +{ + ENTER( "closeTransaction, doCommit=" << doCommit ); + + if (taIsOpen) + { + if (doCommit) + { + TALK( "transaction was open, committing it..." ); + ta.commit(); + TALK( "ok" ); + } + else + { + TALK( "transaction was open, aborting it..." ); + ta.abort(); + TALK( "ok" ); + } + taIsOpen = false; + } + + LEAVE( "closeTransaction" ); + return; +} // closeTransaction() + +void printScalar( const r_Scalar& scalar ) +{ + ENTER( "printScalar" ); + + switch( scalar.get_type()->type_id() ) + { + case r_Type::BOOL: + LOG( ( ((r_Primitive*)&scalar)->get_boolean() ? "t" : "f" ) << flush ); + break; + + case r_Type::CHAR: + LOG( (int)((r_Primitive*)&scalar)->get_char() << flush ); + break; + + case r_Type::OCTET: + LOG( (int)((r_Primitive*)&scalar)->get_octet() << flush ); + break; + + case r_Type::SHORT: + LOG( ((r_Primitive*)&scalar)->get_short() << flush ); + break; + + case r_Type::USHORT: + LOG( ((r_Primitive*)&scalar)->get_ushort() << flush ); + break; + + case r_Type::LONG: + LOG( ((r_Primitive*)&scalar)->get_long() << flush ); + break; + + case r_Type::ULONG: + LOG( ((r_Primitive*)&scalar)->get_ulong() << flush ); + break; + + case r_Type::FLOAT: + LOG( ((r_Primitive*)&scalar)->get_float() << flush ); + break; + + case r_Type::DOUBLE: + LOG( ((r_Primitive*)&scalar)->get_double() << flush ); + break; + + case r_Type::COMPLEXTYPE1: + case r_Type::COMPLEXTYPE2: + LOG( "(" << ((r_Complex*)&scalar)->get_re() << "," << ((r_Complex*)&scalar)->get_im() << ")" << flush ); + break; + + case r_Type::STRUCTURETYPE: + { + r_Structure* structValue = (r_Structure*)&scalar; + LOG( "{ " << flush ); + for( int i=0; i<structValue->count_elements(); i++ ) + { + printScalar( (*structValue)[i] ); + if( i < structValue->count_elements()-1 ) + LOG( ", " << flush ); + } + LOG( " }" << endl ); + } + break; + default: + LOG( "scalar type " << scalar.get_type()->type_id() << " not supported!" << endl ); + break; + } + LEAVE( "printScalar" ); +} // printScalar() + + +// result_set should be parameter, but is global -- see def for reason +void printResult( /* r_Set< r_Ref_Any > result_set */ ) +{ + ENTER( "printResult" ); + + LOG( "Query result collection has " << result_set.cardinality() << " element(s):" << endl ); + + if (displayType) + { + cout << " Oid...................: " << result_set.get_oid() << endl; + cout << " Type Structure........: " + << ( result_set.get_type_structure() ? result_set.get_type_structure() : "<nn>" ) << endl; + cout << " Type Schema...........: " << flush; + if( result_set.get_type_schema() ) + result_set.get_type_schema()->print_status( cout ); + else + cout << "(no name)" << flush; + cout << endl; + cout << " Number of entries.....: " << result_set.cardinality() << endl; + cout << " Element Type Schema...: " << flush; + if( result_set.get_element_type_schema() ) + result_set.get_element_type_schema()->print_status( cout ); + else + cout << "(no name)" << flush; + cout << endl; + } + + /* The following can be used if the type is known and the element type is not atomic. + + r_Set< r_Ref< r_Point > >* set2 = (r_Set< r_Ref< r_Point > >*)&result_set; + r_Iterator< r_Ref<r_Point> > iter2 = set2->create_iterator(); + for( iter2.reset(); iter2.not_done(); iter2++ ) + cout << **iter2 << endl; + */ + + r_Iterator< r_Ref_Any > iter = result_set.create_iterator(); + // iter.not_done() seems to behave wrongly on empty set, therefore this additional check -- PB 2003-aug-16 + for ( int i=1 ; i<=result_set.cardinality() && iter.not_done(); iter++, i++ ) + { + switch( result_set.get_element_type_schema()->type_id() ) + { + case r_Type::MARRAYTYPE: + switch ( outputType ) + { + case OUT_NONE: + break; + case OUT_STRING: + { + int numCells = r_Ref<r_GMarray>(*iter)->get_array_size(); + const char* theStuff = r_Ref<r_GMarray>(*iter)->get_array(); + LOG( " Result object " << i << ": " ); + for (int cnt = 0; cnt < numCells; cnt++) + cout << theStuff[cnt]; + cout << endl; + } + break; + case OUT_HEX: + { + int numCells = r_Ref<r_GMarray>(*iter)->get_array_size(); + const char* theStuff = r_Ref<r_GMarray>(*iter)->get_array(); + LOG( " Result object " << i << ": " ); + cout << hex; + for (int cnt = 0; cnt < numCells; cnt++) + cout << setw(2) << (unsigned short) (0xff & theStuff[cnt]) << " "; + cout << dec << endl; + } + break; + case OUT_FORMATTED: + LOG( " Result object " << i << ":" << endl ); + // for (int cnt = 0; cnt < numCells; cnt++) + printScalar( *(r_Ref<r_Scalar>(*iter)) ); + cout << endl; + break; + case OUT_FILE: + { + char defFileName[FILENAME_MAX]; + (void) snprintf( defFileName, sizeof(defFileName)-1, outFileMask, i ); + TALK( "filename for #" << i << " is " << defFileName ); + + // special treatment only for DEFs + r_Data_Format mafmt = r_Ref<r_GMarray>(*iter)->get_current_format(); + switch (mafmt) + { + case r_TIFF: + strcat( defFileName, ".tif" ); break; + case r_JPEG: + strcat( defFileName, ".jpg" ); break; + case r_HDF: + strcat( defFileName, ".hdf" ); break; + case r_PNG: + strcat( defFileName, ".png" ); break; + case r_BMP: + strcat( defFileName, ".bmp" ); break; + case r_VFF: + strcat( defFileName, ".vff" ); break; + default: + strcat( defFileName, ".unknown" ); break; + break; + } + + LOG( " Result object " << i << ": going into file " << defFileName << "..." << flush ); + FILE *tfile = fopen( defFileName, "wb" ); + fwrite((void*)r_Ref<r_GMarray>(*iter)->get_array(), 1, r_Ref<r_GMarray>(*iter)->get_array_size(), tfile ); + fclose(tfile); + LOG( "ok." << endl ); + } + break; + default: + cerr << "Internal error: unknown output type, ignoring action: " << outputType << endl; + break; + } // switch(outputType) + break; + + case r_Type::POINTTYPE: + LOG( " Result element " << i << ": " ); + cout << *(r_Ref<r_Point>(*iter)) << endl; + break; + + case r_Type::SINTERVALTYPE: + LOG( " Result element " << i << ": " ); + cout << *(r_Ref<r_Sinterval>(*iter)) << endl; + break; + + case r_Type::MINTERVALTYPE: + LOG( " Result element " << i << ": " ); + cout << *(r_Ref<r_Minterval>(*iter)) << endl; + break; + + case r_Type::OIDTYPE: + LOG( " Result element " << i << ": " ); + cout << *(r_Ref<r_OId>(*iter)) << endl; + break; + + default: + LOG( " Result element " << i << ": " << flush ); + printScalar( *(r_Ref<r_Scalar>(*iter)) ); + cout << endl; + // or simply + // r_Ref<r_Scalar>(*iter)->print_status( cout ); + } // switch + } // for(...) + + LEAVE( "printResult" ); +} // printResult() + + +/* + * get database type structure from type name + * returns ptr if an MDD type with the given name exists in the database, NULL otherwise + * throws r_Error upon general database comm error + * needs an open transaction + */ +r_Marray_Type * getTypeFromDatabase( const char *mddTypeName ) throw(RasqlError, r_Error) +{ + ENTER( "getTypeFromDatabase, mddTypeName=" << mddTypeName ); + r_Marray_Type *retval = NULL; + char* typeStructure = NULL; + + // first, try to get type structure from database using a separate r/o transaction + try + { + typeStructure = db.getComm()->getTypeStructure(mddTypeName, ClientComm::r_MDDType_Type); + TALK( "type structure is " << typeStructure ); + } + catch (r_Error& err) + { + if (err.get_kind() == r_Error::r_Error_DatabaseClassUndefined) + { + TALK( "Type is not a well known type: " << typeStructure ); + typeStructure = new char[strlen(mddTypeName) + 1]; + // earlier code tried this one below, but I feel we better are strict -- PB 2003-jul-06 + // strcpy(typeStructure, mddTypeName); + // TALK( "using instead: " << typeStructure ); + throw RasqlError( MDDTYPEINVALID ); + } + else // unanticipated error + { + TALK( "Error during type retrieval from database: " << err.get_errorno() << " " << err.what() ); + throw; + } + } + + // next, find out whether it is an MDD type (and not a base or set type, eg) + try + { + r_Type* tempType = r_Type::get_any_type(typeStructure); + TALK( "get_any_type() for this type returns: " << tempType ); + if (tempType->isMarrayType()) + { + retval = (r_Marray_Type*)tempType; + tempType = NULL; + TALK( "found MDD type: " << retval ); + } + else + { + TALK( "type is not an marray type: " << typeStructure ); + delete tempType; + tempType = NULL; + retval = NULL; + throw RasqlError( MDDTYPEINVALID ); + } + } + catch (r_Error& err) + { + TALK( "Error during retrieval of MDD type structure (" << typeStructure << "): " << err.get_errorno() << " " << err.what() ); + throw; + } + + delete [] typeStructure; + typeStructure = NULL; + + LEAVE( "getTypeFromDatabase, retval=" << retval ); + return retval; +} // getTypeFromDatabase() + +void doStuff( int argc, char** argv ) throw (r_Error) +{ + char *fileContents = NULL; // contents of file satisfying "$1" parameter in query + r_Ref<r_GMarray> fileMDD = NULL; // MDD to satisfy a "$1" parameter + r_Marray_Type *mddType = NULL; // this MDD's type + + ENTER( "doStuff" ); + + r_OQL_Query query( queryString ); + TALK( "query is: " << query.get_query() ); + + if ( fileName != NULL ) + { + openTransaction( false ); + + // if no type name was specified then assume byte string (for encoded files) + if ( ! mddTypeNameDef ) + mddTypeName = MDD_STRINGTYPE; + + LOG( "fetching type information for " << mddTypeName << " from database, using readonly transaction..." << flush ); + mddType = getTypeFromDatabase( mddTypeName ); + closeTransaction( true ); + LOG( "ok" << endl ); + + LOG( "reading file " << fileName << "..." << flush ); + FILE* fileD = fopen( fileName, "r" ); + if (fileD == NULL) + throw RasqlError( FILEINACCESSIBLE ); + + fseek( fileD, 0, SEEK_END ); + int size = ftell( fileD ); + TALK( "file size is " << size << " bytes" ); + try + { + fileContents = new char[size]; + } + catch(std::bad_alloc) + { + TALK( "Unable to claim memory: " << size << " Bytes" ); + throw RasqlError( UNABLETOCLAIMRESOURCEFORFILE ); + } + + fseek( fileD, 0, SEEK_SET ); + fread( fileContents, 1, size, fileD ); + fclose( fileD ); + + // if no domain specified (this is the case with encoded files), then set to byte stream: + if ( ! mddDomainDef ) + { + mddDomain = r_Minterval( 1 ) << r_Sinterval ( 0, size-1 ); + TALK( "domain set to " << mddDomain ); + } + + if (size != mddDomain.cell_count() * mddType->base_type().size()) + throw RasqlError( FILESIZEMISMATCH ); + LOG( "ok" << endl ); + + TALK( "setting up MDD with domain " << mddDomain << " and base type " << mddTypeName ); + fileMDD = new (mddTypeName) r_GMarray( mddDomain, mddType->base_type().size() ); + fileMDD->set_type_schema( mddType ); + fileMDD->set_array_size( mddDomain.cell_count() * mddType->base_type().size() ); + fileMDD->set_array( fileContents ); + + query << *fileMDD; + + TALK( "constants are:" ); + r_Set<r_GMarray *> * myConstSet = (r_Set<r_GMarray *> *) query.get_constants(); + r_Iterator< r_GMarray *> iter = myConstSet->create_iterator(); + int i; + for ( i=1, iter.reset(); iter.not_done(); iter++, i++ ) + { + r_Ref< r_GMarray > myConstant = *iter; + LOG( " constant " << i << ": " ); + myConstant->print_status( cout ); +// the following can be used for sporadic debugging of input files, but beware: is very verbose! +#if 0 + cout << " Contents: " << hex; + const char *a = myConstant->get_array(); + for (int m=0; m < myConstant->get_array_size(); m++) + cout << (unsigned short) (a[m] & 0xFF) << " "; + cout << dec << endl; +#endif + } + } + + if( query.is_update_query() ) + { + openTransaction( true ); + + r_Marray<r_ULong>* mddConst = NULL; + + LOG( "Executing update query..." << flush ); + r_oql_execute( query ); + LOG( "ok" << endl ); + + if( mddConst ) + delete mddConst; + + closeTransaction( true ); + } + else + { + openTransaction( false ); + + // should be defined here, but is global; see def for reason + // r_Set< r_Ref_Any > result_set; + + LOG( "Executing retrieval query..." << flush ); + r_oql_execute( query, result_set ); + LOG( "ok" << endl ); + + // generate output only if explicitly requested + if( output ) + printResult( /* result_set */ ); + + closeTransaction( true ); + } + + if (fileContents != NULL) + delete [] fileContents; + + LEAVE( "doStuff" ); +} + +/* + * returns 0 on success, -1 on error + */ +int main(int argc, char** argv) +{ + SET_OUTPUT( false ); // inhibit unconditional debug output, await cmd line evaluation + + int retval = EXIT_SUCCESS; // overall result status + + try + { + parseParams( argc, argv ); + + // put LOG after parsing parameters to respect a '--quiet' + LOG( argv[0] << ": rasdaman query tool v1.0, rasdaman v" << RMANVERSION/1000 << " -- generated on " << COMPDATE << "." << endl ); + + openDatabase(); + doStuff( argc, argv ); + closeDatabase(); + retval = EXIT_SUCCESS; + } + catch (RasqlError& e) + { + cerr << argv[0] << ": " << e.what() << endl; + retval = EXIT_FAILURE; + } + catch (const r_Error& e) + { + cerr << "rasdaman error " << e.get_errorno() << ": " << e.what() << endl; + retval = EXIT_FAILURE; + } + catch (...) + { + cerr << argv[0] << ": panic: unexpected internal exception." << endl; + retval = EXIT_FAILURE; + } + + if (retval != EXIT_SUCCESS && (dbIsOpen || taIsOpen) ) + { + LOG( "aborting transaction..." << flush ); + closeTransaction( false ); // abort transaction and close database, ignore any further exceptions + LOG( "ok" << endl ); + closeDatabase(); + } + + LOG( argv[0] << " done." << endl ); + return retval; +} // main() + +// end of rasql.cc + diff --git a/applications/rasql/rasql_error.cc b/applications/rasql/rasql_error.cc new file mode 100644 index 0000000..2d5f6e0 --- /dev/null +++ b/applications/rasql/rasql_error.cc @@ -0,0 +1,120 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** +* rasqlError +* +* Provides a error checking for the rasql queries +* options for storing results to file(s) or displaying them +* +* COMMENTS: +* +* No comments +*/ + +using namespace std; + +static const char rcsid[] = "@(#)raslib, RasqlError: $Id: rasql_error.cc,v 1.1 2003/12/27 19:30:23 rasdev Exp $"; + +#include <exception> +#include <cstring> + +// for sprintf(): +#include <stdio.h> + +#include "rasql_error.hh" + +// debug facility; relies on -DDEBUG at compile time +#include "debug-clt.hh" + +/// error object, carrying int error code +RasqlError::RasqlError( unsigned int e ) +{ + TALK( "Exception: " << e ); + errno = e; +} + +/// default destructor +RasqlError::~RasqlError() +{ +} + +/// print error message (including error code) +/// NB: not all messages can occur +const char* +RasqlError::what() +{ + const char *errorMsg; + switch (errno) + { + case NOQUERY: + errorMsg = "Mandatory parameter '--query' missing."; + break; + case ERRORPARSINGCOMMANDLINE: + errorMsg = "Command line syntax error."; + break; + case ILLEGALOUTPUTTYPE: + errorMsg = "Illegal output type specifier, must be one of none, file, formatted, string, hex."; + break; + case FILEINACCESSIBLE: + errorMsg = "Cannot read input file."; + break; + case UNABLETOCLAIMRESOURCEFORFILE: + errorMsg = "Cannot allocate memory for file read."; + break; + case NOVALIDDOMAIN: + errorMsg = "Syntax error in mdddomain specification, must be [x0:x1,y0:y1] (forgot to quote or escape?)"; + break; + case MDDTYPEINVALID: + errorMsg = "MDD type invalid."; + break; + case FILESIZEMISMATCH: + errorMsg = "Input file size does not correspond with MDD domain specified."; + break; + default : + errorMsg = "Unknown error code."; + break; + case ALLDONE: + case 0: + errorMsg = "No errors."; + } + +// size of error text buffer below +#define ERRTEXT_BUFSIZ 200 + + static char errorText[ERRTEXT_BUFSIZ]; + +// text constants for error msg +#define MODULE_TAG "IO" +#define ERROR_TEXT " Error: " + + // check for buffer overflow + if (strlen(MODULE_TAG) + 3 + strlen(ERROR_TEXT) + strlen(errorMsg) + 1 > ERRTEXT_BUFSIZ) + sprintf( errorText, "%s%03d%s", MODULE_TAG, errno, "(error message too long, cannot display)" ); + else + sprintf( errorText, "%s%03d%s%s", MODULE_TAG, errno, ERROR_TEXT, errorMsg ); + + return errorText; +} // what() + + diff --git a/applications/rasql/rasql_error.hh b/applications/rasql/rasql_error.hh new file mode 100644 index 0000000..cf372ab --- /dev/null +++ b/applications/rasql/rasql_error.hh @@ -0,0 +1,79 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** +* rasqlError +* +* Provides a error checking for rasql queries +* +* COMMENTS: +* +* No Comments +*/ + +#ifndef _RASQL_ERROR_HH_ +#define _RASQL_ERROR_HH_ + +#ifdef __VISUALC__ +#pragma warning( disable : 4290 ) +#endif + +//@ManMemo: Module: {\bf raslib} + +/*@Doc: + + This class ... +*/ + + + /// valid error codes: +#define ALLDONE -1 +#define OK 0 +#define NOQUERY 1 +#define ERRORPARSINGCOMMANDLINE 2 +#define ILLEGALOUTPUTTYPE 3 +#define FILEINACCESSIBLE 4 +#define UNABLETOCLAIMRESOURCEFORFILE 5 +#define NOVALIDDOMAIN 6 +#define MDDTYPEINVALID 7 +#define FILESIZEMISMATCH 8 + +class RasqlError // : public std::exception +{ + public: + + /// constructor receiving an error number + RasqlError( unsigned int e ); + + /// destructor + virtual ~RasqlError(); + + /// get an error description + virtual const char * what(); + + private: + /// error information + unsigned int errno; +}; + +#endif // _RASQL_ERROR_HH_ diff --git a/applications/rasql/rasql_signal.cc b/applications/rasql/rasql_signal.cc new file mode 100644 index 0000000..49ce7d4 --- /dev/null +++ b/applications/rasql/rasql_signal.cc @@ -0,0 +1,244 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** +* rasql_signal +* +* PURPOSE: Provides signal handling +* +* COMMENTS: +* +* No comments +*/ + + +static const char rcsid[] = "@(#)rasodmg/test,ImportOrthoUtil: $Id: rasql_signal.cc,v 1.1 2003/12/27 19:30:23 rasdev Exp $"; + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#include "raslib/template_inst.hh" +#endif + +#include <iostream> +#include <string> +#include <iostream> +#include <signal.h> +#ifdef SOLARIS +#include <strings.h> +#endif + +#include "rasql_error.hh" +#include "rasql_signal.hh" + +// debug facility; relies on -DDEBUG at compile time +#include "debug-clt.hh" + +//signalCleanup function is called when a signal is received by the program. +//You should write your function in order to have signal management +void signalCleanup(); + +//signalHandler function is called when a signal occurs +void +signalHandler(int sig); + +//installSignalHandlers function should be called first in main function +//in order to receive a signal in your program +void +signalHandler(int sig) +{ + static bool handleSignal = true; // sema to prevent nested signals + + cout << "Caught signal " << sig << ": "; + switch (sig) + { + case SIGHUP: + cout << "Hangup (POSIX). "; + break; + case SIGINT: + cout << "Interrupt (ANSI)."; + break; + case SIGQUIT: + cout << "Quit (POSIX)."; + break; + case SIGILL: + cout << "Illegal instruction (ANSI)."; + break; + case SIGTRAP: + cout << "Trace trap (POSIX)."; + break; + case SIGABRT: + cout << "Abort (ANSI) or IOT trap (4.2 BSD)."; + break; + case SIGBUS: + cout << "BUS error (4.2 BSD)."; + break; + case SIGFPE: + cout << "Floating-point exception (ANSI)."; + break; + case SIGKILL: + cout << "Kill, unblockable (POSIX)."; + break; + case SIGUSR1: + cout << "User-defined signal 1 (POSIX)."; + break; + case SIGSEGV: + cout << "Segmentation violation (ANSI)."; + break; + case SIGUSR2: + cout << "User-defined signal 2 (POSIX)."; + break; + case SIGPIPE: + cout << "Broken pipe (POSIX)."; + break; + case SIGALRM: + cout << "Alarm clock (POSIX)."; + break; + case SIGTERM: + cout << "Termination (ANSI)."; + break; +#ifndef SOLARIS +#ifndef DECALPHA + case SIGSTKFLT: + cout << "Stack fault."; + break; +#endif +#endif + case SIGCLD: + cout << "SIGCHLD (System V) or child status has changed (POSIX)."; + break; + case SIGCONT: + cout << "Continue (POSIX)."; + break; + case SIGSTOP: + cout << "Stop, unblockable (POSIX)."; + break; + case SIGTSTP: + cout << "Keyboard stop (POSIX). Continuing operation."; + break; + case SIGTTIN: + cout << "Background read from tty (POSIX)."; + break; + case SIGTTOU: + cout << "Background write to tty (POSIX). Continuing operation"; + break; + case SIGURG: + cout << "Urgent condition on socket (4.2 BSD)."; + break; + case SIGXCPU: + cout << "CPU limit exceeded (4.2 BSD)."; + break; + case SIGXFSZ: + cout << "File size limit exceeded (4.2 BSD)."; + break; + case SIGVTALRM: + cout << "Virtual alarm clock (4.2 BSD)."; + break; + case SIGPROF: + cout << "Profiling alarm clock (4.2 BSD)."; + break; + case SIGWINCH: + cout << "Window size change (4.3 BSD, Sun). Continuing operation."; + break; + case SIGPOLL: + cout << "Pollable event occurred (System V) or I/O now possible (4.2 BSD)."; + break; + case SIGPWR: + cout << "Power failure restart (System V)."; + break; + case SIGSYS: + cout << "Bad system call."; + break; + default: + cout << "Unknown signal."; + break; + } + cout << endl << flush; + + // no repeated signals + if (handleSignal) + handleSignal = false; + + if (sig == SIGCONT || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU || sig == SIGWINCH) + return; + else + { + TALK( "fatal signal, exiting." << flush ); + exit(sig); + } +} + +void +installSignalHandlers() +{ + ENTER( "installSignalHandlers" ); + + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + signal(SIGHUP, signalHandler); + signal(SIGPIPE, signalHandler); + signal(SIGHUP, signalHandler); + signal(SIGINT, signalHandler); + signal(SIGQUIT, signalHandler); + signal(SIGILL, signalHandler); + signal(SIGTRAP, signalHandler); + signal(SIGABRT, signalHandler); + signal(SIGIOT, signalHandler); + signal(SIGBUS, signalHandler); + signal(SIGFPE, signalHandler); + signal(SIGKILL, signalHandler); + signal(SIGUSR1, signalHandler); + signal(SIGSEGV, signalHandler); + signal(SIGUSR2, signalHandler); + signal(SIGPIPE, signalHandler); + signal(SIGALRM, signalHandler); + signal(SIGTERM, signalHandler); +#ifndef SOLARIS +#ifndef DECALPHA + signal(SIGSTKFLT, signalHandler); +#endif +#endif + signal(SIGCLD, signalHandler); + signal(SIGCHLD, signalHandler); + signal(SIGCONT, signalHandler); + signal(SIGSTOP, signalHandler); + signal(SIGTSTP, signalHandler); + signal(SIGTTIN, signalHandler); + signal(SIGTTOU, signalHandler); + signal(SIGURG, signalHandler); + signal(SIGXCPU, signalHandler); + signal(SIGXFSZ, signalHandler); + signal(SIGVTALRM, signalHandler); + signal(SIGPROF, signalHandler); + signal(SIGWINCH, signalHandler); + signal(SIGPOLL, signalHandler); + signal(SIGIO, signalHandler); + signal(SIGPWR, signalHandler); + signal(SIGSYS, signalHandler); +#if !defined SOLARIS +#if !defined DECALPHA + signal(SIGUNUSED, signalHandler); +#endif +#endif + LEAVE( "installSignalHandlers" ); +} + diff --git a/applications/rasql/rasql_signal.hh b/applications/rasql/rasql_signal.hh new file mode 100644 index 0000000..5ac67a5 --- /dev/null +++ b/applications/rasql/rasql_signal.hh @@ -0,0 +1,54 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** +* rasql_signal +* +* Provides signal handling +* +* COMMENTS: +* +* No comments +*/ + +#ifndef _RASQL_SIGNAL_HH_ +#define _RASQL_SIGNAL_HH_ + +#include <signal.h> + +//signalCleanup function is called when a signal is received by the program. +//You should write your function in order to have signal management +void signalCleanup(); + +//signalHandler function is called when a signal occurs +void +signalHandler(int sig); + +//installSignalHandlers function should be called first in main function +//in order to receive signal in your program + +void +installSignalHandlers(); + +#endif _RASQL_SIGNAL_HH_ + diff --git a/applications/rasql/test/Makefile b/applications/rasql/test/Makefile new file mode 100644 index 0000000..023f27c --- /dev/null +++ b/applications/rasql/test/Makefile @@ -0,0 +1,66 @@ +# -*-Makefile-*- +# +# This file is part of rasdaman community. +# +# Rasdaman community 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 3 of the License, or +# (at your option) any later version. +# +# Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +# +# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +# rasdaman GmbH. +# +# For more information please see <http://www.rasdaman.org> +# or contact Peter Baumann via <baumann@rasdaman.com>. # Top Level makefile. This points to the various modules that have to be build +# and/or deployed +# + +# MAKEFILE FOR: +# the test program for a module +# +# COMMENTS: +# List environment dependencies, known bugs, specialities etc. +# +################################################################## +# +# This is just an example Makefile for a test program. +# The dependency of the test program on the lib of the +# corresponding module is in the Makefile of the module. +# + +######################### Definitions ############################ + +# standard include with general options +include $(RMANBASE)/Makefile.inc + +# object files for test program +TESTOBJS = test/test.o +# name of test program +TESTPRG = testex + +########################### Targets ############################## + +$(TESTPRG): $(TESTOBJS) $(OBJS) + $(CXX) $(LDFLAGS) -o $@ $^ + +.PHONY : clean +clean: + -rm $(TESTOBJS) + -rm $(TESTPRG) + +# deletes all non modified, but checked out files +.PHONY : rcsclean +rcsclean: + -rcsclean + +######################## Dependencies ############################ + +test.o: $(INCDIR)/m1.hh diff --git a/applications/rview/Makefile b/applications/rview/Makefile new file mode 100644 index 0000000..27f894c --- /dev/null +++ b/applications/rview/Makefile @@ -0,0 +1,499 @@ +# -*-Makefile-*- +# +# This file is part of rasdaman community. +# +# Rasdaman community 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 3 of the License, or +# (at your option) any later version. +# +# Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +# +# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +# rasdaman GmbH. +# +# For more information please see <http://www.rasdaman.org> +# or contact Peter Baumann via <baumann@rasdaman.com>. # Top Level makefile. This points to the various modules that have to be build +# and/or deployed +# +# MAKEFILE FOR: +# RASDAMAN rview client +# +# COMMENTS: +# - does not work currently, use old .SAVE instead +# +################################################################## + +# standard include with general options +include $(RMANBASE)/Makefile.inc + +# WXDIR = $(SUPPORT_BASEDIR)/wxWindows +WXDIR = $(RMANBASE)/3rdParty/wxX11-2.4.2 + +# Get all common compiler flags: +ifeq ($(OSTYPE),$(OSTYPE_SOLARIS)) + include $(WXDIR)/src/makeenvs/sol_sun.env +endif + +ifeq ($(OSTYPE),$(OSTYPE_LINUX)) +# include $(WXDIR)/src/makeenvs/linux.env +endif + +PIXMAPLIB = lib/libpixmap$(GUISUFFIX).a +PIXMAPSHLIB = lib/libpixmap$(GUISUFFIX).$(SHLIBSUFF) + +# flags common to all Unix platforms +#INC = -I$(WXDIR)/include/base -I$(WXDIR)/include/x +#INC = -I$(WXDIR)/include -I$(WXDIR)/include/wx -D__WXUNIVERSAL__ -D__WXX11__ -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -DHAVE_BOOL +INC = `wx-config --cxxflags` -DwxUSE_GLCANVAS=1 + +RVIEW_VERSION = 2.1 + +TIFFPATH = /usr/local/ +TIFFINC = -I$(TIFFPATH)/include +TIFFLIBPATH = -L$(TIFFPATH)/lib +TIFFLIB = $(TIFFLIBPATH) -ltiff + +# define this if you want to use the VFF convertor +IODEFINES = -DRVIEW_USE_VFF + +#stuff for static link +ifeq ($(OSTYPE),$(OSTYPE_SOLARIS)) +DYNAMICLIBS = -lXm -lXmu -lICE -lSM -lXt -lX11 -lXext -ljpeg -lcrypto +STATICLIBS = $(SUPPORT_BASE)/lib/libwx_motif.a $(SUPPORT_BASE)/lib/libpixmap_motif.a \ + $(SUPPORT_BASE)/lib/libtiff.a $(SUPPORT_BASE)/lib/libjpeg.a $(SUPPORT_BASE)/lib/libpng.a\ + $(SUPPORT_BASE)/lib/libmfhdf.a $(SUPPORT_BASE)/lib/libdf.a $(SUPPORT_BASE)/lib/libz.a \ + $(SUPPORT_BASE)/lib/libcrypto.a +endif + +ifeq ($(OSTYPE),$(OSTYPE_LINUX)) +DYNAMICLIBS = -lXt -lX11 -lXp -lXext -lICE -lSM +#STATICLIBS =$(SUPPORT_BASE)/lib/libwx_motif.a $(SUPPORT_BASE)/lib/libpixmap_motif.a -lXpm -lXm -lXmu \ +# $(SUPPORT_BASE)/lib/libtiff.a $(SUPPORT_BASE)/lib/libjpeg.a $(SUPPORT_BASE)/lib/libpng.a\ +# $(SUPPORT_BASE)/lib/libmfhdf.a $(SUPPORT_BASE)/lib/libdf.a $(SUPPORT_BASE)/lib/libz.a \ +# $(SUPPORT_BASE)/lib/libcrypto.a +endif + +ifeq ($(OSTYPE),$(OSTYPE_SOLARIS)) +# Purify gets libraries wrong otherwise! +TIFFLIB_PURE = -L. -ltiff +SOUNDPATH = /usr/demo/SOUND +SOUNDINC = -I$(SOUNDPATH)/include +SOUNDLIB = -L$(SOUNDPATH)/lib -laudio +endif + +LINK_LIBS = $(LDFLAGS) $(SOUNDLIB) $(LDLIBS) -lwx$(GUISUFFIX) +#-lwx$(GUISUFFIX) +STATICLINK_LIBS = $(LDFLAGS) $(SOUNDLIB) $(COMPLIBS) -lXm -lXmu -lXt -lX11 -lm +#-L./lib -pthread /usr/local/lib/libwx_x11univ-2.4.a -L/usr/X11R6/lib -lX11 -lXpm -lpng -ljpeg -ltiff -ldl -lm + + +# On HP: add +eh +a1 -ptb +# Replace -DSOLARIS with -DHPUX on HP +ifeq ($(OSTYPE),$(OSTYPE_SOLARIS)) +RVFLAGS = $(CPPFLAGS) $(CXXFLAGS) -DONCRPC -DSOLARIS $(COMPFLAGS) +ifdef RMANGCC +RVFLAGS += -DEARLY_TEMPLATE +else +RVFLAGS += -ptr$(RMANBASE)/include/ptrepository +endif # end ifdef RMANGCC +endif #end ifeq OSTYPE=solaris + +ifeq ($(OSTYPE),$(OSTYPE_LINUX)) +RVFLAGS = $(CPPFLAGS) $(CXXFLAGS) -DONCRPC -DLINUX -DEARLY_TEMPLATE $(COMPFLAGS) $(INC) +endif + +# Replace -DSOLARIS on HP +ifeq ($(OSTYPE),$(OSTYPE_SOLARIS)) +RVLINK = $(LINK_LIBS) -DONCRPC -DSOLARIS -Llib \ + $(RASODMG) $(CLIENTCOMM) $(COMPRESSION) $(CONVERSION) $(RASLIB) -lz +STATICRVLINK = $(STATICLINK_LIBS) -DONCRPC -DSOLARIS -Llib \ + $(RASODMG) $(CLIENTCOMM) $(CONVERSION) $(COMPRESSION) $(RASLIB) +endif +ifeq ($(OSTYPE),$(OSTYPE_LINUX)) +RVLINK = $(LINK_LIBS) -DONCRPC -DLINUX -Llib -L/usr/X11R6/lib \ + $(RASODMG) $(CLIENTCOMM) $(COMPRESSION) $(CONVERSION) $(RASLIB) -lz +STATICRVLINK = $(STATICLINK_LIBS) -DONCRPC -DLINUX -DEARLY_TEMPLATE -Llib -L/usr/X11R6/lib \ + $(RASODMG) $(CLIENTCOMM) $(COMPRESSION) $(CONVERSION) $(RASLIB) -lz +endif + +ifneq (,$(findstring -g,$(COMPFLAGS))) + RVLINK += -g +endif + +DEPENDFLAGS = $(RVFLAGS) $(TIFFINC) $(SOUNDINC) + + +TEX_RERUN_CHECK = 'Rerun to get cross-references right' + + + + +RVIEW_SOURCES = rview.cpp rviewChart.cpp rviewDb.cpp rviewDisplay.cpp rviewIO.cpp \ + rviewImage.cpp rviewMDD.cpp rviewPrefs.cpp rviewQuery.cpp \ + rviewTable.cpp rviewThumb.cpp rviewUtils.cpp rviewSound.cpp \ + rviewTypeMan.cpp rviewColMap.cpp rviewOSection.cpp rviewApp.cpp \ + rviewStrings.cpp neuroView.cpp neuroMeta.cpp labelManager.cpp cube_render.c + +BASEOBJECTS = rviewUtils.o rviewDb.o \ + rviewPrefs.o rviewDisplay.o \ + rviewImage.o rviewChart.o \ + rviewTable.o rviewMDD.o \ + rviewQuery.o rviewIO.o \ + rviewThumb.o rviewSound.o \ + rviewTypeMan.o rviewColMap.o \ + rviewOSection.o rviewApp.o \ + rviewStrings.o labelManager.o \ + cube_render.o + +RVIEWOBJECTS = rview.o $(BASEOBJECTS) + +NVIEWOBJECTS = neuroView.o neuroMeta.o $(BASEOBJECTS) + +.PHONY : all +all: # rview_static + # FIXME: while rview doesn't compile, use old copy + cp rview.SAVE rview + echo wx dyn lib is in /usr/local/lib/libwx_x11univ-2.4.so.0.1.1 + + +# Main project + +# Standard rView binary dynamical linked +rview: $(RVIEWOBJECTS) $(PIXMAPLIB) + $(CC) -o $@ $(RVIEWOBJECTS) $(TEMPLOBJS) $(TIFFLIB) $(RVLINK) -lpixmap$(GUISUFFIX) \ + $(RASODMG) -lcrypto + +test: + $(CC) -nodefaultlibs -o rview $(RVIEWOBJECTS) $(TEMPLOBJS) $(DYNAMICLIBS) \ + -Xlinker -Bstatic $(STATICRVLINK) $(STATICLIBS) $(RASODMG) -lstdc++ \ + -lwx_motif -lwxstring_motif -Xlinker -Bdynamic -lm -lgcc -lc -lgcc + +# Standard rView binary statical linked +ifeq ($(OSTYPE),$(OSTYPE_LINUX)) + +rview_static: $(RVIEWOBJECTS) $(PIXMAPLIB) + # $(CC) -nodefaultlibs -o rview $(RVIEWOBJECTS) $(TEMPLOBJS) $(DYNAMICLIBS) \ + # -Xlinker -Bstatic $(STATICRVLINK) $(STATICLIBS) $(RASODMG) -lstdc++ \ + # -Xlinker -Bdynamic -lm -lgcc -lc -lgcc + $(CC) -nodefaultlibs -o rview $(RVIEWOBJECTS) $(TEMPLOBJS) $(DYNAMICLIBS) \ + -Xlinker -Bstatic $(STATICRVLINK) $(STATICLIBS) $(RASODMG) -lstdc++ \ + -lwx_motif -lwxstring_motif -Xlinker -Bdynamic -lm -lgcc -lc -lgcc + +endif +ifeq ($(OSTYPE),$(OSTYPE_SOLARIS)) + +rview_static: $(RVIEWOBJECTS) $(PIXMAPLIB) + $(PURIFY) $(CC) -o rview $(RVIEWOBJECTS) $(TEMPLOBJS) $(STATICRVLINK) $(STATICLIBS) +endif + +# Purified rView binary +rview_pure: $(RVIEWOBJECTS) $(PIXMAPLIB) + purify $(CC) -o $@ $(RVIEWOBJECTS) $(TEMPLOBJS) $(TIFFLIB_PURE) $(RVLINK) \ + -lpixmap$(GUISUFFIX) + +# Create rView source file dependencies and store them in the file rviewdepend. +rview_depend: +# Create all dependencies and write them to rviewdepend + -rm -f _rdp_ + echo 'for i in $(RVIEW_SOURCES); do \ + echo $$i; \ + $(CC) -xM1 $(DEPENDFLAGS) $$i | grep "[a-zA-Z_0-9]* : [a-zA-Z_0-9]" >> _rdp_; \ + done' | ksh + sort -u -o _rdps_ < _rdp_ + cp _rdps_ Makefile.dep # sed -e 's/\</\$$\(OBJDIR\)\//' _rdps_ > Makefile.dep + -rm -f _rdp_ _rdps_ + + +# create release +rview-release: rview_static + -rm rview_$(RVIEW_VERSION).tar.gz + -strip rview + tar fc rview_$(RVIEW_VERSION).tar labels.txt rview + gzip -9 rview_$(RVIEW_VERSION).tar + +# rView objects -- dependencies in separate Makefile.dep +rview.o: rview.cpp + @# $(CC) -c $(RVFLAGS) -DRVIEW_VERSION=$(RVIEW_VERSION) -o $@ rview.cpp + g++ -c -I/home/rasdev/Compile/rasdaman -DCOMPDATE="\"`date +\"%d.%m.%Y %H:%M:%S\"`\"" -DRMANVERSION=6000 -g -DLINUX -DEARLY_TEMPLATE -DLITTLE_ENDIAN $(INC) -DRVIEW_VERSION=2.1 -o objects/rview.o rview.cpp + +rviewUtils.o: rviewUtils.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewUtils.cpp + +rviewDb.o: rviewDb.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewDb.cpp + +rviewPrefs.o: rviewPrefs.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewPrefs.cpp + +rviewDisplay.o: rviewDisplay.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewDisplay.cpp + +rviewImage.o: rviewImage.cpp wx_pixmap.h + $(CC) -c $(RVFLAGS) -o $@ rviewImage.cpp + +rviewChart.o: rviewChart.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewChart.cpp + +rviewTable.o: rviewTable.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewTable.cpp + +rviewMDD.o: rviewMDD.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewMDD.cpp + +rviewQuery.o: rviewQuery.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewQuery.cpp + +rviewIO.o: rviewIO.cpp wx_pixmap.h + $(CC) -c $(RVFLAGS) $(TIFFINC) $(IODEFINES) -o $@ rviewIO.cpp + +rviewThumb.o: rviewThumb.cpp wx_pixmap.h + $(CC) -c $(RVFLAGS) -o $@ rviewThumb.cpp + +rviewSound.o: rviewSound.cpp + $(CC) -c $(RVFLAGS) $(SOUNDINC) -o $@ rviewSound.cpp + +rviewTypeMan.o: rviewTypeMan.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewTypeMan.cpp + +rviewColMap.o: rviewColMap.cpp wx_pixmap_translate.h + $(CC) -c $(RVFLAGS) -o $@ rviewColMap.cpp + +rviewOSection.o: rviewOSection.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewOSection.cpp + +rviewApp.o: rviewApp.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewApp.cpp + +rviewStrings.o: rviewStrings.cpp + $(CC) -c $(RVFLAGS) -o $@ rviewStrings.cpp + +labelManager.o: labelManager.cpp + $(CC) -c $(CPPFLAGS) -o $@ labelManager.cpp + + +# Test program +test_trans: test_trans.o $(PIXMAPLIB) + $(CC) -o $@ test_trans.o $(LINK_LIBS) \ + -lpixmap$(GUISUFFIX) + +test_trans.o: test.cpp wx_pixmap.h + $(CC) -c $(CPPFLAGS) -o $@ test.cpp + + +test_dither: test_dither.o $(PIXMAPLIB) + $(CC) -o $@ test_dither.o $(LINK_LIBS) \ + -lpixmap$(GUISUFFIX) + +test_dither.o: test.cpp wx_pixmap.h + $(CC) -c $(CPPFLAGS) -o $@ test.cpp -DTEST_QUALITY='WX_PIXFLAG_TRANSLATE | WX_PIXFLAG_DITHER' + + +test_render: test_render.o cube_render.o $(PIXMAPLIB) + $(CC) -o $@ test_render.o cube_render.o \ + $(LINK_LIBS) -lpixmap$(GUISUFFIX) + +test_render.o: test.cpp cube_render.h wx_pixmap.h + $(CC) -c $(CPPFLAGS) -o $@ test.cpp -DTEST_RENDERER + + +test_tomo: test_tomo.o cube_render.o $(PIXMAPLIB) + $(CC) -o $@ test_tomo.o cube_render.o \ + $(LINK_LIBS) -lpixmap$(GUISUFFIX) + +test_tomo.o: test.cpp cube_render.h wx_pixmap.h + $(CC) -c $(CPPFLAGS) -o $@ test.cpp -DTEST_RENDERER \ + -DSOURCE_FILE='"/home/hpwibas0/wiss/dehmel/Temp/tomo.raw"' + +test_drag: test_drag.o cube_render.o $(PIXMAPLIB) + $(CC) -o $@ test_drag.o cube_render.o \ + $(LINK_LIBS) -lpixmap$(GUISUFFIX) + +test_drag.o: test.cpp cube_render.h wx_pixmap.h + $(CC) -c $(CPPFLAGS) -o $@ test.cpp -DTEST_RENDERER -DTEST_DRAGGING + +test_tdrag: test_tdrag.o cube_render.o $(PIXMAPLIB) + $(CC) -o $@ test_tdrag.o cube_render.o \ + $(LINK_LIBS) -lpixmap$(GUISUFFIX) + +test_tdrag.o: test.cpp cube_render.h wx_pixmap.h + $(CC) -c $(CPPFLAGS) -o $@ test.cpp -DTEST_RENDERER -DTEST_DRAGGING \ + -DSOURCE_FILE='"/home/hpwibas0/wiss/dehmel/Temp/tomo.raw"' + + +# Standalone sound player for testing +splayer: splayer.o + $(CC) -o $@ splayer.o $(SOUNDLIB) + +splayer.o: rviewSound.cpp rviewSound.hh + $(CC) -c $(RVFLAGS) $(SOUNDINC) -D__HAL_ONLY__ -o $@ rviewSound.cpp + + + +# HP targets -- obsolete +rview_hp: + make rview GUISUFFIX=_hp \ + LINK_LIBS='-L$(WXDIR)/lib $(HPLDLIBS)' + +rview_pure_hp: + make rview_pure GUISUFFIX=_hp \ + LINK_LIBS='-L$(WXDIR)/lib $(HPLDLIBS)' + +test_trans_hp: + make test_trans GUISUFFIX=_hp \ + LINK_LIBS='-L$(WXDIR)/lib $(HPLDLIBS)' + +test_dither_hp: + make test_dither GUISUFFIX=_hp \ + LINK_LIBS='-L$(WXDIR)/lib $(HPLDLIBS)' + +test_render_hp: + make test_render GUISUFFIX=_hp \ + LINK_LIBS='-L$(WXDIR)/lib $(HPLDLIBS)' + +test_tomo_hp: + make test_tomo GUISUFFIX=_hp \ + LINK_LIBS='-L$(WXDIR)/lib $(HPLDLIBS)' + +test_drag_hp: + make test_drag GUISUFFIX=_hp \ + LINK_LIBS='-L$(WXDIR)/lib $(HPLDLIBS)' + +test_tdrag_hp: + make test_tdrag GUISUFFIX=_hp \ + LINK_LIBS='-L$(WXDIR)/lib $(HPLDLIBS)' + + +.PHONY: clean +clean: clean_docs + -rm -f client.* bclient.* core rview *.o lib/* wx_pixmap_* + -rm $(BASEOBJECTS) $(RVIEWOBJECTS) $(NVIEWOBJECTS) +ifeq ($(OSTYPE),$(OSTYPE_LINUX)) + -rm -f *.rpo +endif + + +# Main library objects +lib: + -mkdir lib + +# Renderer C-Lib. +cube_render.o: cube_render.c cube_render.h cube_render_core.c \ + cube_render_line.c cube_render_voxline.c \ + cube_render_mesh.c + $(CC) -c $(CFLAGS) -o $@ cube_render.c + + +# Documentation; use only the targets beginning with docs* + +# Dependency from rview.dvi makes sure that rview.dvi is created anew when +# rview.tex has changed. The shell-script repeats the latex runs until no +# more label changes occur. +docs: rview.dvi + # grep always throws an error :-( + -if [ -f rview.log ]; then \ + rerun=`grep -c $(TEX_RERUN_CHECK) rview.log`; \ + else \ + rerun=1; \ + fi; \ + while [ "$$rerun" -ne 0 ]; do \ + latex rview.tex; \ + rerun=`grep -c $(TEX_RERUN_CHECK) rview.log`; \ + done + +# Normal PS-output, 1:1 +docs_ps: rview.ps + +# 2 up landscape PS output +docs_ps2: docs_ps rview2.ps + +# 2 up landscape and every other page rotated by 180 degrees (suitable for duplex printers) +docs_ps2m: docs_ps2 rview2m.ps + +clean_docs: + -rm rview.aux rview.dvi rview.log rview*.ps + + +# Don't use these targets directly +rview.dvi: rview.tex + latex rview.tex + +rview.ps: docs + dvips -D600 rview.dvi + +rview2.ps: docs + -dvidvi '2:0(0in,0in),1(8in,0in)' rview.dvi rview.dvi2 + dvips -D600 -x 667 -t landscape rview.dvi2 + mv rview.ps rview2.ps + rm rview.dvi2 + +rview2m.ps: docs_ps2 + pstops "2:0@1.00(0cm,0cm),1U@1.00(21cm,29cm)" rview2.ps rview2m.ps + + +# PIXMAP_LIBRARY for wxWindows +# The sources consist of the regular source files wx_pixmap.cpp and +# wx_pixmap.h and several source files automatically generated by the +# script generate_trans.sh which creates the sources depending on +# low-level machine specifics. Thus do not edit wx_pixmap_* by hand. + +PIXOBJECTS = wx_pixmap.o wx_pixmap_translate.o + +# The script auto-creating translation- and dithering sources. +GENSCRIPT = generate_trans.sh + +# Parameters for the generating script are: (0 = lsb, 1 = msb) +# (bitorder \in [0,1]), (byteorder \in [0,1]), (palette_fill \in [0,3]) +# palette_fill gives the format of RGB in a 32bit word, bit0 describes +# the fill order, bit1 the colour order: 0:0bgr 1:bgr0 2:0rgb 3:rgb0 +# Use 1 1 0 for Sun Solaris, 0 0 2 for WindowsNT +ifneq ($(OSTYPE),linux-gnu) +GENCONFIG = 1 1 0 +else +GENCONFIG = 0 0 2 +endif + +pixmaplib: $(PIXMAPLIB) $(PIXMAPSHLIB) + +$(PIXMAPLIB): $(PIXOBJECTS) + -mkdir lib + ar $(AROPTIONS) $@ $(PIXOBJECTS) + $(RANLIB) $@ + +$(PIXMAPSHLIB): $(PIXOBJECTS) + -mkdir lib + $(BUILDSHLIB) $@ $(PIXOBJECTS) + +wx_pixmap.o: wx_pixmap.cpp wx_pixmap.h wx_pixmap_translate.h \ + wx_pixmap_dither.cpp wx_pixmap_dither.h + $(CC) -c $(CPPFLAGS) $(INC) -o $@ wx_pixmap.cpp + +wx_pixmap_translate.o: wx_pixmap_translate.c wx_pixmap_translate.h + $(CC) -c $(CFLAGS) -o $@ wx_pixmap_translate.c + +wx_pixmap.h: wx_pixmap_translate.h wx_pixmap_dither.h + +wx_pixmap_translate.c: $(GENSCRIPT) + $(GENSCRIPT) $(GENCONFIG) + +wx_pixmap_translate.h: $(GENSCRIPT) + $(GENSCRIPT) $(GENCONFIG) + +wx_pixmap_dither.cpp: $(GENSCRIPT) + $(GENSCRIPT) $(GENCONFIG) + +wx_pixmap_dither.h: $(GENSCRIPT) + $(GENSCRIPT) $(GENCONFIG) + +# this is not appropriate here, Makefile.rel is for relational stuff! -- PB 2003-aug-29 +# general rules +# include $(RMANBASE)/Makefile.rel + +# dependencies +include Makefile.dep diff --git a/applications/rview/cube_render.c b/applications/rview/cube_render.c new file mode 100644 index 0000000..250afce --- /dev/null +++ b/applications/rview/cube_render.c @@ -0,0 +1,3106 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * SOURCE: cube_render.c + * + * MODULE: applications/rview + * + * PURPOSE: + * Renderers for RasDaMan MDD of various base types. Renderers provided + * are: + * 3D: surface ( RenderCubeSurf() ), voxel ( RenderCubeVoxel() ) + * 2D: height-fields ( RenderHeightField() ). + * Misc primitives: lines ( RenderLineSegment() ), shaded polyings using + * a Z-Buffer ( RenderShadedPolygon() ). + * + * This file includes cube_render_line.c, cube_render_core.c, + * cube_render_voxline.c and cube_render_mesh.c to build renderers + * for different base types. + * + * The renderer module is standalone and can be used independently + * from rView. In the rView project it's used by rviewImage. + * + * COMMENTS: + * No comments + * + * BUGS: + */ + + +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include <limits.h> +#include <float.h> + + +#include "cube_render.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* Internally visible structs and declarations */ + +/* 0 for LSB, 1 for MSB */ +#ifdef LITTLE_ENDIAN +#define MACHINE_BYTE_ORDER 0 +#else +#define MACHINE_BYTE_ORDER 1 +#endif + +#define FIXPOINT_PREC 16 + + +/* Flags used in faces */ +#define CUBE_RENDER_FACE_HIDDEN 1 + + +/* Set debug level */ +#ifndef CUBE_RENDER_DEBUG +#define CUBE_RENDER_DEBUG 0 +#endif + +/* 8 bit values */ +typedef char int8; +typedef unsigned char uint8; + +/* 16 bit values */ +typedef short int16; +typedef unsigned short uint16; + +/* 32bit values */ +typedef long int32; +typedef unsigned long uint32; + +/* RGB data */ +typedef struct rgb_pixel { + uint8 r, g, b; +} rgb_pixel; + + + +#define MAXIMUM_SEGMENTS 7 +/* + * For voxel rendering: holds global and texture coordinates for one segment of a plane. + */ +typedef struct project_plane_intersect { + vertex_fp left_g; + vertex_fp right_g; + vertex_fp left_t; + vertex_fp right_t; + vertex_fp deltaLR_g; + vertex_fp deltaLR_t; + long left_p; + long right_p; +} project_plane_intersect; + +/* + * For approximating the normal when voxel rendering + */ +typedef struct norm_kernel_desc { + int region; + real_t *kernel; +} norm_kernel_desc; + +/* + * For voxel rendering: holds all global and texture coordinates for all segments of a + * plane, the number of planes, the scanline extent and additional rendering parameters. + */ +typedef struct project_plane_desc { + project_plane_intersect ppi[MAXIMUM_SEGMENTS]; + long left_p, right_p; + int segs; + unsigned long pixelThresholdLow; /* Minimum brightness threshold. Default 4. */ + unsigned long pixelThresholdHigh; /* Same for upper threshold. Default: type's maximum value */ + unsigned long weightThreshold; /* Terminate depth scan when this weight is reached. Default 64 */ + double pixelThresholdLowF; /* The same for FP types */ + double pixelThresholdHighF; + double weightThresholdF; + int weightQuantisation; /* log2 of weight quantisation steps */ + real_t zpro; + vertex_fp lights; + real_t lightsAmbient; + real_t lightsCos; + real_t lightsScale; + real_t lightsScintCos; + real_t lightsScintScale; + norm_kernel_desc *kDesc; + void *voxColour; /* For shading isosurfaces: each voxel gets same colour */ +} project_plane_desc; + + + +static void RenderCubeDumpFaces(const face *faces, int first, int last); +static void RenderCubeGetScanline(int ys, int faceNo, render_desc *renderDesc, int scaleTexture); + +/* Surface-oriented rendering cores */ +static void RenderCubeCore1(int faceNo, render_desc *renderDesc); +static void RenderCubeCore2(int faceNo, render_desc *renderDesc); +static void RenderCubeCore3(int faceNo, render_desc *renderDesc); +static void RenderCubeCore4(int faceNo, render_desc *renderDesc); +static void RenderCubeCore8(int faceNo, render_desc *renderDesc); + +/* Voxel-oriented rendering cores */ +static void RenderCubeVoxLine1(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine2(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine3(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine3B(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine4(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine4F(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine8F(int line, project_plane_desc *ppd, const render_desc *renderDesc); + + + + +#define sqr(x) (x)*(x) +#define RENDER_SWAP(x,y,h) h=x; x=y; y=h; + + +/* Number of vertices reserved per cube face (including x/z-clipping and appended 1st). + Each clipping pass can introduce a new vertex ==> 6, append first ==> 7. */ +#define VERTICES_PER_FACE 7 +/* The same for the clipped face. */ +#define VERTICES_CLIP_FACE 8 + +#define VERTICES_TOTAL (6*VERTICES_PER_FACE + VERTICES_CLIP_FACE) + + + + +#if (MACHINE_BYTE_ORDER == 0) +#define CUBE_RENDER_BSHIFT 0 +#define CUBE_RENDER_BSTEP 8 +#define CUBE_RENDER_SSHIFT 0 +#define CUBE_RENDER_SSTEP 16 +#else +#define CUBE_RENDER_BSHIFT 24 +#define CUBE_RENDER_BSTEP -8 +#define CUBE_RENDER_SSHIFT 16 +#define CUBE_RENDER_SSTEP -16 +#endif + + + + + +/* For easier writing down the cube initialisation. First coordinate is the constant. + Have to use vectors for initialising! */ +#define INIT_CUBE(x,y,z,u,v,w) \ + currentv[0].x = u.x; currentv[0].y = u.y; currentv[0].z = u.z; \ + currentv[1].x = u.x + w.x; currentv[1].y = u.y + w.y; currentv[1].z = u.z + w.z; \ + currentv[2].x = u.x + v.x + w.x; currentv[2].y = u.y + v.y + w.y; currentv[2].z = u.z + v.z + w.z; \ + currentv[3].x = u.x + v.x; currentv[3].y = u.y + v.y; currentv[3].z = u.z + v.z; \ + currentv += 7; + +/* Initialise the texture base vectors. They have to be normalised in such a way that + when multiplied with the cube's base-vectors the result is the cube texture's + base vector (i.e. the texture dimension). Therefore divide by L_2^2 instead of L_2. */ +#define INIT_TEXBASE(i,c) \ + l = sqr((real_t)(geomData[i].x)) + sqr((real_t)(geomData[i].y)) + sqr((real_t)(geomData[i].z)); \ + if (l > 0) { \ + h = ((real_t)(texDesc->c) / l); \ + t[i-1].x = h*geomData[i].x; t[i-1].y = h*geomData[i].y; t[i-1].z = h*geomData[i].z; \ + } \ + else { \ + return -1; \ + } + +/* Init a bounding box structure */ +#define INIT_BBOX(root) root minx = INT_MAX; root maxx = INT_MIN; \ + root miny = INT_MAX; root maxy = INT_MIN; + +/* Update the bounding box */ +#define UPDATE_BBOX(root,x,y) if (root minx > x) {root minx = x;} \ + if (root maxx < x) {root maxx = x;} \ + if (root miny > y) {root miny = y;} \ + if (root maxy < y) {root maxy = y;} + + + + + + + +/* Global vars */ +static norm_kernel_desc NormalizeKernelHomo = {-1, NULL}; +static norm_kernel_desc NormalizeKernelLinear = {-1, NULL}; +static norm_kernel_desc NormalizeKernelGauss = {-1, NULL}; +static norm_kernel_desc NormalizeKernelDummy = {0, NULL}; + + + +/* + * For debugging purposes: dumps all faces to stdout. + */ +#if (CUBE_RENDER_DEBUG > 0) +static void RenderCubeDumpFaces(const face *faces, int first, int last) +{ + int i, j; + + for (i=first; i<=last; i++) + { + printf("Face #%d\n", i); + for (j=0; j<faces[i].vertices; j++) + { + printf("\t(%f, %f, %f) : (%d, %d)\n", + faces[i].first[j].x, faces[i].first[j].y, faces[i].first[j].z, + faces[i].first_p[j].x, faces[i].first_p[j].y); + } + if (faces[i].vertices > 0) + { + printf("\t[%f, %f, %f] : [%d, %d]\n", + faces[i].first[j].x, faces[i].first[j].y, faces[i].first[j].z, + faces[i].first_p[j].x, faces[i].first_p[j].y); + } + printf("\n"); + } + fflush(stdout); +} +#endif + + + +/* + * Calculate the intersection of the current scanplane with the face number faceNo. + * If found the translation to texture coordinates is also performed here. + */ + +static void RenderCubeGetScanline(int ys, int faceNo, render_desc *renderDesc, int scaleTexture) +{ + int j; + face *cface; + real_t h; + real_t x, y, z, zpro, scanLine; + long xp; + vertex_fp *vx, *t, *sg, *st; + vertex_p *vxp; + + scanLine = (real_t)ys; + renderDesc->found = 0; zpro = (real_t)(renderDesc->graphEnv->zpro); + cface = renderDesc->faces + faceNo; + renderDesc->left_p = INT_MAX; renderDesc->right_p = INT_MIN; + + for (j=0, vx=cface->first, vxp=cface->first_p; j<cface->vertices; j++, vx++, vxp++) + { + /* Does this line segment intersect with the scanplane? */ + if (((vxp[1].y <= ys) && (vxp[0].y >= ys)) || + ((vxp[1].y >= ys) && (vxp[0].y <= ys))) + { + h = (vx[1].y - vx[0].y) * zpro - (vx[1].z - vx[0].z) * scanLine; + if (h != 0) + { + h = - (vx[0].y * zpro - vx[0].z * scanLine) / h; + } + /* Make sure it's in range (rounding errors could result in problems) */ + if (h < 0.0) h = 0.0; + if (h > 1.0) h = 1.0; + x = vx[0].x + h * (vx[1].x - vx[0].x); + y = vx[0].y + h * (vx[1].y - vx[0].y); + z = vx[0].z + h * (vx[1].z - vx[0].z); + xp = (long)((zpro * x) / z + 0.5); + + if (xp < renderDesc->left_p) + { + renderDesc->left_p = xp; + sg = &(renderDesc->left_g); sg->x = x; sg->y = y; sg->z = z; + renderDesc->found++; + } + if (xp > renderDesc->right_p) + { + renderDesc->right_p = xp; + sg = &(renderDesc->right_g); sg->x = x; sg->y = y; sg->z = z; + renderDesc->found++; + } + } + } + if (renderDesc->found == 0) return; + + /* Calculate the texel positions only when the texture descriptor is defined */ + if (renderDesc->texDesc != NULL) + { + vx = &(renderDesc->org); t = renderDesc->texbase; + /* Calculate the corresponding texel positions by projecting the global data to the + texture base vectors */ + sg = &(renderDesc->left_g); st = &(renderDesc->left_t); + st->x = (sg->x - vx->x)*t[0].x + (sg->y - vx->y)*t[0].y + (sg->z - vx->z)*t[0].z; + st->y = (sg->x - vx->x)*t[1].x + (sg->y - vx->y)*t[1].y + (sg->z - vx->z)*t[1].z; + st->z = (sg->x - vx->x)*t[2].x + (sg->y - vx->y)*t[2].y + (sg->z - vx->z)*t[2].z; + if (scaleTexture != 0) + { + st->x *= (1<<FIXPOINT_PREC); st->y *= (1<<FIXPOINT_PREC); st->z *= (1<<FIXPOINT_PREC); + } + if (st->x < 0.0) st->x = 0.0; if (st->x > renderDesc->tmax.x) st->x = renderDesc->tmax.x; + if (st->y < 0.0) st->y = 0.0; if (st->y > renderDesc->tmax.y) st->y = renderDesc->tmax.y; + if (st->z < 0.0) st->z = 0.0; if (st->z > renderDesc->tmax.z) st->z = renderDesc->tmax.z; +#if (CUBE_RENDER_DEBUG > 1) + printf("Left (%d, %d):\t(%f, %f, %f) : (%f, %f, %f)\n", + ys, faceNo, sg->x, sg->y, sg->z, st->x, st->y, st->z); +#endif + sg = &(renderDesc->right_g); st = &(renderDesc->right_t); + st->x = (sg->x - vx->x)*t[0].x + (sg->y - vx->y)*t[0].y + (sg->z - vx->z)*t[0].z; + st->y = (sg->x - vx->x)*t[1].x + (sg->y - vx->y)*t[1].y + (sg->z - vx->z)*t[1].z; + st->z = (sg->x - vx->x)*t[2].x + (sg->y - vx->y)*t[2].y + (sg->z - vx->z)*t[2].z; + if (scaleTexture != 0) + { + st->x *= (1<<FIXPOINT_PREC); st->y *= (1<<FIXPOINT_PREC); st->z *= (1<<FIXPOINT_PREC); + } + if (st->x < 0.0) st->x = 0.0; if (st->x > renderDesc->tmax.x) st->x = renderDesc->tmax.x; + if (st->y < 0.0) st->y = 0.0; if (st->y > renderDesc->tmax.y) st->y = renderDesc->tmax.y; + if (st->z < 0.0) st->z = 0.0; if (st->z > renderDesc->tmax.z) st->z = renderDesc->tmax.z; +#if (CUBE_RENDER_DEBUG > 1) + printf("Right (%d, %d):\t(%f, %f, %f) : (%f, %f, %f)\n", + ys, faceNo, sg->x, sg->y, sg->z, st->x, st->y, st->z); +#endif + } +} + + +/* + * The core functionality of the rendering engine: each function renders all the + * visible lines. The code generated is dependent on the following makros: + * + * TEXEL_BSIZE The size in bytes of one cell. + * TEXEL_POINTER The name of the texture pointer (basetype dependent!). + * TEXEL_MULTIPLIER Used when calculating the texel position. If the base type is an atomic + * type set it to 1 and TEXEL_POINTER to the atomic type. Otherwise set + * TEXEL_POINTER to texture.c (byte) and TEXEL_MULTIPLIER to TEXEL_BSIZE. + * TEXEL_ASSIGN Used to write one texel to the next pixel and incrementing the pixel ptr. + * TEXEL_ACCU_0..3 Used to read texels into a 32 bit var for cacheing. + */ + +/* Byte-sized base type */ +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_POINTER +#undef TEXEL_MULTIPLIER +#undef TEXEL_ASSIGN +#undef TEXEL_ACCU_0 +#undef TEXEL_ACCU_1 +#undef TEXEL_ACCU_2 +#undef TEXEL_ACCU_3 +#define RENDER_CORE_NAME RenderCubeCore1 +#define TEXEL_BSIZE 1 +#define TEXEL_POINTER texture.c +#define TEXEL_MULTIPLIER 1 +#define TEXEL_ASSIGN \ + *dest.c++ = (TEXEL_FETCH); TEXEL_STEP; +#define TEXEL_ACCU_0(a) \ + a = ((TEXEL_FETCH) << CUBE_RENDER_BSHIFT); TEXEL_STEP; +#define TEXEL_ACCU_1(a) \ + a |= ((TEXEL_FETCH) << (CUBE_RENDER_BSHIFT + CUBE_RENDER_BSTEP)); TEXEL_STEP; +#define TEXEL_ACCU_2(a) \ + a |= ((TEXEL_FETCH) << (CUBE_RENDER_BSHIFT + 2*CUBE_RENDER_BSTEP)); TEXEL_STEP; +#define TEXEL_ACCU_3(a) \ + a |= ((TEXEL_FETCH) << (CUBE_RENDER_BSHIFT + 3*CUBE_RENDER_BSTEP)); TEXEL_STEP; *dest.l++ = a; +#include "cube_render_core.c" + +/* Short-sized base type */ +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_POINTER +#undef TEXEL_MULTIPLIER +#undef TEXEL_ASSIGN +#undef TEXEL_ACCU_0 +#undef TEXEL_ACCU_1 +#undef TEXEL_ACCU_2 +#undef TEXEL_ACCU_3 +#define RENDER_CORE_NAME RenderCubeCore2 +#define TEXEL_BSIZE 2 +#define TEXEL_POINTER texture.s +#define TEXEL_MULTIPLIER 1 +#define TEXEL_ASSIGN \ + *dest.s++ = (TEXEL_FETCH); TEXEL_STEP; +#define TEXEL_ACCU_0(a) \ + a = ((TEXEL_FETCH) << CUBE_RENDER_SSHIFT); TEXEL_STEP; +#define TEXEL_ACCU_1(a) \ + a |= ((TEXEL_FETCH) << (CUBE_RENDER_SSHIFT + CUBE_RENDER_SSTEP)); TEXEL_STEP; *dest.l++ = a; +#define TEXEL_ACCU_2(a) \ + a = ((TEXEL_FETCH) << CUBE_RENDER_SSHIFT); TEXEL_STEP; +#define TEXEL_ACCU_3(a) \ + a |= ((TEXEL_FETCH) << (CUBE_RENDER_SSHIFT + CUBE_RENDER_SSTEP)); TEXEL_STEP; *dest.l++ = a; +#include "cube_render_core.c" + +/* Shared by RGB, Long and double: */ +#undef TEXEL_ACCU_0 +#undef TEXEL_ACCU_1 +#undef TEXEL_ACCU_2 +#undef TEXEL_ACCU_3 +#define TEXEL_ACCU_0(a) TEXEL_ASSIGN +#define TEXEL_ACCU_1(a) TEXEL_ASSIGN +#define TEXEL_ACCU_2(a) TEXEL_ASSIGN +#define TEXEL_ACCU_3(a) TEXEL_ASSIGN + +/* RGB-sized base type */ +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_POINTER +#undef TEXEL_MULTIPLIER +#undef TEXEL_ASSIGN +#define RENDER_CORE_NAME RenderCubeCore3 +#define TEXEL_BSIZE 3 +#define TEXEL_POINTER texture.c +#define TEXEL_MULTIPLIER 3 +#define TEXEL_ASSIGN \ + auxPtr = &TEXEL_FETCH; *dest.c++ = auxPtr[0]; *dest.c++ = auxPtr[1]; *dest.c++ = auxPtr[2]; TEXEL_STEP; +#include "cube_render_core.c" + +/* Long-sized base type */ +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_POINTER +#undef TEXEL_MULTIPLIER +#undef TEXEL_ASSIGN +#define RENDER_CORE_NAME RenderCubeCore4 +#define TEXEL_BSIZE 4 +#define TEXEL_POINTER texture.l +#define TEXEL_MULTIPLIER 1 +#define TEXEL_ASSIGN \ + *dest.l++ = (TEXEL_FETCH); TEXEL_STEP; +#include "cube_render_core.c" + + +/* Double-sized base type */ +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_POINTER +#undef TEXEL_MULTIPLIER +#undef TEXEL_ASSIGN +#define RENDER_CORE_NAME RenderCubeCore8 +#define TEXEL_BSIZE 8 +#define TEXEL_POINTER texture.l +#define TEXEL_MULTIPLIER 2 +#define TEXEL_ASSIGN \ + auxPtr = &TEXEL_FETCH; *dest.l++ = auxPtr[0]; *dest.l++ = auxPtr[1]; TEXEL_STEP; +#include "cube_render_core.c" + + + + +/* + * Function builds a clipped cube into the render_desc structure. Requires faces + * and graphEnv to be set up correctly. + */ + +void RenderCubeClipCube(const vertex_fp geomData[4], render_desc *renderDesc, int removeHidden) +{ + face *faces, *cface; + vertex_fp *currentv, *nextv, *clippedv, *pool; + vertex_p *currentv_p, *nextv_p, *clippedv_p, *pool_p; + vertex_fp norm; + int i, j, k, number; + real_t l, h; + real_t z0, z1, clip; + real_t zpro; + long longvar; + graph_env *graphEnv; + + faces = renderDesc->faces; + pool = renderDesc->faces[0].first; pool_p = renderDesc->faces[0].first_p; + graphEnv = renderDesc->graphEnv; zpro = (real_t)(graphEnv->zpro); + + /* Now expand descriptor to full cube description */ + /* Keep first two vertices of each face free (-> clipping) */ + currentv = pool + 2; + /* Init 3 vertices in pool which aren't needed yet with the coordinates of + geomData[0] + geomData[i] */ + for (i=0; i<3; i++) + { + pool[VERTICES_PER_FACE*i].x = geomData[0].x + geomData[i+1].x; + pool[VERTICES_PER_FACE*i].y = geomData[0].y + geomData[i+1].y; + pool[VERTICES_PER_FACE*i].z = geomData[0].z + geomData[i+1].z; + } + INIT_CUBE(z,x,y,geomData[0],geomData[1],geomData[2]); + INIT_CUBE(z,x,y,pool[2*VERTICES_PER_FACE],geomData[2],geomData[1]); + INIT_CUBE(x,y,z,geomData[0],geomData[2],geomData[3]); + INIT_CUBE(x,y,z,pool[0],geomData[3],geomData[2]); + INIT_CUBE(y,x,z,geomData[0],geomData[3],geomData[1]); + INIT_CUBE(y,x,z,pool[VERTICES_PER_FACE],geomData[1],geomData[3]); + + for (i=0; i<7; i++) faces[i].flags = 0; + + /* Init seventh (clipz-) face. */ + faces[6].vertices = 0; + faces[6].first = pool + 6*VERTICES_PER_FACE + 1; faces[6].first_p = pool_p + 6*VERTICES_PER_FACE + 1; + + /* First pass: clipz, remove hidden surfaces. */ + for (i=0; i<6; i++) + { + cface = faces + i; + cface->vertices = 4; + cface->first = pool + i*VERTICES_PER_FACE + 2; cface->first_p = pool_p + i*VERTICES_PER_FACE + 1; + /* Mustn't remove hidden surfaces before z-clipping! Otherwise z-clipping doesn't + work right. */ + + /* Init min / max values: get bounding box of projected cube. */ + INIT_BBOX(cface->bBox.); + + /* Append first vertex */ + cface->first[4].x = cface->first[0].x; + cface->first[4].y = cface->first[0].y; + cface->first[4].z = cface->first[0].z; + + /* z-clipping (vertices is still a constant 4 here) */ + clip = (real_t)(graphEnv->clipz); nextv = cface->first; z0 = nextv->z; + currentv = nextv-1; cface->first = currentv; currentv_p = cface->first_p; + for (j=0, number=0; j<4; j++) + { + z1 = nextv[1].z; + if ((z0 >= clip) || (z1 >= clip)) + { + clippedv = NULL; + if (z0 < clip) /* clip first: just store clipped vertex */ + { + h = (real_t)(z1 - 2*clip + z0) / (z1 - z0); + /* Have to trap this case or we might get identical consecutive vertices */ + currentv->x = (real_t)(0.5*(nextv[1].x + nextv[0].x - h*(nextv[1].x - nextv[0].x))); + currentv->y = (real_t)(0.5*(nextv[1].y + nextv[0].y - h*(nextv[1].y - nextv[0].y))); + currentv->z = clip; + clippedv = currentv; clippedv_p = currentv_p; + } + else if (z1 < clip) /* clip second: store first vertex (in range) AND clipped vertex */ + { + currentv->x = nextv->x; currentv->y = nextv->y; currentv->z = nextv->z; + /* Also do the central for the first vertex projection here */ + h = (zpro / z0); + currentv_p->x = (long)(h*(currentv->x)+0.5); currentv_p->y = (long)(h*(currentv->y)+0.5); + UPDATE_BBOX(cface->bBox., currentv_p->x, currentv_p->y); + currentv++; currentv_p++; + h = (real_t)(z1 - 2*clip + z0) / (z1 - z0); + currentv->x = (real_t)(0.5*(nextv[1].x + nextv[0].x - h*(nextv[1].x - nextv[0].x))); + currentv->y = (real_t)(0.5*(nextv[1].y + nextv[0].y - h*(nextv[1].y - nextv[0].y))); + currentv->z = clip; + clippedv = currentv; clippedv_p = currentv_p; + number++; + } + else + { + currentv->x = nextv->x; currentv->y = nextv->y; currentv->z = nextv->z; + } + + /* Do central projection (have to use currentv here rather than z0!) */ + h = (zpro / currentv->z); + currentv_p->x = (long)(h*(currentv->x)+0.5); currentv_p->y = (long)(h*(currentv->y)+0.5); + UPDATE_BBOX(cface->bBox., currentv_p->x, currentv_p->y); + currentv++; currentv_p++; number++; + + /* Was a vertex clipped? Yes ==> build seventh face */ + if (clippedv != NULL) + { +#if (CUBE_RENDER_DEBUG > 1) + printf("Clipped (face %d): (%f, %f, %f)\n", i, clippedv->x, clippedv->y, clippedv->z); +#endif + /* Compare projected vertex with ones already stored. This is used because projected + vertices are integers and thus the test for equality actually means something ;-) */ + for (k=0; k<faces[6].vertices; k++) + { + if ((faces[6].first_p[k].x == clippedv_p->x) && (faces[6].first_p[k].y == clippedv_p->y)) + break; + } + /* Didn't break loop, i.e. didn't find duplicate, therefore add it */ + if (k == faces[6].vertices) + { + faces[6].first[k].x = clippedv->x; + faces[6].first[k].y = clippedv->y; + faces[6].first[k].z = clippedv->z; + faces[6].first_p[k].x = clippedv_p->x; + faces[6].first_p[k].y = clippedv_p->y; + faces[6].vertices++; + } + } + } + z0 = z1; nextv++; + } + /* In case this face isn't visible after all set its vertex count to 0 */ + if ((number < 3) || (cface->bBox.minx > graphEnv->clipr) || (cface->bBox.maxx < graphEnv->clipl) + || (cface->bBox.miny > graphEnv->clipu) || (cface->bBox.maxy < graphEnv->clipd)) + { + cface->vertices = 0; +#if (CUBE_RENDER_DEBUG > 0) + printf("Clipz (%d): %d, %d, %d, %d : %d\n", i, cface->bBox.minx, cface->bBox.miny, cface->bBox.maxx, cface->bBox.maxy, number); +#endif + } + else + { + cface->vertices = number; + /* Remove identical vertices */ + number = 1; + currentv = cface->first; nextv = currentv+1; + currentv_p = cface->first_p; nextv_p = currentv_p + 1; + for (k=1; k<cface->vertices; k++) + { + l = sqr(nextv->x - currentv->x) + + sqr(nextv->y - currentv->y) + + sqr(nextv->z - currentv->z); + if (l > 1e-3) + { + currentv++; currentv_p++; number++; + currentv->x = nextv->x; currentv->y = nextv->y; currentv->z = nextv->z; + currentv_p->x = nextv_p->x; currentv_p->y = nextv_p->y; + } + nextv++; nextv_p++; + } + + currentv = cface->first + 1; k = 2; + /* Calculate face normal. Take into account that consecutive + vertices might be identical + but sets of 3 may be linearily correlated. So loop until the + normal vector isn't of zero length. */ + do + { + norm.x = (cface->first[1].y - cface->first[0].y) * (currentv[1].z - currentv[0].z) + - (currentv[1].y - currentv[0].y) * (cface->first[1].z - cface->first[0].z); + norm.y = (cface->first[1].z - cface->first[0].z) * (currentv[1].x - currentv[0].x) + - (currentv[1].z - currentv[0].z) * (cface->first[1].x - cface->first[0].x); + norm.z = (cface->first[1].x - cface->first[0].x) * (currentv[1].y - currentv[0].y) + - (currentv[1].x - currentv[0].x) * (cface->first[1].y - cface->first[0].y); + l = sqr(norm.x) + sqr(norm.y) + sqr(norm.z); k++; currentv++; + } + while ((l == 0) && (k < number)); + + currentv = cface->first; + + /* project normal to a straight line connecting the global origin and a point on + the face's surface (use first vertex for convenience). The sign of this scalar + product determines whether the face is visible or not. */ + l = norm.x * currentv->x + norm.y * currentv->y + norm.z * currentv->z; + + if (l >= 0) + { + cface->flags |= CUBE_RENDER_FACE_HIDDEN; + } + + if (removeHidden == 0) l = -1; + + if (l < 0) + { + cface->vertices = number; + /* Append first vertex again */ + currentv = cface->first + number; currentv_p = cface->first_p + number; + currentv->x = cface->first->x; currentv->y = cface->first->y; currentv->z = cface->first->z; + currentv_p->x = cface->first_p->x; currentv_p->y = cface->first_p->y; + } + else + { + cface->vertices = 0; +#if (CUBE_RENDER_DEBUG > 0) + printf("Face invisible (%d)\n", i); +#endif + } + } + } + + /* Some code to test the ordering of the clipped face's vertices: + cface = faces + 6; cface->first = pool+42; cface->first_p = pool_p+42; + cface->vertices = 6; + for (i=0; i<6; i++) + { + cface->first[i].x = 5*i*i; cface->first[i].y = 200*i*(0.5 - (i & 1)); cface->first[i].z = 0; + } + */ + + /* If a new face had to be created due to z-clipping, vertices have to be ordered so + there are no crossing lines. The orientation is irrelevant, however, because + the clipped face is always visible and the hidden face removal has already been + done at this point. Careful, however, because the face might be completely off + screen. */ + if ((number = faces[6].vertices) >= 3) + { + cface = faces + 6; + + /* Create a bounding box for the clipped face too */ + INIT_BBOX(cface->bBox.); + for (i=0; i<number; i++) + { + UPDATE_BBOX(cface->bBox., cface->first_p[i].x, cface->first_p[i].y); + } + /* Is bbox off screen? */ + if ((cface->bBox.minx > graphEnv->clipr) || (cface->bBox.maxx < graphEnv->clipl) + || (cface->bBox.miny > graphEnv->clipu) || (cface->bBox.maxy < graphEnv->clipd)) + {number = 0; cface->vertices = 0;} + + /* Basically the clipped face can contain up to 6 vertices. If there are + only 3 nothing has to be done. */ + if (number > 3) + { + real_t tangi[VERTICES_CLIP_FACE]; /* Will contain the tangens of each face */ + + /* Calculate the tangens of each vertex relative to the first one. Since tan is + strictly monotonous in (-pi/2, pi/2) ordering by tan(x) equals ordering by x. + For 2D-coordinates just use the _projected_ coordinates rather than do an + expensive transformation into face coordinates. */ + + /* First up the anchor point has to be the leftmost or rightmost one (since tan + can't tell the difference between y/x and -y/-x). Use left. */ + longvar = INT_MAX; j = 0; + for (i=0; i<number; i++) + { + if (cface->first_p[i].x < longvar) {longvar = cface->first_p[i].x; j = i;} + } + if (j != 0) + { + RENDER_SWAP(cface->first[0].x, cface->first[j].x, z0); + RENDER_SWAP(cface->first[0].y, cface->first[j].y, z0); + RENDER_SWAP(cface->first[0].z, cface->first[j].z, z0); + RENDER_SWAP(cface->first_p[0].x, cface->first_p[j].x, longvar); + RENDER_SWAP(cface->first_p[0].y, cface->first_p[j].y, longvar); + } + /* Now calculate the tangi */ + currentv_p = cface->first_p; nextv_p = currentv_p + 1; + for (i=1; i<number; i++, nextv_p++) + { + z0 = (real_t)(nextv_p->x - currentv_p->x); + z1 = (real_t)(nextv_p->y - currentv_p->y); + if (z0 < 0.0) {z0 = -z0; z1 = -z1;} + if (z0 < 1e-6) z0 = (real_t)1e-6; /* Trap division by very small values */ + tangi[i] = (z1/z0); + } + /* Now sort (Quicksort is not a good idea with <= 6 vertices) */ + for (i=1; i < number-1; i++) + { + for (j=i+1; j < number; j++) + { + if (tangi[i] > tangi[j]) + { + RENDER_SWAP(tangi[i], tangi[j], z0); + RENDER_SWAP(cface->first[i].x, cface->first[j].x, z0); + RENDER_SWAP(cface->first[i].y, cface->first[j].y, z0); + RENDER_SWAP(cface->first[i].z, cface->first[j].z, z0); + RENDER_SWAP(cface->first_p[i].x, cface->first_p[j].x, longvar); + RENDER_SWAP(cface->first_p[i].y, cface->first_p[j].y, longvar); + } + } + } + + /*printf("Tangi (%d): ", cface->vertices); + for (i=1; i<cface->vertices; i++) + { + printf("%f ", tangi[i]); + } + printf("\n"); fflush(stdout);*/ + } + + /* Append first vertex to new face too */ + currentv = cface->first + number; currentv_p = cface->first_p + number; + currentv->x = cface->first->x; currentv->y = cface->first->y; currentv->z = cface->first->z; + currentv_p->x = cface->first_p->x; currentv_p->y = cface->first_p->y; + } + else + { + faces[6].vertices = 0; + } + +#if (CUBE_RENDER_DEBUG > 0) + printf("Pass1 -- clipz + hidden face removal:\n"); + RenderCubeDumpFaces(faces, 0, 6); +#endif + + /* Clip at the left border by calculating intersections with the clipping plane. + Since each face's bounding box has been determined above this can't eliminate + any more faces, it's purely for efficiency. */ + for (i=0; i<7; i++) + { + cface = faces + i; + if ((cface->vertices > 0) && (cface->bBox.minx < graphEnv->clipl)) + { + /* Have to build the bbox anew in that case (updated y) */ + INIT_BBOX(cface->bBox.); + + nextv = cface->first; currentv = nextv-1; cface->first = currentv; + nextv_p = cface->first_p; currentv_p = nextv_p-1; cface->first_p = currentv_p; + z0 = (real_t)(nextv_p->x); + clip = (real_t)(graphEnv->clipl) - 0.5f; /* -0.5 is vital here or you get frayed edges. */ + for (j=0, number=0; j<cface->vertices; j++) + { + z1 = (real_t)(nextv_p[1].x); + if ((z0 >= clip) || (z1 >= clip)) + { + /* In case both >= clip or the 2nd vertex lies outside the range: copy 1st */ + if (z0 >= clip) + { + currentv->x = nextv->x; currentv->y = nextv->y; currentv->z = nextv->z; + currentv_p->x = nextv_p->x; currentv_p->y = nextv_p->y; + UPDATE_BBOX(cface->bBox., currentv_p->x, currentv_p->y); + currentv++; currentv_p++; number++; + } + /* Next the actual clipping */ + if ((z0 < clip) || (z1 < clip)) + { + l = ((nextv[1].x - nextv[0].x) * zpro - (nextv[1].z - nextv[0].z) * clip); + if (l == 0.0) {h = 0.0;} + else + { + h = (nextv->x * zpro - nextv->z * clip) / l; + } + currentv->x = nextv[0].x - h*(nextv[1].x - nextv[0].x); + currentv->y = nextv[0].y - h*(nextv[1].y - nextv[0].y); + currentv->z = nextv[0].z - h*(nextv[1].z - nextv[0].z); + currentv_p->x = graphEnv->clipl; currentv_p->y = (long)((zpro * currentv->y) / currentv->z + 0.5); + UPDATE_BBOX(cface->bBox., currentv_p->x, currentv_p->y); + currentv++; currentv_p++; number++; + } + } + z0 = z1; nextv++; nextv_p++; + } + cface->vertices = number; + /* Append first vertex */ + currentv->x = cface->first->x; currentv->y = cface->first->y; currentv->z = cface->first->z; + currentv_p->x = cface->first_p->x; currentv_p->y = cface->first_p->y; + } + + if (cface->vertices > 0) + { + /* Restrict bBox y values (do that _after_ x-clipping) */ + if (cface->bBox.miny < graphEnv->clipd) {cface->bBox.miny = graphEnv->clipd;} + if (cface->bBox.maxy > graphEnv->clipu) {cface->bBox.maxy = graphEnv->clipu;} + +#if (CUBE_RENDER_DEBUG > 0) + printf("Bounding Box: %d, %d, %d, %d\n", + cface->bBox.minx, cface->bBox.miny, cface->bBox.maxx, cface->bBox.maxy); +#endif + } + } + +#if (CUBE_RENDER_DEBUG > 0) + printf("Pass2 -- clipx\n"); + RenderCubeDumpFaces(faces, 0, 6); +#endif +} + + + + +/* + * Initialise texture descriptor and transform matrix + */ +static int CubeRenderInitTextures(const vertex_fp *geomData, const tex_desc *texDesc, render_desc *renderDesc, int scaleTexture) +{ + real_t l, h; + vertex_fp *t; + + t = renderDesc->texbase; + + /* Calculate the texture base vectors */ + INIT_TEXBASE(1,widthx); + INIT_TEXBASE(2,widthy); + INIT_TEXBASE(3,widthz); + + /* Highest legal value of texture coordinates scaled by fixpoint precision. + It's _terribly_ important where you subtract a fraction! You can get + _incredible_ distortions by subtracting before shifting. + Also the amount to subtract has to be set relative to the texture + dimensions because if the subtracted value is too small relative to + the number's size you get cases where (x - y) == x (e.g. dim = 300 + and real_t = float). */ + if (scaleTexture == 0) + { + renderDesc->tmax.x = ((real_t)(texDesc->widthx)) - 0.5f; + renderDesc->tmax.y = ((real_t)(texDesc->widthy)) - 0.5f; + renderDesc->tmax.z = ((real_t)(texDesc->widthz)) - 0.5f; + } + else + { + renderDesc->tmax.x = (real_t)((texDesc->widthx << FIXPOINT_PREC) - texDesc->widthx / 64.0); + renderDesc->tmax.y = (real_t)((texDesc->widthy << FIXPOINT_PREC) - texDesc->widthy / 64.0); + renderDesc->tmax.z = (real_t)((texDesc->widthz << FIXPOINT_PREC) - texDesc->widthz / 64.0); + } + + /*printf("%f,%f,%f\n", renderDesc->tmax.x, renderDesc->tmax.y, renderDesc->tmax.z);*/ + + renderDesc->texDesc = (tex_desc *)texDesc; + + return 0; +} + + + + +/* + * Render the bounding box. + */ +static void RenderCubeBBox(const render_desc *renderDesc, int hidden) +{ + int i, j; + unsigned int flagval; + face *faces; + graph_env *graphEnv; + + if (hidden == 0) flagval = 0; else flagval = CUBE_RENDER_FACE_HIDDEN; + faces = renderDesc->faces; + graphEnv = renderDesc->graphEnv; + + for (i=0; i<7; i++) + { + if ((faces[i].vertices != 0) && ((faces[i].flags & CUBE_RENDER_FACE_HIDDEN) == flagval)) + { + for (j=0; j<faces[i].vertices; j++) + { + RenderLineSegment(faces[i].first_p + j, faces[i].first_p + (j+1), (render_desc*)renderDesc, graphEnv->bbox_colour); + } + } + } +} + + + +/* + * Main function to render a cube in 3D. + * geomData contains four vertices: the origin (0) and the three base vectors. + * graphEnv describes where the plot goes to and what clippling should be applied. + * texDesc is the descriptor of the texture data (the cube's contents). + */ + +int RenderCubeSurf(const vertex_fp geomData[4], const graph_env *graphEnv, const tex_desc *texDesc) +{ + vertex_fp t[3]; + face faces[7]; /* For each face */ + vertex_fp pool[VERTICES_TOTAL]; + vertex_p pool_p[VERTICES_TOTAL]; +#if (CUBE_RENDER_DEBUG > 0) + vertex_fp *currentv, *nextv; + vertex_p *currentv_p, *nextv_p; + int j; + long longvar; +#endif + /*real_t l, h;*/ + int i; + render_desc renderDesc; + + renderDesc.texbase = t; + + CubeRenderInitTextures(geomData, texDesc, &renderDesc, 1); + +#if (CUBE_RENDER_DEBUG > 0) + /* Projections of the base vectors on the texture base vectors: */ + for (i=0; i<3; i++) + { + longvar = (long)(t[i].x * (real_t)(geomData[i+1].x) + t[i].y * (real_t)(geomData[i+1].y) + t[i].z * (real_t)(geomData[i+1].z)); + printf("texbase %d: (%f, %f, %f), max tex: %ld\n", i, t[i].x, t[i].y, t[i].z, longvar); + } + /* Scalar products of the texture base vectors (how orthogonal are they?) */ + for (i=0; i<2; i++) + { + for (j=i+1; j<3; j++) + { + printf("t_%d*t_%d: %g ", i, j, t[i].x*t[j].x + t[i].y*t[j].y + t[i].z*t[j].z); + } + } + printf("\n"); fflush(stdout); + /* Projection of cube diagonal on texture base vectors (= translation to texture coordinates) */ + pool->x = geomData[0].x + geomData[1].x + geomData[2].x + geomData[3].x; + pool->y = geomData[0].y + geomData[1].y + geomData[2].y + geomData[3].y; + pool->z = geomData[0].z + geomData[1].z + geomData[2].z + geomData[3].z; + printf("pool: %f, %f, %f\n", pool->x, pool->y, pool->z); + for (i=0; i<3; i++) + { + printf("tex_%d = %f ", i, ((pool->x - geomData[0].x)*t[i].x + (pool->y - geomData[0].y)*t[i].y + (pool->z - geomData[0].z)*t[i].z)); + } + printf("\n"); fflush(stdout); +#endif + + /* Set up rendering descriptor */ + renderDesc.faces = faces; + renderDesc.faces[0].first = pool; renderDesc.faces[0].first_p = pool_p; + renderDesc.graphEnv = (graph_env *)graphEnv; + + /* Set up descriptor structure for scanline analyser */ + renderDesc.org.x = (real_t)(geomData[0].x); + renderDesc.org.y = (real_t)(geomData[0].y); + renderDesc.org.z = (real_t)(geomData[0].z); + + RenderCubeClipCube(geomData, &renderDesc, 1); + + /*for (i=0; i<7; i++) + { + j = faces[i].first + faces[i].vertices - pool; + if ((j >= VERTICES_TOTAL) || (j < 0)) {printf("ARGHH1 (%d: %d)!!!", i, j); fflush(stdout); exit(0);} + j = faces[i].first_p + faces[i].vertices - pool_p; + if ((j >= VERTICES_TOTAL) || (j < 0)) {printf("ARGHH2 (%d: %d)!!!", i, j); fflush(stdout); exit(0);} + }*/ + + /* Now render all faces */ + for (i=0; i<7; i++) + { + if (faces[i].vertices > 0) + { +#if (CUBE_RENDER_DEBUG > 0) + currentv = faces[i].first; nextv = currentv + faces[i].vertices; + currentv_p = faces[i].first_p; nextv_p = currentv_p + faces[i].vertices; + if ((currentv->x != nextv->x) || (currentv->y != nextv->y) || (currentv->z != nextv->z) || (currentv_p->x != nextv_p->x) || (currentv_p->y != nextv_p->y)) + { + printf("First vertex not appended correctly (%f, %f, %f : %d, %d)!\n", nextv->x, nextv->y, nextv->z, nextv_p->x, nextv_p->y); fflush(stdout); + } +#endif + switch (texDesc->baseSize) + { + case 1: RenderCubeCore1(i, &renderDesc); break; + case 2: RenderCubeCore2(i, &renderDesc); break; + case 3: RenderCubeCore3(i, &renderDesc); break; + case 4: RenderCubeCore4(i, &renderDesc); break; + case 8: RenderCubeCore8(i, &renderDesc); break; + default: fprintf(stderr, "Bad base type size (%d)\n", texDesc->baseSize); exit(-1); + } + } + } + + if ((graphEnv->bbox_colour) != 0xffffffff) + { + RenderCubeBBox(&renderDesc, 0); + } + +#if (CUBE_RENDER_DEBUG > 0) + printf("CubeRender successful.\n"); fflush(stdout); +#endif + + return(0); +} + + + + +/* + * Render one line in voxel mode for various depths + */ + +#define RENDER_CAST_TYPE uint32 + +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#define RENDER_CORE_NAME RenderCubeVoxLine1 +#define TEXEL_BSIZE 1 +#define TEXEL_FETCH (texture.c[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC))]) +#define RENDER_TABLE_TYPE texture.c +#include "cube_render_voxline.c" + +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#undef RENDER_TABLE_TYPE +#define RENDER_CORE_NAME RenderCubeVoxLine2 +#define TEXEL_BSIZE 2 +#define TEXEL_FETCH (texture.s[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC))]) +#define RENDER_TABLE_TYPE texture.s +#include "cube_render_voxline.c" + +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#undef RENDER_TABLE_TYPE +#define RENDER_CORE_NAME RenderCubeVoxLine3 +#define TEXEL_BSIZE 3 +#define TEXEL_FETCH srcPix=(texture.c + (((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC)) * 3); +#define RENDER_TABLE_TYPE texture.c +#include "cube_render_voxline.c" + +#undef RENDER_CORE_NAME +#define RENDER_CORE_NAME RenderCubeVoxLine3B +#define TEXEL_RGB_BRIGHTNESS +#include "cube_render_voxline.c" +#undef TEXEL_RGB_BRIGHTNESS + +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#undef RENDER_TABLE_TYPE +#define RENDER_CORE_NAME RenderCubeVoxLine4 +#define TEXEL_BSIZE 4 +#define TEXEL_FETCH (texture.l[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC))]) +#define RENDER_TABLE_TYPE texture.l +#include "cube_render_voxline.c" + +#undef RENDER_CAST_TYPE + +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#undef RENDER_TABLE_TYPE +#define RENDER_CAST_TYPE float +#define RENDER_FLOAT_TYPE float +#define RENDER_CORE_NAME RenderCubeVoxLine4F +#define TEXEL_BSIZE 4 +#define TEXEL_FETCH (texture.f[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC))]) +#define RENDER_TABLE_TYPE texture.f +#include "cube_render_voxline.c" + +#undef RENDER_CAST_TYPE +#undef RENDER_FLOAT_TYPE +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#undef RENDER_TABLE_TYPE +#define RENDER_CAST_TYPE double +#define RENDER_FLOAT_TYPE double +#define RENDER_CORE_NAME RenderCubeVoxLine8F +#define TEXEL_BSIZE 8 +#define TEXEL_FETCH (texture.d[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC))]) +#define RENDER_TABLE_TYPE texture.d +#include "cube_render_voxline.c" + +#undef RENDER_FLOAT_TYPE + + + + +#define RENDER_SWAP_PLANE_DESC(i,j) \ + memcpy(&aux, ppd->ppi + i, sizeof(project_plane_intersect)); \ + memcpy(ppd->ppi + i, ppd->ppi + j, sizeof(project_plane_intersect)); \ + memcpy(ppd->ppi + j, &aux, sizeof(project_plane_intersect)); + +/* sort project_plane_desc structure into front/back segments and init some members */ +static void CubeRenderNormalizePlane(project_plane_desc *ppd) +{ + int i; + long min, max; + /*int j; + unsigned long isBack; + real_t nom, den, pos, h, actual_z, zpro; + project_plane_intersect aux;*/ + + /* Eliminate segments that are parallel to the scan-beam */ + i=0; + while (i < ppd->segs) + { + if (ppd->ppi[i].left_p == ppd->ppi[i].right_p) + { + (ppd->segs)--; + if (i < ppd->segs) + { + memmove(ppd->ppi + i, ppd->ppi + (i+1), (ppd->segs - i) * sizeof(project_plane_intersect)); + } + } + else i++; + } + +#if 0 + /* Sorting doesn't work well at all... */ + for (i=0; i<ppd->segs; i++) + { + /* Use deltaLR for coordinates of middle of segment */ + ppd->ppi[i].deltaLR_g.x = (ppd->ppi[i].left_g.x + ppd->ppi[i].right_g.x) / 2; + ppd->ppi[i].deltaLR_g.y = (ppd->ppi[i].left_g.y + ppd->ppi[i].right_g.y) / 2; + ppd->ppi[i].deltaLR_g.z = (ppd->ppi[i].left_g.z + ppd->ppi[i].right_g.z) / 2; + } + + /* Leftmost segment (described by middle) to position 0 */ + for (i=1; i<ppd->segs; i++) + { + if (ppd->ppi[i].deltaLR_g.x < ppd->ppi[0].deltaLR_g.x) + { + RENDER_SWAP_PLANE_DESC(i,0); + } + } + /* Determine tan, using deltaLR_t.x */ + for (i=1; i<ppd->segs; i++) + { + h = (ppd->ppi[i].deltaLR_g.x - ppd->ppi[0].deltaLR_g.x); + if (h == 0.0) h = 1e-4; + ppd->ppi[i].deltaLR_t.x = (ppd->ppi[i].deltaLR_g.z - ppd->ppi[0].deltaLR_g.z) / h; + } + /* Sort by tan in ascending order (i.e. counter-clockwise) */ + for (i=1; i<ppd->segs-1; i++) + { + for (j=i+1; j<ppd->segs; j++) + { + if (ppd->ppi[j].deltaLR_t.x < ppd->ppi[i].deltaLR_t.x) + { + RENDER_SWAP_PLANE_DESC(i,j); + } + } + } + /* Make sure there are no gaps between segments */ + min = ppd->ppi[0].left_p; max = min; + for (i=0; i<ppd->segs; i++) + { + j = i+1; if (j >= ppd->segs) j = 0; + if ((ppd->ppi[i].left_p != ppd->ppi[j].left_p) && (ppd->ppi[i].right_p != ppd->ppi[j].right_p) + && (ppd->ppi[i].left_p != ppd->ppi[j].right_p) && (ppd->ppi[i].right_p != ppd->ppi[j].left_p)) + { + int dll, drr, dlr, drl; + + dll = ppd->ppi[i].left_p - ppd->ppi[j].left_p; if (dll < 0) dll = -dll; + drr = ppd->ppi[i].right_p - ppd->ppi[j].right_p; if (drr < 0) drr = -drr; + dlr = ppd->ppi[i].left_p - ppd->ppi[j].right_p; if (dlr < 0) dlr = -dlr; + drl = ppd->ppi[i].right_p - ppd->ppi[j].left_p; if (drl < 0) drl = -drl; + if ((dll < drr) && (dll < dlr) && (dll < drl)) + { + ppd->ppi[i].left_p = ppd->ppi[j].left_p; + } + else if ((drr < dll) && (drr < dlr) && (drr < drl)) + { + ppd->ppi[i].right_p = ppd->ppi[j].right_p; + } + else if ((dlr < dll) && (dlr < drr) && (dlr < drl)) + { + ppd->ppi[i].left_p = ppd->ppi[j].right_p; + } + else + { + ppd->ppi[i].right_p = ppd->ppi[j].left_p; + } + } + if (ppd->ppi[i].left_p < min) min = ppd->ppi[i].left_p; + if (ppd->ppi[i].right_p > max) max = ppd->ppi[i].right_p; + } + ppd->left_p = min; ppd->right_p = max; + + /* + * If the first segment is a back segment is a back segment then its left_p is identical + * to the next segment's. Since the polygon is convex there can only be two cases of + * orderings of the leftmost front / back segments. + */ + if (ppd->ppi[0].left_p == ppd->ppi[1].left_p) + { + /* First segment is back segment ==> move to end */ + memcpy(&aux, ppd->ppi + 0, sizeof(project_plane_intersect)); + memmove(ppd->ppi + 0, ppd->ppi + 1, (ppd->segs - 1) * sizeof(project_plane_intersect)); + memcpy(ppd->ppi + (ppd->segs - 1), &aux, sizeof(project_plane_intersect)); + } + + /*for (i=0; i<ppd->segs; i++) + { + printf("%f,%f,%f\n", ppd->ppi[i].deltaLR_g.x, ppd->ppi[i].deltaLR_g.y, ppd->ppi[i].deltaLR_g.z); + } + printf("\n");*/ +#endif + + min = ppd->ppi[0].left_p; max = min; + for (i=0; i<ppd->segs; i++) + { + /*printf("[%d: %d,%d] ", i, ppd->ppi[i].left_p, ppd->ppi[i].right_p);*/ + ppd->ppi[i].deltaLR_g.x = ppd->ppi[i].right_g.x - ppd->ppi[i].left_g.x; + ppd->ppi[i].deltaLR_g.y = ppd->ppi[i].right_g.y - ppd->ppi[i].left_g.y; + ppd->ppi[i].deltaLR_g.z = ppd->ppi[i].right_g.z - ppd->ppi[i].left_g.z; + ppd->ppi[i].deltaLR_t.x = ppd->ppi[i].right_t.x - ppd->ppi[i].left_t.x; + ppd->ppi[i].deltaLR_t.y = ppd->ppi[i].right_t.y - ppd->ppi[i].left_t.y; + ppd->ppi[i].deltaLR_t.z = ppd->ppi[i].right_t.z - ppd->ppi[i].left_t.z; + if (ppd->ppi[i].left_p < min) min = ppd->ppi[i].left_p; + if (ppd->ppi[i].right_p > max) max = ppd->ppi[i].right_p; + } + ppd->left_p = min; ppd->right_p = max; + /*printf("\n");*/ + + /*for (i=0; i<ppd->segs-1; i++) + { + for (j=i+1; j<ppd->segs; j++) + { + if (ppd->ppi[j].left_p < ppd->ppi[i].left_p) + { + RENDER_SWAP_PLANE_DESC(i,j); + } + } + }*/ +} + + + + +int RenderCube(const vertex_fp geomData[4], const graph_env *graphEnv, const tex_desc *texDesc) +{ + voxel_desc voxDesc; + tex_desc newTexDesc; + + voxDesc.pixelThresholdLow = (double)4; + voxDesc.pixelThresholdHigh = (double)0xffffffff; + voxDesc.weightThreshold = (double)64; + voxDesc.weightQuantisation = 4; + voxDesc.useRgbBrightness = 0; + /*voxDesc.lights.x = 0.70710678; voxDesc.lights.y = 0.70710678; voxDesc.lights.z = 0;*/ + voxDesc.light.lights.x = 512; voxDesc.light.lights.y = 512; voxDesc.light.lights.z = (real_t)(graphEnv->zpro); + voxDesc.light.ambient = 0.5; voxDesc.light.gain = 1.0; + voxDesc.light.cosine = 0.0; voxDesc.light.scintCos = 0.0; + voxDesc.kernSize = 2; voxDesc.kernType = RENDER_NORM_KERNEL_GAUSS; + voxDesc.voxColour = NULL; + memcpy(&newTexDesc, texDesc, sizeof(tex_desc)); + newTexDesc.floatType = 0; + newTexDesc.minVal = 0.0; newTexDesc.maxVal = 1e6; + return RenderCubeVoxel(geomData, graphEnv, &newTexDesc, &voxDesc); +} + + +static norm_kernel_desc *RenderCubeEnsureNormKernel(voxel_desc *voxDesc) +{ + if (voxDesc->light.ambient >= 0) + { + norm_kernel_desc *kDesc; + int ksize; + + switch (voxDesc->kernType) + { + case RENDER_NORM_KERNEL_HOMO: kDesc = &NormalizeKernelHomo; break; + case RENDER_NORM_KERNEL_LINEAR: kDesc = &NormalizeKernelLinear; break; + case RENDER_NORM_KERNEL_GAUSS: kDesc = &NormalizeKernelGauss; break; + default: kDesc = &NormalizeKernelDummy; return kDesc; + } + if (kDesc->region != voxDesc->kernSize) + { + int i, j, k; + real_t *kernel; + + if (kDesc->kernel != NULL) + { + free(kDesc->kernel); kDesc->kernel = NULL; kDesc->region = -1; + } + if ((kDesc->region = voxDesc->kernSize) == 0) return kDesc; + ksize = 2 * voxDesc->kernSize + 1; + ksize = ksize * ksize * ksize; + if ((kDesc->kernel = (real_t*)malloc(ksize * sizeof(real_t))) == NULL) + return NULL; + kernel = kDesc->kernel; + /* This doesn't have to be efficient */ + for (i=-voxDesc->kernSize; i<=voxDesc->kernSize; i++) + { + for (j=-voxDesc->kernSize; j<=voxDesc->kernSize; j++) + { + for (k=-voxDesc->kernSize; k<=voxDesc->kernSize; k++) + { + switch (voxDesc->kernType) + { + case RENDER_NORM_KERNEL_HOMO: + *kernel = 1.0; + break; + case RENDER_NORM_KERNEL_LINEAR: + *kernel = (real_t)(1.0 - sqrt(sqr(i) + sqr(j) + sqr(k)) / (sqrt(3) * voxDesc->kernSize)); + break; + case RENDER_NORM_KERNEL_GAUSS: + *kernel = (real_t)(exp(-(sqr(i) + sqr(j) + sqr(k)) / 2)); + break; + default: + break; + } + kernel++; + } + } + } + /*kernel = kDesc->kernel; + for (i=-voxDesc->kernSize; i<=voxDesc->kernSize; i++) + { + for (j=-voxDesc->kernSize; j<=voxDesc->kernSize; j++) + { + printf("%d,%d: ", i, j); + for (k=-voxDesc->kernSize; k<=voxDesc->kernSize; k++) + { + printf("%f ", *kernel); kernel++; + } + printf("\n"); + } + }*/ + } + return kDesc; + } + return NULL; +} + + + +/* + * Voxel renderer. + */ + +int RenderCubeVoxel(const vertex_fp geomData[4], const graph_env *graphEnv, const tex_desc *texDesc, voxel_desc *voxDesc) +{ + vertex_fp t[3]; + face faces[7]; + vertex_fp pool[VERTICES_TOTAL]; + vertex_p pool_p[VERTICES_TOTAL]; + project_plane_desc ppd; + int segsPerLine; + render_desc renderDesc; + long miny_p, maxy_p; + int i, j; + double h; + + /* standard renderer preamble (see RenderCube()) */ + renderDesc.texbase = t; + + CubeRenderInitTextures(geomData, texDesc, &renderDesc, 0); + + renderDesc.faces = faces; + renderDesc.faces[0].first = pool; renderDesc.faces[0].first_p = pool_p; + renderDesc.graphEnv = (graph_env *)graphEnv; + + renderDesc.org.x = (real_t)(geomData[0].x); + renderDesc.org.y = (real_t)(geomData[0].y); + renderDesc.org.z = (real_t)(geomData[0].z); + + RenderCubeClipCube(geomData, &renderDesc, 0); + + /* Determine bounding box of entire cube */ + miny_p = graphEnv->clipu + 1; + maxy_p = graphEnv->clipd - 1; + for (i=0; i<7; i++) + { + if (faces[i].vertices != 0) + { + if (faces[i].bBox.miny < miny_p) miny_p = faces[i].bBox.miny; + if (faces[i].bBox.maxy > maxy_p) maxy_p = faces[i].bBox.maxy; + } + } + /* vertical clipping */ + if ((miny_p > graphEnv->clipu) || (maxy_p < graphEnv->clipd)) return 0; + if (miny_p < graphEnv->clipd) miny_p = graphEnv->clipd; + if (maxy_p > graphEnv->clipu) maxy_p = graphEnv->clipu; + + /* Set rendering thresholds */ + if ((texDesc->floatType != 0) || (texDesc->baseSize == 8)) + { + ppd.pixelThresholdLowF = voxDesc->pixelThresholdLow; + ppd.pixelThresholdHighF = voxDesc->pixelThresholdHigh; + ppd.weightThresholdF = voxDesc->weightThreshold; + } + else + { + ppd.pixelThresholdLow = (voxDesc->pixelThresholdLow > (double)ULONG_MAX) ? ULONG_MAX : (unsigned long)(voxDesc->pixelThresholdLow); + ppd.pixelThresholdHigh = (voxDesc->pixelThresholdHigh > (double)ULONG_MAX) ? ULONG_MAX : (unsigned long)(voxDesc->pixelThresholdHigh); + ppd.weightThreshold = (voxDesc->weightThreshold > (double)ULONG_MAX) ? ULONG_MAX : (unsigned long)(voxDesc->weightThreshold); + /*printf("%ul:%f, %ul:%f, %ul:%f\n", ppd.pixelThresholdLow, voxDesc->pixelThresholdLow, ppd.pixelThresholdHigh, voxDesc->pixelThresholdHigh, ppd.weightThreshold, voxDesc->weightThreshold);*/ + } + ppd.weightQuantisation = voxDesc->weightQuantisation; + ppd.zpro = (real_t)graphEnv->zpro; + + /* + * Transform the lights into texture coordinates. This is done by applying the + * same rotations to the lightsource that are necessary to rotate the cube into + * a standard orthonormal 3D base. + */ + if ((ppd.lightsAmbient = (real_t)(voxDesc->light.ambient)) >= 0) + { + vertex_fp lights; + real_t gain; + + if ((gain = (real_t)(voxDesc->light.gain)) < 0) gain = 0.0; + if ((ppd.lightsCos = (real_t)(voxDesc->light.cosine)) > 0.999) ppd.lightsCos = 0.999f; + ppd.lightsScale = (real_t)(1 / (1.0 - ppd.lightsCos)); + if ((ppd.lightsScintCos = (real_t)(voxDesc->light.scintCos)) > 0.999) ppd.lightsScintCos = 0.999f; + ppd.lightsScintScale = (real_t)(gain / (1.0 - ppd.lightsScintCos)); + + /* Translate light coordinates into texture coordinates */ + lights.x = voxDesc->light.lights.x - geomData[0].x; + lights.y = voxDesc->light.lights.y - geomData[0].y; + lights.z = voxDesc->light.lights.z - geomData[0].z; + +#if 0 + rotation_desc rd[3]; + + RenderCubeDetermineRotation(geomData+1, rd); + + /* Rotate light into texture coordinates */ + h = rd[2].cos * lights.x + rd[2].sin * lights.y; + lights.y = -rd[2].sin * lights.x + rd[2].cos * lights.y; + lights.x = h; + h = rd[1].cos * lights.x + rd[1].sin * lights.z; + lights.z = -rd[1].sin * lights.x + rd[1].cos * lights.z; + lights.x = h; + h = rd[0].cos * lights.y + rd[0].sin * lights.z; + lights.z = -rd[0].sin * lights.y + rd[0].cos * lights.z; + lights.y = h; + + ppd.lights.x = lights.x; + ppd.lights.y = lights.y; + ppd.lights.z = lights.z; +#else + +#define PROJECT_LIGHT_SOURCE(n,c) \ + h = sqr(geomData[n].x) + sqr(geomData[n].y) + sqr(geomData[n].z); \ + if (h != 0) \ + { \ + h = 1/sqrt(h); ppd.lights.c = (real_t)(h * (geomData[n].x * (lights.x-geomData[0].x) + geomData[n].y * (lights.y-geomData[0].y) + geomData[n].z * (lights.z)-geomData[0].z)); \ + } \ + + PROJECT_LIGHT_SOURCE(1,x); + PROJECT_LIGHT_SOURCE(2,y); + PROJECT_LIGHT_SOURCE(3,z); +#endif + + ppd.kDesc = RenderCubeEnsureNormKernel(voxDesc); + } + ppd.voxColour = voxDesc->voxColour; + + /* Render hidden surfaces first */ + if (graphEnv->bbox_colour != 0xffffffff) + { + RenderCubeBBox(&renderDesc, 1); + } + + /* Now render the voxels */ + for (i=maxy_p; i>=miny_p; i--) + { + /*printf("line %d\n", i);*/ + segsPerLine = 0; + for (j=0; j<7; j++) + { + if (faces[j].vertices != 0) + { + RenderCubeGetScanline(i, j, &renderDesc, 0); + if (renderDesc.found != 0) + { + ppd.ppi[segsPerLine].left_g = renderDesc.left_g; + ppd.ppi[segsPerLine].right_g = renderDesc.right_g; + ppd.ppi[segsPerLine].left_t = renderDesc.left_t; + ppd.ppi[segsPerLine].right_t = renderDesc.right_t; + ppd.ppi[segsPerLine].left_p = renderDesc.left_p; + ppd.ppi[segsPerLine].right_p = renderDesc.right_p; + ppd.segs = ++segsPerLine; + } + } + } + /* need at least two segments for further computation */ + if (segsPerLine < 2) continue; + + CubeRenderNormalizePlane(&ppd); + + switch (texDesc->baseSize) + { + case 1: RenderCubeVoxLine1(i, &ppd, &renderDesc); break; + case 2: RenderCubeVoxLine2(i, &ppd, &renderDesc); break; + case 3: + if (voxDesc->useRgbBrightness == 0) + RenderCubeVoxLine3(i, &ppd, &renderDesc); + else + RenderCubeVoxLine3B(i, &ppd, &renderDesc); + break; + case 4: + if (texDesc->floatType == 0) + RenderCubeVoxLine4(i, &ppd, &renderDesc); + else + RenderCubeVoxLine4F(i, &ppd, &renderDesc); + break; + case 8: RenderCubeVoxLine8F(i, &ppd, &renderDesc); break; + default: fprintf(stderr, "Bad base type size (%d)\n", texDesc->baseSize); exit(-1); break; + } + } + + if (graphEnv->bbox_colour != 0xffffffff) + { + RenderCubeBBox(&renderDesc, 0); + } + +#if 0 + if (voxDesc->lightsAmbient >= 0) + { + vertex_p from, to; + + printf("Lights at %f, %f, %f\n", ppd.lights.x, ppd.lights.y, ppd.lights.z); + from.x = (geomData[0].x * graphEnv->zpro) / geomData[0].z; + from.y = (geomData[0].y * graphEnv->zpro) / geomData[0].z; + to.x = ((geomData[0].x + ppd.lights.x) * graphEnv->zpro) / (geomData[0].z + ppd.lights.z); + to.y = ((geomData[0].y + ppd.lights.y) * graphEnv->zpro) / (geomData[0].z + ppd.lights.z); + RenderLineSegment(&from, &to, &renderDesc, graphEnv->bbox_colour); + } +#endif + + return 0; +} + + + +/* + * Functions for external use of the clipped cube. + */ + +render_desc *RenderCubeBuild(const vertex_fp geomData[4], const graph_env *graphEnv) +{ + render_desc *renderDesc; + + if ((renderDesc = (render_desc *)malloc(sizeof(render_desc))) == NULL) + return NULL; + if ((renderDesc->faces = (face *)malloc(7*sizeof(face))) == NULL) + { + free(renderDesc); return NULL; + } + if ((renderDesc->faces[0].first = (vertex_fp *)malloc(VERTICES_TOTAL * sizeof(vertex_fp))) == NULL) + { + free(renderDesc->faces); free(renderDesc); return NULL; + } + if ((renderDesc->faces[0].first_p = (vertex_p *)malloc(VERTICES_TOTAL * sizeof(vertex_p))) == NULL) + { + free(renderDesc->faces[0].first); free(renderDesc->faces); free(renderDesc); return NULL; + } + renderDesc->graphEnv = (graph_env *)graphEnv; + /* Setting the texture descriptor to NULL disables the calculation of left_t, right_t in + RenderCubeGetScanline. */ + renderDesc->texDesc = NULL; + + RenderCubeClipCube(geomData, renderDesc, 1); + + return renderDesc; +} + + + +void RenderCubeFreeDesc(render_desc *renderDesc) +{ + free(renderDesc->faces[0].first_p); + free(renderDesc->faces[0].first); + free(renderDesc->faces); + free(renderDesc); +} + + + +/* + * This function rotates a cube into the standard orthonormal 3D-base and + * logs the necessary rotations. Rotation is done in z,y,x order, so in + * order to rotate a standard base such that its axes coincide with the + * input cube's the rotations have to be applied in x,y,z order with + * negative sin values. + */ +void RenderCubeDetermineRotation(const vertex_fp *base, rotation_desc *rd) +{ + vertex_fp e[3]; + double len; + double c, s, h; + int i; + + /* Normalize the base */ + for (i=0; i<3; i++) + { + len = sqr(base[i].x) + sqr(base[i].y) + sqr(base[i].z); + h = 1/sqrt(len); + e[i].x = (real_t)(h*base[i].x); e[i].y = (real_t)(h*base[i].y); e[i].z = (real_t)(h*base[i].z); + } + + /* First rotate x-vector around z into xz plane */ + len = sqr(e[0].x) + sqr(e[0].y); + if (fabs(len) <= 1e-12) + { + rd[2].sin = 0.0; rd[2].cos = 1; + } + else + { + h = 1/sqrt(len); c = h * e[0].x; s = h * e[0].y; + rd[2].sin = s; rd[2].cos = c; + for (i=0; i<3; i++) + { + h = c*e[i].x + s*e[i].y; + e[i].y = (real_t)(-s*e[i].x + c*e[i].y); + e[i].x = (real_t)h; + /*printf("RotZ: %f, %f, %f\n", e[i].x, e[i].y, e[i].z);*/ + } + } + /* Then rotate x-vector around y to x axis */ + len = sqr(e[0].x) + sqr(e[0].z); + h = 1/sqrt(len); c = h * e[0].x; s = h * e[0].z; + rd[1].sin = s; rd[1].cos = c; + for (i=0; i<3; i++) + { + h = c*e[i].x + s*e[i].z; + e[i].z = (real_t)(-s*e[i].x + c*e[i].z); + e[i].x = (real_t)h; + /*printf("RotY: %f, %f, %f\n", e[i].x, e[i].y, e[i].z);*/ + } + /* Finally rotate y-vector around x to y-axis */ + len = sqr(e[1].y) + sqr(e[1].z); + h = 1/sqrt(len); c = h * e[1].y; s = h * e[1].z; + rd[0].sin = s; rd[0].cos = c; + for (i=0; i<3; i++) + { + h = c*e[i].y + s*e[i].z; + e[i].z = (real_t)(-s*e[i].y + c*e[i].z); + e[i].y = (real_t)h; + /*printf("RotX: %f, %f, %f\n", e[i].x, e[i].y, e[i].z);*/ + } +} + + + +/* + * This function can be called to get the 3D coordinates of the intersection of a + * line from (0,0) through (x_p, y_p) and the cube. It requires renderDesc to be + * set up by the function RenderCubeBuild. + */ + +int RenderCubeGetPosition(int x_p, int y_p, vertex_fp *pos, render_desc *renderDesc) +{ + int i, status; + face *cface; + real_t h, zpro, x_r; + + status = 0; zpro = (real_t)(renderDesc->graphEnv->zpro); x_r = (real_t)x_p; + for (i=0; (i<7) && (status == 0); i++) + { + cface = renderDesc->faces + i; + if (renderDesc->faces[i].vertices > 0) + { + if ((y_p >= cface->bBox.miny) && (y_p <= cface->bBox.maxy)) + { + RenderCubeGetScanline(y_p, i, renderDesc, 1); + if (renderDesc->found != 0) + { + if ((x_p >= renderDesc->left_p) && (x_p <= renderDesc->right_p)) + { + pos->x = renderDesc->right_g.x - renderDesc->left_g.x; + pos->y = renderDesc->right_g.y - renderDesc->left_g.y; + pos->z = renderDesc->right_g.z - renderDesc->left_g.z; + h = pos->x * zpro - pos->z * x_r; + if (h != 0) + { + h = - (renderDesc->left_g.x * zpro - renderDesc->left_g.z * x_r) / h; + } + if (h < 0.0) h = 0.0; + if (h > 1.0) h = 1.0; + pos->x = renderDesc->left_g.x + h * pos->x; + pos->y = renderDesc->left_g.y + h * pos->y; + pos->z = renderDesc->left_g.z + h * pos->z; + status = 1; + } + } + } + } + } + return status; +} + + + +/* Used for plotting the lines in various depths */ +#define RENDER_LINE_CORE_H(d,col) \ + while (x0 <= x1) \ + { \ + *d = col; \ + if (h < 0) h += delta1; else {h += delta2; destPtr.c += step;} \ + x0++; d++; \ + } + +#define RENDER_LINE_CORE_V(d,col) \ + while (y0 != y1) \ + { \ + *d = col; \ + if (h < 0) h += delta1; else {h += delta2; d++;} \ + y0 += stepy; destPtr.c += step; \ + } \ + *d = col; + +/* + * Render a line (for drawing the object's bounding box). Write a line-plotter explicitly so it + * can be easily combined with the rest of the renderer. + */ + +void RenderLineSegment(const vertex_p *from, const vertex_p *to, const render_desc *renderDesc, long colour) +{ + int x0, y0, x1, y1, h, step, delta1, delta2, baseSize; + graph_env *graphEnv; + union {uint8 *c; uint16 *s; rgb_pixel *r; uint32 *l; float *f; double *d;} destPtr; + rgb_pixel rgb_colour; + + graphEnv = renderDesc->graphEnv; + baseSize = renderDesc->texDesc->baseSize; + + /* Order vertices from left to right */ + if (from->x < to->x) + { + x0 = from->x; y0 = from->y; x1 = to->x; y1 = to->y; + } + else + { + x0 = to->x; y0 = to->y; x1 = from->x; y1 = from->y; + } + /* Completely off-screen? */ + if ((x0 > graphEnv->clipr) || (x1 < graphEnv->clipl)) return; + if ((y0 > graphEnv->clipu) && (y1 > graphEnv->clipu)) return; + if ((y0 < graphEnv->clipd) && (y1 < graphEnv->clipd)) return; + + /*printf("%4d,%4d,%4d,%4d\n", x0, y0, x1, y1); fflush(stdout);*/ + + /* Horizontal clipping */ + if (x0 < graphEnv->clipl) + { + y0 = (y0 + y1 - ((y1 - y0) * (x0 - 2*(graphEnv->clipl) + x1)) / (x1 - x0)) >> 1; + x0 = graphEnv->clipl; + } + if (x1 > graphEnv->clipr) + { + y1 = (y0 + y1 - ((y1 - y0) * (x0 - 2*(graphEnv->clipr) + x1)) / (x1 - x0)) >> 1; + x1 = graphEnv->clipr; + } + + /* After horizontal clipping both y-values can be outside the clipping rectangle! */ + if ((y0 > graphEnv->clipu) && (y1 > graphEnv->clipu)) return; + if ((y0 < graphEnv->clipd) && (y1 < graphEnv->clipd)) return; + + /* Vertical clipping */ + if ((y0 > graphEnv->clipu) || (y1 > graphEnv->clipu)) + { + h = (x0 + x1 - ((x1 - x0) * (y0 - 2*(graphEnv->clipu) + y1)) / (y1 - y0)) >> 1; + if (y0 > graphEnv->clipu) + { + y0 = graphEnv->clipu; x0 = h; + } + else + { + y1 = graphEnv->clipu; x1 = h; + } + } + if ((y0 < graphEnv->clipd) || (y1 < graphEnv->clipd)) + { + h = (x0 + x1 - ((x1 - x0) * (y0 - 2*(graphEnv->clipd) + y1)) / (y1 - y0)) >> 1; + if (y0 < graphEnv->clipd) + { + y0 = graphEnv->clipd; x0 = h; + } + else + { + y1 = graphEnv->clipd; x1 = h; + } + } + + /*printf("%4d,%4d,%4d,%4d [%4d,%4d,%4d,%4d]\n", x0, y0, x1, y1, graphEnv->clipl, graphEnv->clipr, graphEnv->clipd, graphEnv->clipu); fflush(stdout);*/ + + destPtr.c = (uint8*)(graphEnv->dest) + (graphEnv->midy - y0) * (graphEnv->lineadd) + + (graphEnv->midx + x0) * baseSize; + + /* vertical stepping in negative logical coordinates steps in positive physical coordinates */ + step = (y1 < y0) ? graphEnv->lineadd : -graphEnv->lineadd; + + if (baseSize == 3) + { + rgb_colour.r = colour & 0xff; rgb_colour.g = (colour >> 8) & 0xff; rgb_colour.b = (colour >> 16) & 0xff; + } + + /* dx is always positive. dy isn't */ + h = (y1 - y0); if (h < 0) h = -h; + if ((x1 - x0) >= h) + { + delta1 = 2*(y1 - y0); if (delta1 < 0) delta1 = -delta1; + delta2 = delta1 - 2*(x1 - x0); + h = delta1 - (x1 - x0); + + switch (baseSize) + { + case 1: + { + RENDER_LINE_CORE_H(destPtr.c, (uint8)colour); + } + break; + case 2: + { + RENDER_LINE_CORE_H(destPtr.s, (uint16)colour); + } + break; + case 3: + { + RENDER_LINE_CORE_H(destPtr.r, rgb_colour); + } + break; + case 4: + if ((renderDesc->texDesc == NULL) || (renderDesc->texDesc->floatType == 0)) + { + RENDER_LINE_CORE_H(destPtr.l, (uint32)colour); + } + else + { + float fpcol = (float)(renderDesc->texDesc->maxVal); + RENDER_LINE_CORE_H(destPtr.f, fpcol); + } + break; + case 8: + { + double fpcol = (double)(renderDesc->texDesc->maxVal); + RENDER_LINE_CORE_H(destPtr.d, fpcol); + } + break; + default: break; + } + } + else + { + int stepy; + + stepy = (y1 < y0) ? -1 : 1; + delta1 = 2*(x1 - x0); + h = (y1 - y0); if (h < 0) h = -h; + delta2 = delta1 - 2*h; + h = delta1 - h; + + switch (baseSize) + { + case 1: + { + RENDER_LINE_CORE_V(destPtr.c, (uint8)colour); + } + break; + case 2: + { + RENDER_LINE_CORE_V(destPtr.s, (uint16)colour); + } + break; + case 3: + { + RENDER_LINE_CORE_V(destPtr.r, rgb_colour); + } + break; + case 4: + if ((renderDesc->texDesc == NULL) || (renderDesc->texDesc->floatType == 0)) + { + RENDER_LINE_CORE_V(destPtr.l, (uint32)colour); + } + else + { + float fpcol = (float)(renderDesc->texDesc->maxVal); + RENDER_LINE_CORE_V(destPtr.f, fpcol); + } + break; + case 8: + { + double fpcol = (double)(renderDesc->texDesc->maxVal); + RENDER_LINE_CORE_V(destPtr.d, fpcol); + } + break; + default: break; + } + } +} + + + +void Render3DLine(const vertex_fp *from, const vertex_fp *to, const render_desc *renderDesc, long colour) +{ + const graph_env *ge = renderDesc->graphEnv; + + if ((from->z >= ge->clipz) || (to->z >= ge->clipz)) + { + vertex_fp v1, v2; + vertex_p p1, p2; + double h; + + memcpy(&v1, from, sizeof(vertex_fp)); + memcpy(&v2, to, sizeof(vertex_fp)); + + /* do z-clipping if necessary */ + if ((from->z < ge->clipz) || (to->z < ge->clipz)) + { + vertex_fp *cv; + + h = (-v1.z + 2*ge->clipz - v2.z) / (v2.z - v1.z); + cv = (from->z < ge->clipz) ? &v1 : &v2; + cv->x = 0.5*(v1.x + v2.x + h * (v2.x - v1.x)); + cv->y = 0.5*(v1.y + v2.y + h * (v2.y - v1.y)); + cv->z = ge->clipz; + } + + /* project to 2D coordinates */ + h = (ge->zpro) / v1.z; + p1.x = (long)(h * v1.x); + p1.y = (long)(h * v1.y); + h = (ge->zpro) / v2.z; + p2.x = (long)(h * v2.x); + p2.y = (long)(h * v2.y); + + /* and finally render it */ + RenderLineSegment(&p1, &p2, renderDesc, colour); + } +} + + + + + +/* + * Shaded polygons + */ + +#define POLYSHADE_BUILD_GREY_FULL \ + { \ + real_t lcos, scos; \ + uint32 pixel; \ + lcos = cospos - lcosine; if (lcos < 0.0) lcos = 0.0; else lcos *= lweight; \ + scos = cospos - scosine; if (scos < 0.0) scos = 0.0; else scos *= sweight; \ + pixel = (uint32)(colour * (ambient + (1-ambient)*lcos + gain*scos)); \ + *destPtr = (pixel > 0xff) ? 0xff : (uint8)pixel; \ + } + +#define POLYSHADE_BUILD_GREY_FAST \ + *destPtr = (uint8)((colour * (cospos + 1)) / 2); + +#define POLYSHADE_BUILD_RGB_FULL \ + { \ + real_t lcos, scos; \ + uint32 pixel; \ + lcos = cospos - lcosine; if (lcos < 0.0) lcos = 0.0; else lcos *= lweight; \ + scos = cospos - scosine; if (scos < 0.0) scos = 0.0; else scos *= sweight; \ + lcos = (ambient + (1-ambient)*lcos); scos = gain*scos*colgrey; \ + pixel = (uint32)( red * lcos + scos); destPtr[0] = (pixel > 0xff) ? 0xff : (uint8)pixel; \ + pixel = (uint32)(green * lcos + scos); destPtr[1] = (pixel > 0xff) ? 0xff : (uint8)pixel; \ + pixel = (uint32)( blue * lcos + scos); destPtr[2] = (pixel > 0xff) ? 0xff : (uint8)pixel; \ + } + +#define POLYSHADE_BUILD_RGB_FAST \ + { \ + real_t lw = (cospos+1)/2; \ + uint32 cc; \ + cc = (uint32)(red * lw); destPtr[0] = (uint8)cc; \ + cc = (uint32)(green * lw); destPtr[1] = (uint8)cc; \ + cc = (uint32)(blue * lw); destPtr[2] = (uint8)cc; \ + } + +#ifdef POLYSHADE_RENDER_EXPERIMENTAL + +#define INIT_POLYSHADE_SIDE(side) \ + side##_ctr = proj[side##_idx].y - proj[side##_idx+1].y; \ + h = (real_t)((side##_ctr == 0) ? 1.0 : (1.0 / side##_ctr)); \ + side##_dp = h * (proj[side##_idx+1].x - proj[side##_idx].x); \ + side##_dc = h * (cosine[side##_idx+1] - cosine[side##_idx]); \ + if (side##_ctr < 0) \ + { \ + side##_p = (real_t)(proj[side##_idx+1].x) + 0.5; side##_c = cosine[side##_idx+1]; \ + side##_ctr = -side##_ctr; \ + } \ + else \ + { \ + side##_p = (real_t)(proj[side##_idx].x) + 0.5; side##_c = cosine[side##_idx]; \ + } + +#define UPDATE_POLYSHADE_SIDE(side) \ + if (side##_ctr-- <= 0) \ + { \ + side##_idx += side##_step; \ + if (side##_idx < 0) side##_idx = number-1; \ + if (side##_idx >= number) side##_idx = 0; \ + INIT_POLYSHADE_SIDE(side) \ + } \ + else \ + { \ + side##_p += side##_dp; side##_c += side##_dc; \ + } + +#define POLYSHADE_LINE_LOOP(PIXSIZE, BUILD_PIXEL) \ + destLine = (uint8*)(graphEnv->dest) + (graphEnv->midy - maxy) * (graphEnv->lineadd) + (graphEnv->midx) * PIXSIZE; \ + for (i=maxy; i>=miny; i--) \ + { \ + int xlp, xrp; \ + xlp = (int)left_p; \ + if (xlp <= graphEnv->clipr) \ + { \ + xrp = (int)right_p; \ + if (xrp >= graphEnv->clipl) \ + { \ + real_t cospos, cosstep; \ + if (xlp > xrp) {printf("l %d, x %d %d, i %d %d\n", i, xlp, xrp, left_idx, right_idx); for (n=0; n<number; n++) printf("%d: %f,%f,%f: %d %d\n", n, vert[n].x, vert[n].y, vert[n].z, proj[n].x, proj[n].y); xrp = (int)left_p; xlp = (int)right_p;} \ + h = (real_t)((xrp == xlp) ? 1 : xrp - xlp); \ + cospos = left_c; cosstep = (right_c - left_c) / h; \ + if (xlp < graphEnv->clipl) \ + { \ + cospos += cosstep * (graphEnv->clipl - xlp); xlp = graphEnv->clipl; \ + } \ + if (xrp > graphEnv->clipr) xrp = graphEnv->clipr; \ + destPtr = destLine + xlp * PIXSIZE; \ + for (; xlp <= xrp; xlp++, destPtr += PIXSIZE) \ + { \ + BUILD_PIXEL \ + cospos += cosstep; \ + } \ + } \ + } \ + destLine += graphEnv->lineadd; \ + UPDATE_POLYSHADE_SIDE(left) \ + UPDATE_POLYSHADE_SIDE(right) \ + } + +#else + +#define POLYSHADE_LINE_LOOP(PIXSIZE, BUILD_PIXEL) \ + destLine = (uint8*)(graphEnv->dest) + (graphEnv->midy - maxy) * (graphEnv->lineadd) + (graphEnv->midx) * PIXSIZE; \ + for (i=(maxy-miny); i>=0; i--, destLine += graphEnv->lineadd, zbLine += zbWidth) \ + { \ + int xlp, xrp; \ + real_t cospos, cosstep; \ + int zpos, zstep; \ + xlp = PolyShadeLines[i].left.xp >> FIXPOINT_PREC; xrp = PolyShadeLines[i].right.xp >> FIXPOINT_PREC; \ + if ((xlp <= graphEnv->clipr) && (xrp >= graphEnv->clipl)) \ + { \ + cospos = PolyShadeLines[i].left.c; zpos = PolyShadeLines[i].left.z; \ + h = (real_t)((xlp == xrp) ? 1.0 : 1.0 / (xrp - xlp)); \ + cosstep = h * (PolyShadeLines[i].right.c - cospos); \ + zstep = (int)(h * (PolyShadeLines[i].right.z - zpos)); \ + if (xlp < graphEnv->clipl) {cospos += (graphEnv->clipl - xlp) * cosstep; zpos += (graphEnv->clipl - xlp) * zstep; xlp = graphEnv->clipl;} \ + if (xrp > graphEnv->clipr) xrp = graphEnv->clipr; \ + destPtr = destLine + xlp * PIXSIZE; zbPtr = zbLine + xlp; \ + for (; xlp <= xrp; xlp++, destPtr += PIXSIZE, zbPtr++, cospos += cosstep, zpos += zstep) \ + { \ + if ((zbuffer_t)(zpos >> FIXPOINT_PREC) < *zbPtr) \ + { \ + *zbPtr = (zbuffer_t)(zpos >> FIXPOINT_PREC); \ + BUILD_PIXEL \ + } \ + } \ + } \ + } + + +typedef struct polyshade_side_t { + int xp; + int z; + real_t c; +} polyshade_side_t; + +typedef struct polyshade_line_t { + polyshade_side_t left; + polyshade_side_t right; +} polyshade_line_t; + +static int PolyShadeLineSize = 0; +static polyshade_line_t *PolyShadeLines = NULL; + + +static int RenderInitZBuffer(const graph_env *graphEnv, mesh_desc *meshDesc) +{ + int width; + unsigned int total; + + width = graphEnv->clipr - graphEnv->clipl + 1; + total = width * (graphEnv->clipu - graphEnv->clipd + 1); + if (meshDesc->zbuffSize != total) + { + if (meshDesc->zbuffer != NULL) free(meshDesc->zbuffer); + meshDesc->zbuffSize = total; + meshDesc->zbuffer = (zbuffer_t*)malloc(total * sizeof(zbuffer_t)); + } + memset(meshDesc->zbuffer, 0xff, total * sizeof(zbuffer_t)); + + return width; +} + +#endif + +/* Render a (convex!) polygon using shading */ +int RenderShadedPolygon(int numVert, const vertex_fp *vertices, const vertex_fp *normals, unsigned int colour, const graph_env *graphEnv, const light_desc *light, const vertex_fp *real_norm, zbuffer_t *zbuffer) +{ + vertex_fp *vert, *vr, *vw; + vertex_p *proj, *pr, *pw; + real_t *cosine, *cr, *cw; + real_t clipz, zpro; + real_t h, orient; + int miny, maxy, minx, maxx, lastY; + int number; + int i, n; +#ifdef POLYSHADE_RENDER_EXPERIMENTAL + int left_idx, right_idx, left_step, right_step; + real_t left_p, left_dp, right_p, right_dp; + real_t left_c, left_dc, right_c, right_dc; + int left_ctr, right_ctr; + vertex_fp dir1, dir2, snorm; + const vertex_fp *snormp; +#endif + uint8 *destLine, *destPtr; + uint8 red, green, blue; + real_t lcosine, scosine, lweight, sweight, ambient, gain; + /* zBuffer */ + zbuffer_t *zbLine, *zbPtr; + int zbWidth; + + clipz = (real_t)(graphEnv->clipz); zpro = (real_t)(graphEnv->zpro); + + /* Early termination of off-screen polygons */ + minx = INT_MAX; maxx = INT_MIN; miny = INT_MAX; maxy = INT_MIN; + for (i=0; i<numVert; i++) + { + int xp, yp; + + /* The effects of this can't be predicted without explicit z-clipping */ + if (vertices[i].z <= 0) break; + h = zpro / vertices[i].z; + xp = (int)(h * vertices[i].x); yp = (int)(h * vertices[i].y); + if (xp < minx) minx = xp; if (xp > maxx) maxx = xp; + if (yp < miny) miny = yp; if (yp > maxy) maxy = yp; + } + if (i >= numVert) + { + if ((maxx < graphEnv->clipl) || (minx > graphEnv->clipr) || (maxy < graphEnv->clipd) || (miny > graphEnv->clipu)) return 0; + } + + /* +1 vertex for closing the outline +3 vertices for clipping effects (2 y, 1 z) */ + vert = (vertex_fp*)malloc((numVert + 4) * sizeof(vertex_fp)); + proj = (vertex_p*)malloc((numVert + 4) * sizeof(vertex_p)); + cosine = (real_t*)malloc((numVert + 4) * sizeof(real_t)); + + vr = vert+3; memcpy(vr, vertices, numVert*sizeof(vertex_fp)); + vr[numVert] = vertices[0]; + cw = cosine+3; + + /* Surface orientation just doesn't work well */ +#if 0 + if (real_norm == NULL) + { + /* Determine surface orientation */ + dir2.x = vertices[2].x - vertices[1].x; dir1.x = vertices[1].x - vertices[0].x; + dir2.y = vertices[2].y - vertices[1].y; dir1.y = vertices[1].y - vertices[0].y; + dir2.z = vertices[2].z - vertices[1].z; dir1.z = vertices[1].z - vertices[0].z; + snorm.x = dir1.y * dir2.z - dir1.z * dir2.y; + snorm.y = dir1.z * dir2.x - dir1.x * dir2.z; + snorm.z = dir1.x * dir2.y - dir1.y * dir2.x; /* signed surface normal */ + snormp = &snorm; + } + else + { + snormp = real_norm; + } + /* If the side facing us is the backside, mirror the normal vectors */ + h = snormp->x * vertices[0].x + snormp->y * vertices[0].y + snormp->z * vertices[0].z; + if (h < 0) orient = -1; else orient = 1; +#else + orient = 1; +#endif + /* Calculate the scalar products of the vertices with the light vector */ + for (i=0; i<numVert; i++) + { + vertex_fp l; + + l.x = (light->lights.x - vertices[i].x); + l.y = (light->lights.y - vertices[i].y); + l.z = (light->lights.z - vertices[i].z); + h = l.x * l.x + l.y * l.y + l.z * l.z; + if (h < 1e-6) h = 1e-6f; + h = (real_t)(orient/sqrt(h)); + cw[i] = h * ((normals[i].x * l.x) + (normals[i].y * l.y) + (normals[i].z * l.z)); + } + cw[i] = cw[0]; + + number = 0; cr = cosine+3; vw = vert+2; cw = cosine+2; pw = proj+2; + + /* z clipping */ + for (i=0; i<numVert; i++) + { + if ((vr[0].z >= clipz) || (vr[1].z >= clipz)) + { + if (vr[0].z >= clipz) + { + *vw++ = *vr; *cw++ = *cr; + pw->x = (long)((zpro * vr->x) / vr->z); pw->y = (long)((zpro * vr->y) / vr->z); pw++; + number++; if (number >= numVert+1) break; + } + if ((vr[0].z < clipz) || (vr[1].z < clipz)) + { + h = (vr[0].z - 2 * clipz + vr[1].z) / (vr[1].z - vr[0].z); + vw->x = (real_t)(0.5*(vr[1].x + vr[0].x - h * (vr[1].x - vr[0].x))); + vw->y = (real_t)(0.5*(vr[1].y + vr[0].y - h * (vr[1].y - vr[0].y))); + vw->z = clipz; + *cw = (real_t)(0.5*(cr[1] + cr[0] - h * (cr[1] - cr[0]))); + vw++; cw++; + pw->x = (long)((zpro * vr->x) / vr->z); pw->y = (long)((zpro * vr->y) / vr->z); pw++; + number++; if (number >= numVert+1) break; + } + } + vr++; cr++; + } + if (number == 0) + { +#if (CUBE_RENDER_DEBUG > 0) + printf("PolyShade: void (z)\n"); +#endif + free(vert); free(cosine); free(proj); + return 0; + } + vr = vert+2; cr = cosine+2; pr = proj+2; *vw = *vr; *cw = *cr; *pw = *pr; + +#if (CUBE_RENDER_DEBUG > 0) + for (i=0; i<number; i++) printf("%d: %f %f %f\n", i, vr[i].x, vr[i].y, vr[i].z); +#endif + + miny = INT_MAX; maxy = INT_MIN; minx = INT_MAX; maxx = INT_MIN; + /* y clipping */ + vw = vert; cw = cosine; pw = proj; n = 0; +#if 1 + lastY = (int)((zpro * vr[0].y) / vr[0].z); + for (i=0; i<number; i++) + { + vertex_fp v0, v1, *uv; + real_t c0, c1, *uc; + int modTwo; + int yp; + + yp = (int)((zpro * vr[1].y) / vr[1].z); + v0 = vr[0]; v1 = vr[1]; c0 = cr[0]; c1 = cr[1]; modTwo = 0; + /* Clip upper bound */ + if ((lastY <= graphEnv->clipu) || (yp <= graphEnv->clipu)) + { + if ((lastY > graphEnv->clipu) || (yp > graphEnv->clipu)) + { + h = (real_t)(zpro * (v1.y - v0.y) - (graphEnv->clipu + 0.5) * (v1.z - v0.z)); + if (h != 0.0) + { + h = (real_t)( - (zpro * v0.y - (graphEnv->clipu + 0.5) * v0.z) / h); + if (lastY > graphEnv->clipu) + { + uv = &v0; uc = &c0; + } + else + { + uv = &v1; uc = &c1; modTwo = 1; + } + uv->x = v0.x + h * (v1.x - v0.x); + uv->y = v0.y + h * (v1.y - v0.y); + uv->z = v0.z + h * (v1.z - v0.z); + *uc = c0 + h * (c1 - c0); + } + } + } + if ((lastY >= graphEnv->clipd) || (yp >= graphEnv->clipd)) + { + if ((lastY < graphEnv->clipd) || (yp < graphEnv->clipd)) + { + h = (real_t)(zpro * (v1.y - v0.y) - (graphEnv->clipd - 0.5) * (v1.z - v0.z)); + if (h != 0.0) + { + h = (real_t)( - (zpro * v0.y - (graphEnv->clipd - 0.5) * v0.z) / h); + if (lastY < graphEnv->clipd) + { + uv = &v0; uc = &c0; + } + else + { + uv = &v1; uc = &c1; modTwo = 1; + } + uv->x = v0.x + h * (v1.x - v0.x); + uv->y = v0.y + h * (v1.y - v0.y); + uv->z = v0.z + h * (v1.z - v0.z); + *uc = c0 + h * (c1 - c0); + } + } + } + if (((lastY <= graphEnv->clipu) || (yp <= graphEnv->clipu)) && ((lastY >= graphEnv->clipd) || (yp >= graphEnv->clipd))) + { + *vw++ = v0; *cw++ = c0; + pw->x = (long)((zpro * v0.x) / v0.z); pw->y = (long)((zpro * v0.y) / v0.z); + if (pw->y < miny) miny = pw->y; if (pw->y > maxy) maxy = pw->y; + if (pw->x < minx) minx = pw->x; if (pw->x > maxx) maxx = pw->x; + pw++; n++; if (n >= numVert+3) break; + if (modTwo != 0) + { + *vw++ = v1; *cw++ = c1; + pw->x = (long)((zpro * v1.x) / v1.z); pw->y = (long)((zpro * v1.y) / v1.z); + if (pw->y < miny) miny = pw->y; if (pw->y > maxy) maxy = pw->y; + if (pw->x < minx) minx = pw->x; if (pw->x > maxx) maxx = pw->x; + pw++; n++; if (n >= numVert+3) break; + } + } + vr++; cr++; lastY = yp; + } +#else + lastY = pr[0].y; + for (i=0; i<number; i++) + { + vertex_p p0, p1, *up; + real_t c0, c1, *uc; + int modTwo; + int yp; + + yp = pr[1].y; + p0 = pr[0]; p1 = pr[1]; c0 = cr[0]; c1 = cr[1]; modTwo = 0; + if ((lastY <= graphEnv->clipu) || (yp <= graphEnv->clipu)) + { + if ((lastY > graphEnv->clipu) || (yp > graphEnv->clipu)) + { + h = (yp - 2 * graphEnv->clipu - 1 + lastY) / (yp - lastY); + if (lastY > graphEnv->clipu) + { + up = &p0; uc = &c0; + } + else + { + up = &p1; uc = &c1; modTwo = 1; + } + up->x = (int)(0.5*(p1.x + p0.x - h * (p1.x - p0.x))); + up->y = graphEnv->clipu; + *uc = 0.5*(c1 + c0 - h * (c1 - c0)); + } + } + if ((lastY >= graphEnv->clipd) || (yp >= graphEnv->clipd)) + { + if ((lastY < graphEnv->clipd) || (yp < graphEnv->clipd)) + { + h = (yp - 2 * graphEnv->clipd + 1 + lastY) / (yp - lastY); + if (lastY < graphEnv->clipd) + { + up = &p0; uc = &c0; + } + else + { + up = &p1; uc = &c1; modTwo = 1; + } + up->x = (int)(0.5*(p1.x + p0.x - h * (p1.x - p0.x))); + up->y = graphEnv->clipd; + *uc = 0.5*(c1 + c0 - h * (c1 - c0)); + } + } + if (((lastY <= graphEnv->clipu) || (yp <= graphEnv->clipu)) && ((lastY >= graphEnv->clipd) || (yp >= graphEnv->clipd))) + { + *pw++ = p0; *cw++ = c0; n++; + if (p0.y < miny) miny = p0.y; if (p0.y > maxy) maxy = p0.y; + if (p0.x < minx) minx = p0.x; if (p0.x > maxx) maxx = p0.x; + if (n >= numVert+3) break; + if (modTwo != 0) + { + *pw++ = p1; *cw++ = c1; n++; + if (p1.y < miny) miny = p1.y; if (p1.y > maxy) maxy = p1.y; + if (p1/x < minx) minx = p1.x; if (p1.x > maxx) maxx = p1.x; + if (n >= numVert+3) break; + } + } + pr++; vr++; + } +#endif + if (n == 0) + { +#if (CUBE_RENDER_DEBUG > 0) + printf("PolyShade: void (y)\n"); +#endif + free(vert); free(cosine); free(proj); + return 0; + } + if ((minx > graphEnv->clipr) || (maxx < graphEnv->clipl)) + { +#if (CUBE_RENDER_DEBUG > 0) + printf("PolyShade: clipped all x\n"); +#endif + free(vert); free(cosine); free(proj); + return 0; + } + *vw = *vert; *cw = *cosine; *pw = *proj; + number = n; + +#if (CUBE_RENDER_DEBUG > 0) + printf("miny %d, maxy %d\n", miny, maxy); + for (i=0; i<number; i++) printf("%d: %f %f %f : %d %d\n", i, vert[i].x, vert[i].y, vert[i].z, proj[i].x, proj[i].y); +#endif + + /* Init lighting parameters */ + if ((ambient = (real_t)(light->ambient)) >= 0) + { + lcosine = (real_t)(light->cosine); scosine = (real_t)(light->scintCos); + lweight = (lcosine == 1) ? 1 : 1 / (1 - lcosine); + sweight = (scosine == 0) ? 1 : 1 / (1 - scosine); + gain = (real_t)(light->gain); + } + +#ifdef POLYSHADE_RENDER_EXPERIMENTAL + /* Find the two topmost, non-horizontal bounding lines */ + left_idx = -1; right_idx = -1; + for (n=0; n<number; n++) + { + if (((proj[n].y == maxy) || (proj[n+1].y == maxy)) && (proj[n].y != proj[n+1].y)) + { + if (left_idx == -1) left_idx = n; else right_idx = n; + } + } + /* is ``polygon'' completely flat? Then just find leftmost / rightmost line */ + if (right_idx < 0) + { + left_idx = 0; right_idx = 0; + for (i=1; i<number; i++) + { + if (proj[i].x < proj[left_idx].x) left_idx = i; + if (proj[i].x > proj[right_idx].x) right_idx = i; + } + } +#if 0 /* Can't happen any more */ + /* Did something serious go wrong? */ + if (right_idx < 0) + { +#if (CUBE_RENDER_DEBUG > 0) + fprintf(stderr, "Bad polygon:\n"); + for (i=0; i<number; i++) + { + fprintf(stderr, "\t%d: v(%f,%f,%f), p(%4d,%4d), c%f\n", i, vert[i].x, vert[i].y, vert[i].z, proj[i].x, proj[i].y, cosine[i]); + } +#endif + free(vert); free(cosine); free(proj); + return -1; + } +#endif + /* Determine surface orientation */ +#if 1 + h = snorm.x * vert[0].x + snorm.y * vert[0].y + snorm.z * vert[0].z; +#else + dir1.x = vert[1].x / vert[1].z; dir1.y = vert[1].y / vert[1].z; + dir2.x = vert[2].x / vert[2].z - dir1.x; dir2.y = vert[2].y / vert[2].z - dir1.y; + dir1.x -= vert[0].x / vert[0].z; dir1.y -= vert[0].y / vert[0].z; + h = dir1.x * dir2.y - dir1.y * dir2.x; +#endif + if (h <= 0) /* clockwise */ + { + /* left side must have growing yp component */ + if (proj[left_idx].y > proj[left_idx+1].y) + { + left_step = left_idx; left_idx = right_idx; right_idx = left_step; + } + left_step = -1; right_step = 1; + } + else /* counter-clockwise */ + { + /* left side must have decreasing yp component */ + if (proj[left_idx].y < proj[left_idx+1].y) + { + left_step = left_idx; left_idx = right_idx; right_idx = left_step; + } + left_step = 1; right_step = -1; + } + + if (miny < graphEnv->clipd) miny = graphEnv->clipd; + if (maxy > graphEnv->clipu) maxy = graphEnv->clipu; + + /*for (i=0; i<number; i++) {int xlp, ylp; xlp = (zpro*vert[i].x)/vert[i].z; ylp = (zpro*vert[i].y)/vert[i].z; if ((xlp != proj[i].x) || (ylp != proj[i].y)) printf("No match %d:%d,%d -- %d,%d\n", i, xlp, ylp, proj[i].x, proj[i].y);}*/ + + INIT_POLYSHADE_SIDE(left) + INIT_POLYSHADE_SIDE(right) + +#if (CUBE_RENDER_DEBUG > 0) + printf("left %d, right %d; ls %d, rs %d; lc %d, rc %d\n", left_idx, right_idx, left_step, right_step, left_ctr, right_ctr); +#endif + + /* Top bits of colour determine rgb / grey mode */ + if ((colour & 0xff000000) != 0) + { + red = colour & 0xff; green = (colour >> 8) & 0xff; blue = (colour >> 16) & 0xff; + POLYSHADE_LINE_LOOP(3, POLYSHADE_BUILD_RGB_FAST) + } + else + { + POLYSHADE_LINE_LOOP(1, POLYSHADE_BUILD_GREY_FAST) + } + +#else + + if (PolyShadeLineSize < (maxy-miny+1)) + { + PolyShadeLineSize = (maxy-miny+1); + if (PolyShadeLines != NULL) free(PolyShadeLines); + PolyShadeLines = (polyshade_line_t*)malloc(PolyShadeLineSize * sizeof(polyshade_line_t)); + } + + for (i=(maxy-miny); i>=0; i--) + { + PolyShadeLines[i].left.xp = INT_MAX; PolyShadeLines[i].right.xp = INT_MIN; + } + + zbWidth = (graphEnv->clipr - graphEnv->clipl + 1); + zbLine = zbuffer + ((graphEnv->midy - maxy) * zbWidth + graphEnv->midx); + + /* Plot the outline into this structure */ + for (n=0; n<number; n++) + { + polyshade_line_t *pl; + int xp, xpstep; + real_t c, cstep; + int z, zstep; + int yp, count, step; + + xp = proj[n].x << FIXPOINT_PREC; yp = proj[n].y; c = cosine[n]; z = (int)((1<<FIXPOINT_PREC)*vert[n].z); + if ((count = proj[n+1].y - yp) < 0) {count = -count; step = -1;} else {step = 1;} + h = (real_t)((count == 0) ? 1.0 : 1.0 / ((real_t)count)); + xpstep = (int)(h * ((proj[n+1].x << FIXPOINT_PREC) - xp)); + cstep = h * (cosine[n+1] - c); zstep = (int)(h * (1<<FIXPOINT_PREC) * (vert[n+1].z - vert[n].z)); + pl = PolyShadeLines + (yp - miny); + if (xp < pl->left.xp) + { + pl->left.xp = xp; pl->left.c = c; pl->left.z = z; + } + if (xp > pl->right.xp) + { + pl->right.xp = xp; pl->right.c = c; pl->right.z = z; + } + while (count-- > 0) + { + pl += step; xp += xpstep; c += cstep; z += zstep; + if (xp < pl->left.xp) + { + pl->left.xp = xp; pl->left.c = c; pl->left.z = z; + } + if (xp > pl->right.xp) + { + pl->right.xp = xp; pl->right.c = c; pl->right.z = z; + } + } + } + /* Top bits of colour determine rgb / grey mode */ + if ((colour & 0xff000000) != 0) + { + uint32 colgrey; + + red = colour & 0xff; green = (colour >> 8) & 0xff; blue = (colour >> 16) & 0xff; + colgrey = (red + green + blue) / 3; + if (ambient < 0) + { + POLYSHADE_LINE_LOOP(3, POLYSHADE_BUILD_RGB_FAST) + } + else + { + POLYSHADE_LINE_LOOP(3, POLYSHADE_BUILD_RGB_FULL) + } + } + else + { + if (ambient < 0) + { + POLYSHADE_LINE_LOOP(1, POLYSHADE_BUILD_GREY_FAST) + } + else + { + POLYSHADE_LINE_LOOP(1, POLYSHADE_BUILD_GREY_FULL) + } + } +#endif + + free(vert); free(proj); free(cosine); + + return 0; +} + + + +#define RENDER_MESH_NAME RenderHeightMesh1 +#define RENDER_MESH_TYPE c +#include "cube_render_mesh.c" +#undef RENDER_MESH_NAME +#undef RENDER_MESH_TYPE + +#define RENDER_MESH_NAME RenderHeightMesh2 +#define RENDER_MESH_TYPE s +#include "cube_render_mesh.c" +#undef RENDER_MESH_NAME +#undef RENDER_MESH_TYPE + +#define RENDER_MESH_NAME RenderHeightMesh4 +#define RENDER_MESH_TYPE l +#include "cube_render_mesh.c" +#undef RENDER_MESH_NAME +#undef RENDER_MESH_TYPE + +#define RENDER_MESH_NAME RenderHeightMesh4F +#define RENDER_MESH_TYPE f +#include "cube_render_mesh.c" +#undef RENDER_MESH_NAME +#undef RENDER_MESH_TYPE + +#define RENDER_MESH_NAME RenderHeightMesh8F +#define RENDER_MESH_TYPE d +#include "cube_render_mesh.c" +#undef RENDER_MESH_NAME +#undef RENDER_MESH_TYPE + +int RenderHeightField(mesh_desc *meshDesc, const vertex_fp *rotTrans, const graph_env *graphEnv, const mdd_desc *mddDesc, const light_desc *lightDesc) +{ + int i, j, dimx, dimz; + vertex_fp *vert, *vp, *vbase; + vertex_fp *norm, *np, *nbase; + int number; + int srcIncX, srcIncZ; + uint8 *src; + real_t h, hh; + real_t x0, x1, x2, x3; + real_t z0, z1, z2, z3; + int backoff, backstep, sideoff, sidestep, iter0, iter1; + zbuffer_t *zbuffer; +#ifdef POLYSHADE_HEIGHT_EXPERIMENTAL + int indices[4][8]; +#endif + + if (RenderHeightGetDomain(mddDesc, &dimx, &dimz, &srcIncX, &srcIncZ) != 0) + return -1; + + number = (dimx * dimz); + vert = (vertex_fp*)malloc(number * sizeof(vertex_fp)); + norm = (vertex_fp*)malloc(number * sizeof(vertex_fp)); + + /* Only create a new mesh if it has changed */ + if ((meshDesc->srcData != mddDesc->data) || (meshDesc->width != dimx) || (meshDesc->height != dimz) || (meshDesc->oldGrid != meshDesc->scaleGrid) || (meshDesc->oldHeight != meshDesc->scaleHeight)) + { + if (meshDesc->vert != NULL) free(meshDesc->vert); + if (meshDesc->norm != NULL) free(meshDesc->norm); + + meshDesc->vert = (vertex_fp*)malloc(number * sizeof(vertex_fp)); + meshDesc->norm = (vertex_fp*)malloc(number * sizeof(vertex_fp)); + + /* Create a vertex mesh (type-dependent) */ + src = (uint8*)(mddDesc->data); + + vp = meshDesc->vert; + switch (mddDesc->baseSize) + { + case 1: RenderHeightMesh1(vp, src, srcIncX, srcIncZ, dimx, dimz, meshDesc->scaleGrid, meshDesc->scaleHeight); + break; + case 2: RenderHeightMesh2(vp, src, srcIncX, srcIncZ, dimx, dimz, meshDesc->scaleGrid, meshDesc->scaleHeight); + break; + case 4: + if (mddDesc->floatType == 0) + RenderHeightMesh4(vp, src, srcIncX, srcIncZ, dimx, dimz, meshDesc->scaleGrid, meshDesc->scaleHeight); + else + RenderHeightMesh4F(vp, src, srcIncX, srcIncZ, dimx, dimz, meshDesc->scaleGrid, meshDesc->scaleHeight); + break; + case 8: RenderHeightMesh8F(vp, src, srcIncX, srcIncZ, dimx, dimz, meshDesc->scaleGrid, meshDesc->scaleHeight); + break; + default: + fprintf(stderr, "Unable to render a height field for this base type.\n"); + free(vert); free(norm); return -1; + } + + /* Construct normal vector approximations */ + vp = meshDesc->vert; np = meshDesc->norm; z0 = vp[0].y; z1 = z0; + for (i = 0; i < dimx; i++) + { + for (j = 0; j < dimz; j++) + { + vertex_fp v; + /*real_t vy;*/ + + /* Determine min/max */ + if (vp->y < z0) z0 = vp->y; + if (vp->y > z1) z1 = vp->y; + /* Calculate normal */ + if (i == 0) v.x = -(real_t)(vp[dimz].y - vp[0].y); + else if (i == dimx-1) v.x = -(real_t)(vp[0].y - vp[-dimz].y); + else v.x = (real_t)( - (vp[dimz].y - vp[-dimz].y) * 0.5); + v.y = 2 * meshDesc->scaleGrid; + if (j == 0) v.z = -(real_t)(vp[1].y - vp[0].y); + else if (j == dimz-1) v.z = -(real_t)(vp[0].y - vp[-1].y); + else v.z = (real_t)( - (vp[1].y - vp[-1].y) * 0.5); + h = (real_t)(1 / sqrt((v.x) * (v.x) + (v.y) * (v.y) + (v.z) * (v.z))); /* v.y != 0 ! */ + np->x = h * v.x; np->y = h * v.y; np->z = h * v.z; + vp++; np++; + } + } + meshDesc->miny = z0; meshDesc->maxy = z1; + meshDesc->srcData = mddDesc->data; + meshDesc->width = dimx; meshDesc->height = dimz; + meshDesc->oldGrid = meshDesc->scaleGrid; meshDesc->oldHeight = meshDesc->scaleHeight; + } + + memcpy(vert, meshDesc->vert, number * sizeof(vertex_fp)); + memcpy(norm, meshDesc->norm, number * sizeof(vertex_fp)); + + z0 = (meshDesc->miny + meshDesc->maxy) / 2; + z1 = (real_t)(rotTrans[0].y - 0.5*(rotTrans[2].x * dimx * meshDesc->scaleGrid + rotTrans[2].z * dimz * meshDesc->scaleGrid)); + + /* Transform vertices and normals */ + vp = meshDesc->vert; np = meshDesc->norm; + for (i=0; i<dimx*dimz; i++, vp++, np++) + { + z2 = vp->y - z0; + vert[i].x = rotTrans[1].x * vp->x + rotTrans[1].y * z2 + rotTrans[1].z * vp->z + rotTrans[0].x; + vert[i].y = rotTrans[2].x * vp->x + rotTrans[2].y * z2 + rotTrans[2].z * vp->z + z1; + vert[i].z = rotTrans[3].x * vp->x + rotTrans[3].y * z2 + rotTrans[3].z * vp->z + rotTrans[0].z; + norm[i].x = rotTrans[1].x * np->x + rotTrans[1].y * np->y + rotTrans[1].z * np->z; + norm[i].y = rotTrans[2].x * np->x + rotTrans[2].y * np->y + rotTrans[2].z * np->z; + norm[i].z = rotTrans[3].x * np->x + rotTrans[3].y * np->y + rotTrans[3].z * np->z; + } + + + /* + * Find order in which to plot: 1) find hindmost side of the grid using min((dz/dx)^2), + * 2) test left/right z of this side for running order in inner loop. + */ + x0 = vert[0].x; x1 = vert[dimz * (dimx-1)].x; x2 = vert[dimz * dimx - 1].x; x3 = vert[dimz-1].x; + z0 = vert[0].z; z1 = vert[dimz * (dimx-1)].z; z2 = vert[dimz * dimx - 1].z; z3 = vert[dimz-1].z; + /* Hindmost corner 0 */ + if ((z0 >= z1) && (z0 >= z2) && (z0 >= z3)) + { + /* (dz0/dx0)^2 < (dz1/dx1)^2 <==> (dz0dx1)^2 < (dz1dx0)^2 */ + h = (z1 - z0) * (x3 - x0); hh = (z3 - z0) * (x1 - x0); + if (h*h < hh*hh) /* back is 01 */ + { + backoff = 0; backstep = 1; iter0 = dimz; + sideoff = 0; sidestep = dimz; + } + else /* back is 30 */ + { + backoff = 0; backstep = dimz; iter0 = dimx; + sideoff = 0; sidestep = 1; + } + } + /* Hindmost corner 1 */ + else if ((z1 >= z0) && (z1 >= z2) && (z1 >= z3)) + { + h = (z2 - z1) * (x0 - x1); hh = (x2 - x1) * (z0 - z1); + if (h*h < hh*hh) /* back is 12 */ + { + backoff = (dimx-2)*dimz; backstep = -dimz; iter0 = dimx; + sideoff = 0; sidestep = 1; + } + else /* back is 01 */ + { + backoff = 0; backstep = 1; iter0 = dimz; + sideoff = (dimx-2)*dimz; sidestep = -dimz; + } + } + /* Hindmost corner 2 */ + else if ((z2 >= z0) && (z2 >= z1) && (z2 >= z3)) + { + h = (z3 - z2) * (x1 - x2); hh = (x3 - x2) * (z1 - z2); + if (h*h < hh*hh) /* back is 23 */ + { + backoff = dimz-2; backstep = -1; iter0 = dimz; + sideoff = (dimx-2)*dimz; sidestep = -dimz; + } + else /* back is 12 */ + { + backoff = (dimx-2)*dimz; backstep = -dimz; iter0 = dimx; + sideoff = dimz-2; sidestep = -1; + } + } + /* Hindmost corner 3 */ + else + { + h = (z0 - z3) * (x2 - x3); hh = (x0 - x3) * (z2 - z3); + if (h*h < hh*hh) /* back is 30 */ + { + backoff = 0; backstep = dimz; iter0 = dimx; + sideoff = dimz-2; sidestep = -1; + } + else /* back is 23 */ + { + backoff = dimz-2; backstep = -1; iter0 = dimz; + sideoff = 0; sidestep = dimz; + } + } +#ifdef POLYSHADE_HEIGHT_EXPERIMENTAL + /* Init indices for triangle plotting order */ + /* 0: 012:023, 1: 023:012, 2: 130:123, 3: 123:130 */ + indices[0][0] = 0; indices[0][1] = dimz; indices[0][2] = dimz+1; + indices[0][3] = 0; indices[0][4] = dimz+1; indices[0][5] = 1; + indices[1][0] = 0; indices[1][1] = dimz+1; indices[1][2] = 1; + indices[1][3] = 0; indices[1][4] = dimz; indices[1][5] = dimz+1; + indices[2][0] = dimz; indices[2][1] = 1; indices[2][2] = 0; + indices[2][3] = dimz; indices[2][4] = dimz+1; indices[2][5] = 1; + indices[3][0] = dimz; indices[3][1] = dimz+1; indices[3][2] = 1; + indices[3][3] = dimz; indices[3][4] = 1; indices[3][5] = 0; + /* normals associated with these */ + indices[0][6] = 0; indices[0][7] = 1; indices[1][6] = 1; indices[1][7] = 0; + indices[2][6] = 0; indices[2][7] = 1; indices[3][6] = 1; indices[3][7] = 0; + + vbase = vert + backoff + sideoff; nbase = norm + backoff + sideoff; +#else + /* When using the ZBuffer we try to render front to back for much more efficiency */ + i = (dimx-2) * dimz + dimz - 2 - backoff - sideoff; vbase = vert + i; nbase = norm + i; + backstep = -backstep; sidestep = -sidestep; + + RenderInitZBuffer(graphEnv, meshDesc); + zbuffer = meshDesc->zbuffer; +#endif + + iter1 = dimx + dimz - iter0; + for (i=0; i<iter0-1; i++) + { + vp = vbase; np = nbase; + for (j=0; j<iter1-1; j++) + { +#if 0 + vertex_fp polygon[4], normals[4]; + + polygon[0] = vp[0]; polygon[1] = vp[dimz]; polygon[2] = vp[dimz+1]; polygon[3] = vp[1]; + normals[0] = np[0]; normals[1] = np[dimz]; normals[2] = np[dimz+1]; normals[3] = np[1]; + /*printf("%d,%d : %f,%f,%f : %f,%f,%f : %f,%f,%f : %f,%f,%f\n", i, j, polygon[0].x, polygon[0].y, polygon[0].z, polygon[1].x, polygon[1].y, polygon[1].z, polygon[2].x, polygon[2].y, polygon[2].z, polygon[3].x, polygon[3].y, polygon[3].z);*/ + /*colour = (((i+j)&1) == 0) ? 128 : 255;*/ + RenderShadedPolygon(4, polygon, normals, colour, graphEnv, lightDesc, NULL, zbuffer); +#else + vertex_fp normals[3]; + vertex_fp diag1, diag2; +#ifdef POLYSHADE_HEIGHT_EXPERIMENTAL + vertex_fp polygon[4], tnorms[2]; + int *ind; +#else + vertex_fp polygon[3]; +#endif + + /*colour = (((i+j)&1) == 0) ? 128 : 255;*/ + + /* + * Determine the best splitting line (BCD, BDA vs. ABC, ACD) by minimizing the scalar product + * of the normalized diagonals with the normals at their endpoints + */ + diag1.x = vp[1].x - vp[dimz].x; diag1.y = vp[1].y - vp[dimz].y; diag1.z = vp[1].z - vp[dimz].z; + h = diag1.x * diag1.x + diag1.y * diag1.y + diag1.z * diag1.z; + if (h > 1e-6) + { + h = (real_t)(1/sqrt(h)); + diag1.x *= h; diag1.y *= h; diag1.z *= h; + } + diag2.x = vp[dimz+1].x - vp[0].x; diag2.y = vp[dimz+1].y - vp[0].y; diag2.z = vp[dimz+1].z - vp[0].z; + h = diag2.x * diag2.x + diag2.y * diag2.y + diag2.z * diag2.z; + if (h > 1e-6) + { + h = (real_t)(1/sqrt(h)); + diag2.x *= h; diag2.y *= h; diag2.z *= h; + } + h = diag1.x * np[dimz].x + diag1.y * np[dimz].y + diag1.z * np[dimz].z; + hh = diag1.x * np[1].x + diag1.y * np[1].y + diag1.z * np[1].z; + x0 = h*h + hh*hh; + h = diag2.x * np[dimz+1].x + diag2.y * np[dimz+1].y + diag2.z * np[dimz+1].z; + hh = diag2.x * np[0].x + diag2.y * np[0].y + diag2.z * np[0].z; + x1 = h*h + hh*hh; +#ifdef POLYSHADE_HEIGHT_EXPERIMENTAL + polygon[0].x = vp[dimz].x - vp[0].x; polygon[1].x = vp[dimz+1].x - vp[dimz].x; + polygon[0].y = vp[dimz].y - vp[0].y; polygon[1].y = vp[dimz+1].y - vp[dimz].y; + polygon[0].z = vp[dimz].z - vp[0].z; polygon[1].z = vp[dimz+1].z - vp[dimz].z; + polygon[2].x = vp[1].x - vp[dimz+1].x; polygon[3].x = vp[0].x - vp[1].x; + polygon[2].y = vp[1].y - vp[dimz+1].y; polygon[3].y = vp[0].y - vp[1].y; + polygon[2].z = vp[1].z - vp[dimz+1].z; polygon[3].z = vp[0].z - vp[1].z; + + if (x1 < x0) + { + /* Diagonal is 02 = diag2 */ + tnorms[0].x = polygon[0].y * polygon[1].z - polygon[0].z * polygon[1].y; + tnorms[0].y = polygon[0].z * polygon[1].x - polygon[0].x * polygon[1].z; + tnorms[0].z = polygon[0].x * polygon[1].y - polygon[0].y * polygon[1].x; + tnorms[1].x = polygon[2].y * polygon[3].z - polygon[2].z * polygon[3].y; + tnorms[1].y = polygon[2].z * polygon[3].x - polygon[2].x * polygon[3].z; + tnorms[1].z = polygon[2].x * polygon[3].y - polygon[2].y * polygon[3].x; +#if 0 + /* Method #1: scalar product of (real) surface normals with position of normals */ + h = tnorms[0].x * vp[dimz].x + tnorms[0].y * vp[dimz].y + tnorms[0].z * vp[dimz].z; + hh = tnorms[1].x * vp[1].x + tnorms[1].y * vp[1].y + tnorms[1].z * vp[1].z; + if (tnorms[0].x * diag1.x + tnorms[0].y * diag1.y + tnorms[0].z * diag1.z > 0) + { + h = -h; hh = -hh; + } + if (h < hh) ind = indices[1]; else ind = indices[0]; +#else + /* Method #2: Compare z values */ + h = tnorms[1].x * vp[dimz].x + tnorms[1].y * vp[dimz].y + tnorms[1].z * vp[dimz].z; + if (h != 0.0) + { + h = (tnorms[1].x * vp[1].x + tnorms[1].y * vp[1].y + tnorms[1].z * vp[1].z) / h; + } + if (h > 1.0) ind = indices[1]; else ind = indices[0]; +#endif + } + else + { + /* Diagonal is 13 = diag1 */ + tnorms[0].x = polygon[3].y * polygon[0].z - polygon[3].z * polygon[0].y; + tnorms[0].y = polygon[3].z * polygon[0].x - polygon[3].x * polygon[0].z; + tnorms[0].z = polygon[3].x * polygon[0].y - polygon[3].y * polygon[0].x; + tnorms[1].x = polygon[1].y * polygon[2].z - polygon[1].z * polygon[2].y; + tnorms[1].y = polygon[1].z * polygon[2].x - polygon[1].x * polygon[2].z; + tnorms[1].z = polygon[1].x * polygon[2].y - polygon[1].y * polygon[2].x; +#if 0 + h = tnorms[0].x * vp[0].x + tnorms[0].y * vp[0].y + tnorms[0].z * vp[0].z; + hh = tnorms[1].x * vp[dimz+1].x + tnorms[1].y * vp[dimz+1].y + tnorms[1].z * vp[dimz+1].z; + if (tnorms[0].x * diag2.x + tnorms[0].y * diag2.y + tnorms[0].z * diag2.z > 0) + { + h = -h; hh = -hh; + } + if (h < hh) ind = indices[3]; else ind = indices[2]; +#else + h = tnorms[1].x * vp[0].x + tnorms[1].y * vp[0].y + tnorms[1].z * vp[0].z; + if (h != 0.0) + { + h = (tnorms[1].x * vp[dimz+1].x + tnorms[1].y * vp[dimz+1].y + tnorms[1].z * vp[dimz+1].z) / h; + } + if (h > 1.0) ind = indices[3]; else ind = indices[2]; +#endif + } + polygon[0] = vp[ind[0]]; polygon[1] = vp[ind[1]]; polygon[2] = vp[ind[2]]; + normals[0] = np[ind[0]]; normals[1] = np[ind[1]]; normals[2] = np[ind[2]]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, tnorms + ind[6], zbuffer); + polygon[1] = vp[ind[4]]; polygon[2] = vp[ind[5]]; + normals[1] = np[ind[4]]; normals[2] = np[ind[5]]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, tnorms + ind[7], zbuffer); +#else + if (x1 < x0) /* splitting diagonal is 02 */ + { + polygon[0] = vp[0]; polygon[1] = vp[dimz]; polygon[2] = vp[dimz+1]; + normals[0] = np[0]; normals[1] = np[dimz]; normals[2] = np[dimz+1]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, NULL, zbuffer); + polygon[1] = vp[dimz+1]; polygon[2] = vp[1]; + normals[1] = np[dimz+1]; normals[2] = np[1]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, NULL, zbuffer); + } + else + { + polygon[0] = vp[dimz]; polygon[1] = vp[dimz+1]; polygon[2] = vp[1]; + normals[0] = np[dimz]; normals[1] = np[dimz+1]; normals[2] = np[1]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, NULL, zbuffer); + polygon[1] = vp[1]; polygon[2] = vp[0]; + normals[1] = np[1]; normals[2] = np[0]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, NULL, zbuffer); + } +#endif /* POLYSHADE_HEIGHT_EXPERIMENTAL */ + +#endif + vp += sidestep; np += sidestep; + } + vbase += backstep; nbase += backstep; + } + + free(vert); free(norm); + + return 0; +} + + +void RenderHeightFreeMesh(mesh_desc *meshDesc) +{ + if (meshDesc->vert != NULL) free(meshDesc->vert); + if (meshDesc->norm != NULL) free(meshDesc->norm); + if (meshDesc->zbuffer != NULL) free(meshDesc->zbuffer); + meshDesc->vert = NULL; meshDesc->norm = NULL; + meshDesc->zbuffer = NULL; meshDesc->zbuffSize = 0; + meshDesc->srcData = NULL; meshDesc->width = 0; meshDesc->height = 0; +} + + +int RenderHeightGetDomain(const mdd_desc *mddDesc, int *dimx, int *dimz, int *stepx, int *stepz) +{ + int i, idx1=-1, idx2=-1; + + for (i=0; i<mddDesc->numDims; i++) + { + if (mddDesc->widths[i] != 1) + { + if (idx1 < 0) idx1 = i; + else if (idx2 < 0) idx2 = i; + else break; + } + } + if ((idx1 < 0) || (idx2 < 0) || (i < mddDesc->numDims)) + { + fprintf(stderr, "Height field projection must be 2D!\n"); + return -1; + } + if (dimx != NULL) *dimx = mddDesc->dims[idx1]; + if (dimz != NULL) *dimz = mddDesc->dims[idx2]; + if ((stepx != NULL) || (stepz != NULL)) + { + int s; + + s = mddDesc->baseSize; + for (i=mddDesc->numDims-1; i>idx2; i--) s *= mddDesc->dims[i]; + if (stepz != NULL) *stepz = s; + if (stepx != NULL) + { + for ( ; i>idx1; i--) s *= mddDesc->dims[i]; + *stepx = s; + } + } + return 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/applications/rview/cube_render.h b/applications/rview/cube_render.h new file mode 100644 index 0000000..05293c6 --- /dev/null +++ b/applications/rview/cube_render.h @@ -0,0 +1,228 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * Renderers for RasDaMan MDD of various base types. Renderers provided + * are: + * 3D: surface ( RenderCubeSurf() ), voxel ( RenderCubeVoxel() ) + * 2D: height-fields ( RenderHeightField() ). + * Misc primitives: lines ( RenderLineSegment() ), shaded polyings using + * a Z-Buffer ( RenderShadedPolygon() ). + * + * The renderer module is standalone and can be used independently + * from rView. In the rView project it's used by rviewImage. + * + * COMMENTS: + * No Comments + */ + + + +#ifndef _CUBE_RENDER_H +#define _CUBE_RENDER_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Externally visible structs and definitions */ + +#define CUBE_BSIZE_CHAR 1 +#define CUBE_BSIZE_SHORT 2 +#define CUBE_BSIZE_RGB 3 +#define CUBE_BSIZE_LONG 4 + + +/* FP type to use */ +typedef float real_t; + + +typedef struct vertex { + long x, y, z; +} vertex; + +typedef struct vertex_fp { + real_t x, y, z; +} vertex_fp; + +typedef struct vertex_p { + long x, y; +} vertex_p; + +typedef struct rotation_desc { + double sin; + double cos; +} rotation_desc; + + + +typedef struct graph_env { + int clipl, clipr, clipd, clipu, clipz; + int midx, midy, lineadd, zpro; + unsigned long bbox_colour; + void *dest; +} graph_env; + + +typedef struct tex_desc { + int dimx, dimy, dimz; + int widthx, widthy, widthz; + int baseSize; + void *data; + int floatType; + double minVal; /* Only needed for float types */ + double maxVal; +} tex_desc; + + +/* generalized tex_desc */ +typedef struct mdd_desc { + int numDims; + int *widths; + int *dims; + int baseSize; + void *data; + int floatType; + double minVal; + double maxVal; +} mdd_desc; + + +typedef struct bounding_box { + int minx, miny, maxx, maxy; +} bounding_box; + +typedef struct face { + int vertices; + unsigned int flags; + vertex_fp *first; + vertex_p *first_p; + bounding_box bBox; +} face; + + +/* Methods for normal vector approximation when summing up neighbouring voxels */ +#define RENDER_NORM_KERNEL_VOID 0 +#define RENDER_NORM_KERNEL_HOMO 1 +#define RENDER_NORM_KERNEL_LINEAR 2 +#define RENDER_NORM_KERNEL_GAUSS 3 + +typedef unsigned short zbuffer_t; + +typedef struct mesh_desc { + void *srcData; + int width, height; + real_t scaleGrid, scaleHeight; + real_t oldGrid, oldHeight; + unsigned int colour; + real_t miny, maxy; + vertex_fp *vert; + vertex_fp *norm; + zbuffer_t *zbuffer; + unsigned int zbuffSize; +} mesh_desc; + +typedef struct light_desc { + vertex_fp lights; + double ambient; + double gain; + double cosine; + double scintCos; +} light_desc; + +typedef struct voxel_desc { + double pixelThresholdLow; + double pixelThresholdHigh; + double weightThreshold; + int weightQuantisation; + int useRgbBrightness; + int kernSize; + int kernType; + void *voxColour; + light_desc light; +} voxel_desc; + +typedef struct render_desc { + face *faces; + vertex_fp left_g, right_g; + vertex_fp left_t, right_t; + long left_p, right_p; + vertex_fp *texbase; + vertex_fp org, tmax; + int found, do_lines; + tex_desc *texDesc; + graph_env *graphEnv; +} render_desc; + + + +/* + * These calls aren't needed for rendering. They let you build a clipped cube + * which can be referenced via the render descriptor (also used internally) and + * free the resources after you're finished with them. + */ +extern void RenderCubeClipCube(const vertex_fp geomData[4], render_desc *renderDesc, int removeHidden); +extern render_desc *RenderCubeBuild(const vertex_fp geomData[4], const graph_env *graphEnv); +extern void RenderCubeFreeDesc(render_desc *renderDesc); +extern int RenderCubeGetPosition(int x_p, int y_p, vertex_fp *pos, render_desc *renderDesc); +extern void RenderCubeDetermineRotation(const vertex_fp *base, rotation_desc *rd); + + + +/* + * The actual renderers. + * Exit status is 0 for OK, otherwise an error occurred. */ + +/* For backwards compatibility, equals RenderCubeSurf */ +extern int RenderCube(const vertex_fp geomData[4], const graph_env *graphEnv, const tex_desc *texDesc); + +/* Renders the textured surfaces of the cube only */ +extern int RenderCubeSurf(const vertex_fp geomData[4], const graph_env *graphEnv, const tex_desc *texDesc); + +/* Renders the cube using a volume-oriented method. */ +extern int RenderCubeVoxel(const vertex_fp geomData[4], const graph_env *graphEnv, const tex_desc *texDesc, voxel_desc *voxDesc); + +/* Line API: only texDesc and graphEnv of renderDesc have to be initialized */ +/* Renders a line in unprocessed 3D coordinates */ +extern void Render3DLine(const vertex_fp *from, const vertex_fp *to, const render_desc *renderDesc, long colour); +/* Renders a line in already projected, z-clipped coordinates */ +extern void RenderLineSegment(const vertex_p *from, const vertex_p *to, const render_desc *renderDesc, long colour); + +/* Renders a polygon using shading */ +extern int RenderShadedPolygon(int numVert, const vertex_fp *vertices, const vertex_fp *normals, unsigned int colour, const graph_env *graphEnv, const light_desc *lightDesc, const vertex_fp *real_norm, zbuffer_t *zbuffer); + +/* Renders a 2D height field */ +extern int RenderHeightField(mesh_desc *meshDesc, const vertex_fp *rotTrans, const graph_env *graphEnv, const mdd_desc *mddDesc, const light_desc *lightDesc); + +extern void RenderHeightFreeMesh(mesh_desc *meshDesc); + +extern int RenderHeightGetDomain(const mdd_desc *mddDesc, int *dimx, int *dimz, int *stepx, int *stepz); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/applications/rview/cube_render_core.c b/applications/rview/cube_render_core.c new file mode 100644 index 0000000..bbf7fd0 --- /dev/null +++ b/applications/rview/cube_render_core.c @@ -0,0 +1,182 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * cube_render_core.c + * Main rendering function. All clipping and hidden face removal has been + * performed by the main function, it's also guaranteed that the face is + * not ``empty''. All that remains to be done is iterate over the scanlines + * and render them. + * + * COMMENTS: + * No comments + */ + +static void RENDER_CORE_NAME (int faceNo, render_desc *renderDesc) +{ + graph_env *ge; + tex_desc *td; + face *cface; + real_t zpro; + int line, width, i; + uint8 *destBase; +#if (TEXEL_BSIZE == 3) + uint8 *auxPtr; +#endif +#if (TEXEL_BSIZE == 8) + uint32 *auxPtr; +#endif + union {uint8 *c; uint16 *s; uint32 *l;} texture; + union {uint8 *c; uint16 *s; uint32 *l;} dest; +#if (TEXEL_BSIZE < 3) + register uint32 accu_t; +#endif + int32 tx, ty, tz, dtx, dty, dtz, ntx, nty, ntz; + vertex_fp deltaLR_g, deltaLR_t, *left_g, *left_t; + int dimy, dimz, dimyz; + real_t nom, den, h, dn, dd; +#if (CUBE_RENDER_DEBUG > 0) + uint8 *high_tide_dest; + unsigned int high_tide_src; +#endif + + ge = (renderDesc->graphEnv); td = (renderDesc->texDesc); zpro = (real_t)(ge->zpro); + cface = renderDesc->faces + faceNo; + +#if (CUBE_RENDER_DEBUG > 0) + high_tide_dest = (uint8*)(ge->dest) + (ge->clipu - ge->clipd + 1) * (ge->lineadd); + high_tide_src = (renderDesc->texDesc->dimx)*(renderDesc->texDesc->dimy)*(renderDesc->texDesc->dimz); + + printf("Render face %d (%d, %d, %d, %d)\n", + faceNo, cface->bBox.minx, cface->bBox.miny, cface->bBox.maxx, cface->bBox.maxy); + if ((cface->bBox.miny < ge->clipd) || (cface->bBox.maxy > ge->clipu)) + { + printf("y out of range (%d,%d)!\n", cface->bBox.miny, cface->bBox.maxy); fflush(stdout); + } +#endif + destBase = (uint8*)(ge->dest) + (ge->midy - cface->bBox.maxy)*(ge->lineadd) + (ge->midx * renderDesc->texDesc->baseSize); + dimy = renderDesc->texDesc->dimy; dimz = renderDesc->texDesc->dimz; dimyz = dimy * dimz; + left_g = &(renderDesc->left_g); left_t = &(renderDesc->left_t); + + /* For each line ... */ + for (line = cface->bBox.maxy; line >= cface->bBox.miny; line--, destBase += renderDesc->graphEnv->lineadd) + { + RenderCubeGetScanline(line, faceNo, renderDesc, 1); + if (renderDesc->found == 0) continue; + + /* First trap case where everything lies outside (in theory this can happen for some lines) */ + if ((renderDesc->left_p > ge->clipr) || (renderDesc->right_p < ge->clipl)) continue; + + /* Left clipping; essentially this shouldn't be (and usually isn't) necessary because we + already clipped the left border in the main procedure, but rounding errors could still + lead to problems. In that case simply updating the plot position without touching the + texture coordinates is OK. */ + if (renderDesc->left_p < ge->clipl) {renderDesc->left_p = ge->clipl;} + if (renderDesc->right_p > ge->clipr) {renderDesc->right_p = ge->clipr;} + + dest.c = destBase + (renderDesc->left_p) * TEXEL_BSIZE; + width = renderDesc->right_p - renderDesc->left_p + 1; + if (width <= 0) continue; + /* Compute deltaLR_g and deltaLR_t components */ + deltaLR_g.x = renderDesc->right_g.x - left_g->x; + deltaLR_g.y = renderDesc->right_g.y - left_g->y; + deltaLR_g.z = renderDesc->right_g.z - left_g->z; + deltaLR_t.x = renderDesc->right_t.x - left_t->x; + deltaLR_t.y = renderDesc->right_t.y - left_t->y; + deltaLR_t.z = renderDesc->right_t.z - left_t->z; + tx = (int32)(left_t->x); + ty = (int32)(left_t->y); + tz = (int32)(left_t->z); + + /*if (faceNo == 6) printf("(%f,%f,%f:%x,%x,%x)", left_t->x, left_t->y, left_t->z, tx, ty, tz);*/ + nom = left_g->x * zpro - ((real_t)(renderDesc->left_p)) * left_g->z; + den = deltaLR_g.x * zpro - ((real_t)(renderDesc->left_p)) * deltaLR_g.z; +#if (CUBE_RENDER_DEBUG > 1) + printf("Start line %d (%d)\n", line, (dest.c - (uint8*)(ge->dest))); fflush(stdout); +#endif + /*if (faceNo == 0) printf("(%d,%d,%d)",line,renderDesc->left_p, renderDesc->right_p);*/ + + /* Avoid jitter */ + if (fabs(renderDesc->right_t.x - tx) <= FIXPOINT_PREC) + { +#undef TEXEL_FETCH +#undef TEXEL_STEP +#define TEXEL_FETCH \ + TEXEL_POINTER[((ty >> FIXPOINT_PREC) * dimz + (tz >> FIXPOINT_PREC)) * TEXEL_MULTIPLIER] +#define TEXEL_STEP \ + ty += dty; tz += dtz; +#define TEXEL_CONST_X + + texture.c = (uint8*)(renderDesc->texDesc->data) + (tx >> FIXPOINT_PREC) * dimyz * TEXEL_BSIZE; +#include "cube_render_line.c" +#undef TEXEL_CONST_X + } + else if (fabs(renderDesc->right_t.y - ty) <= FIXPOINT_PREC) + { +#undef TEXEL_FETCH +#undef TEXEL_STEP +#define TEXEL_FETCH \ + TEXEL_POINTER[((tx >> FIXPOINT_PREC) * dimyz + (tz >> FIXPOINT_PREC)) * TEXEL_MULTIPLIER] +#define TEXEL_STEP \ + tx += dtx; tz += dtz; +#define TEXEL_CONST_Y + + texture.c = (uint8*)(renderDesc->texDesc->data) + (ty >> FIXPOINT_PREC) * dimz * TEXEL_BSIZE; +#include "cube_render_line.c" +#undef TEXEL_CONST_Y + } + else if (fabs(renderDesc->right_t.z - tz) <= FIXPOINT_PREC) + { +#undef TEXEL_FETCH +#undef TEXEL_STEP +#define TEXEL_FETCH \ + TEXEL_POINTER[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz) * TEXEL_MULTIPLIER] +#define TEXEL_STEP \ + tx += dtx; ty += dty; +#define TEXEL_CONST_Z + + texture.c = (uint8*)(renderDesc->texDesc->data) + (tz >> FIXPOINT_PREC) * TEXEL_BSIZE; +#include "cube_render_line.c" +#undef TEXEL_CONST_Z + } + else + { +#undef TEXEL_FETCH +#undef TEXEL_STEP +#define TEXEL_FETCH \ + TEXEL_POINTER[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC)) * TEXEL_MULTIPLIER] +#define TEXEL_STEP \ + tx += dtx; ty += dty; tz += dtz; + + texture.c = (uint8*)(renderDesc->texDesc->data); +#include "cube_render_line.c" + } + +#if (CUBE_RENDER_DEBUG > 1) + printf("Line OK\n"); fflush(stdout); +#endif + } +#if (CUBE_RENDER_DEBUG > 0) + printf("Face OK.\n"); fflush(stdout); +#endif +} diff --git a/applications/rview/cube_render_line.c b/applications/rview/cube_render_line.c new file mode 100644 index 0000000..6c40e6d --- /dev/null +++ b/applications/rview/cube_render_line.c @@ -0,0 +1,172 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * COMMENTS: + * + * This is called from within cube_render.c to render a line. It has been + * moved out here to be able to easily optimise the whole system for various + * settings and simplify the use of different texture base sizes. + */ + + + + i = (8 - (ge->midx + renderDesc->left_p)) & 7; + if ((i > width) || (width < 8)) i = width; + while (i > 0) + { + width -= i; + nom -= ((real_t)i) * left_g->z; den -= ((real_t)i) * deltaLR_g.z; h = 0.0; + if (den != 0) + { + h = -nom/den; + if (h < 0.0) h = 0.0; + if (h > 1.0) h = 1.0; + } +#ifndef TEXEL_CONST_X + dtx = ((int32)(left_t->x + h * deltaLR_t.x) - tx) / i; +#endif +#ifndef TEXEL_CONST_Y + dty = ((int32)(left_t->y + h * deltaLR_t.y) - ty) / i; +#endif +#ifndef TEXEL_CONST_Z + dtz = ((int32)(left_t->z + h * deltaLR_t.z) - tz) / i; +#endif + while (i > 0) + { +#if (CUBE_RENDER_DEBUG > 2) + printf("\t(%d, %d, %d: %d)\n", (tx>>FIXPOINT_PREC), (ty>>FIXPOINT_PREC), (tz>>FIXPOINT_PREC), ((tx>>FIXPOINT_PREC)*dimy + (ty>>FIXPOINT_PREC))*dimz + (tz>>FIXPOINT_PREC)); fflush(stdout); +#endif +#if (CUBE_RENDER_DEBUG > 0) + if (((tx>>FIXPOINT_PREC)*dimy + (ty>>FIXPOINT_PREC))*dimz + (tz>>FIXPOINT_PREC) > high_tide_src) + { + printf("Texture buffer overflow (%d, %d, %d) | (%f, %f, %f)!\n", (tx>>FIXPOINT_PREC), (ty>>FIXPOINT_PREC), (tz>>FIXPOINT_PREC), left_t->x + deltaLR_t.x, left_t->y + deltaLR_t.y, left_t->z + deltaLR_t.z); fflush(stdout); break; + } + if (dest.c > high_tide_dest) + { + printf("Screen buffer overflow (%d)!\n", (int)(high_tide_dest - (uint8*)(ge->dest))); + fflush(stdout); break; + } +#endif + TEXEL_ASSIGN; + i--; + } + if (width < 8) i = width; + } + if (width <= 0) continue; + /* The above loop guarantees that at this point width >= 8 */ + dn = (real_t)(-8.0 * left_g->z); dd = (real_t)(-8.0 * deltaLR_g.z); + nom += dn; den += dd; h = 0.0; + if (den != 0) + { + h = -nom/den; + if (h < 0.0) h = 0.0; + if (h > 1.0) h = 1.0; + } +#ifndef TEXEL_CONST_X + dtx = ((int32)(left_t->x + h * deltaLR_t.x) - tx) / 8; +#endif +#ifndef TEXEL_CONST_Y + dty = ((int32)(left_t->y + h * deltaLR_t.y) - ty) / 8; +#endif +#ifndef TEXEL_CONST_Z + dtz = ((int32)(left_t->z + h * deltaLR_t.z) - tz) / 8; +#endif + + /* Help the compiler do efficient Int/FP parallelism. */ + while (width >= 16) + { + width -= 8; h = 0.0; + nom += dn; + TEXEL_ACCU_0(accu_t); + den += dd; + TEXEL_ACCU_1(accu_t); + if (den != 0) h = -nom/den; + TEXEL_ACCU_2(accu_t); + if (h < 0.0) h = 0.0; + TEXEL_ACCU_3(accu_t); + if (h > 1.0) h = 1.0; + TEXEL_ACCU_0(accu_t); +#ifndef TEXEL_CONST_X + ntx = (int32)(left_t->x + h * deltaLR_t.x); +#endif + TEXEL_ACCU_1(accu_t); +#ifndef TEXEL_CONST_Y + nty = (int32)(left_t->y + h * deltaLR_t.y); +#endif + TEXEL_ACCU_2(accu_t); +#ifndef TEXEL_CONST_Z + ntz = (int32)(left_t->z + h * deltaLR_t.z); +#endif + TEXEL_ACCU_3(accu_t); +#ifndef TEXEL_CONST_X + dtx = (ntx - tx) / 8; +#endif +#ifndef TEXEL_CONST_Y + dty = (nty - ty) / 8; +#endif +#ifndef TEXEL_CONST_Z + dtz = (ntz - tz) / 8; +#endif + } + /* 8 <= width <= 15 here! */ + width -= 8; h = 0.0; + nom -= ((real_t)width) * left_g->z; + TEXEL_ACCU_0(accu_t); + den -= ((real_t)width) * deltaLR_g.z; + TEXEL_ACCU_1(accu_t); + if (den != 0) h = -nom/den; + TEXEL_ACCU_2(accu_t); + if (h < 0.0) h = 0.0; + TEXEL_ACCU_3(accu_t); + if (h > 1.0) h = 1.0; + TEXEL_ACCU_0(accu_t); +#ifndef TEXEL_CONST_X + ntx = (int32)(left_t->x + h * deltaLR_t.x); +#endif + TEXEL_ACCU_1(accu_t); +#ifndef TEXEL_CONST_Y + nty = (int32)(left_t->y + h * deltaLR_t.y); +#endif + TEXEL_ACCU_2(accu_t); +#ifndef TEXEL_CONST_Z + ntz = (int32)(left_t->z + h * deltaLR_t.z); +#endif + TEXEL_ACCU_3(accu_t); + if (width > 0) + { +#ifndef TEXEL_CONST_X + dtx = (ntx - tx) / width; +#endif +#ifndef TEXEL_CONST_Y + dty = (nty - ty) / width; +#endif +#ifndef TEXEL_CONST_Z + dtz = (ntz - tz) / width; +#endif + while (width > 0) + { + TEXEL_ASSIGN; + width--; + } + } diff --git a/applications/rview/cube_render_mesh.c b/applications/rview/cube_render_mesh.c new file mode 100644 index 0000000..bebaab6 --- /dev/null +++ b/applications/rview/cube_render_mesh.c @@ -0,0 +1,48 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * COMMENTS: + * This is called from within cube_render.c to create a vertex mesh + * out of the source data when rendering height fields. + */ + +static void RENDER_MESH_NAME(vertex_fp *vert, const uint8 *src, int srcIncX, int srcIncZ, int dimx, int dimz, real_t scaleGrid, real_t scaleHeight) +{ + union {uint8 *c; uint16 *s; uint32 *l; float *f; double *d;} srcLine, srcPtr; + real_t gridx, gridz; + real_t posx, posz; + int i, j; + vertex_fp *vp; + + gridx = scaleGrid; gridz = scaleGrid; srcLine.c = (uint8*)src; vp = vert; + for (i=0, posx=0.0; i<dimx; i++, posx += gridx, srcLine.c += srcIncX) + { + srcPtr.c = srcLine.c; + for (j=0, posz=0.0; j<dimz; j++, posz += gridz, srcPtr.c += srcIncZ) + { + vp->x = posx; vp->y = (real_t)(*srcPtr.RENDER_MESH_TYPE)*scaleHeight; vp->z = posz; + vp++; + } + } +} diff --git a/applications/rview/cube_render_voxline.c b/applications/rview/cube_render_voxline.c new file mode 100644 index 0000000..6afa7d6 --- /dev/null +++ b/applications/rview/cube_render_voxline.c @@ -0,0 +1,482 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * COMMENTS: + * + * This is called from within cube_render.c to render a line using a voxel + * approch. It has been moved here to easily handle different base types + * and sizes. + */ + +static void RENDER_CORE_NAME(int line, project_plane_desc *ppd, const render_desc *renderDesc) +{ + graph_env *ge; + tex_desc *td; + union {uint8 *c; uint16 *s; uint32 *l; float *f; double *d;} texture; + union {uint8 *c; uint16 *s; uint32 *l; float *f; double *d;} dest; + int minx, maxx, posx; + project_plane_intersect *front, *back, *lastFront, *lastBack; + real_t front_z, back_z, new_z; + real_t nom, den, pos, h; + real_t zpro; + vertex_fp front_t, back_t, deltaFB_t; + real_t front_h, back_h; + /*int front_num, back_num;*/ + int i, depth; + int32 tx, ty, tz, dtx, dty, dtz; + int dimy, dimz, dimyz; + int pixelsDone; + RENDER_CAST_TYPE value; + RENDER_CAST_TYPE ptl, pth, wth; /* thresholds */ + RENDER_CAST_TYPE wgtAccu, weight; + RENDER_CAST_TYPE *voxColour; +#ifdef RENDER_FLOAT_TYPE + real_t weightScale; +#else + int weightQuant; +#endif +#if (TEXEL_BSIZE == 3) + uint8 *srcPix; + uint32 red, green, blue; +#else + RENDER_CAST_TYPE pixel; +#endif + + ge = renderDesc->graphEnv; td = renderDesc->texDesc; zpro = (real_t)ge->zpro; + /* Kill warnings */ + dtx = 0; dty = 0; dtz = 0; + dimy = td->dimy; dimz = td->dimz; dimyz = dimy*dimz; + /* Horizontal clipping */ + minx = (ppd->left_p); if (minx > ge->clipr) return; + maxx = (ppd->right_p); if (maxx < ge->clipl) return; + if (minx < ge->clipl) minx = ge->clipl; + if (maxx > ge->clipr) maxx = ge->clipr; +#ifdef RENDER_FLOAT_TYPE + ptl = (RENDER_CAST_TYPE)(ppd->pixelThresholdLowF); + pth = (RENDER_CAST_TYPE)(ppd->pixelThresholdHighF); + wth = (RENDER_CAST_TYPE)(ppd->weightThresholdF); + weightScale = (real_t)(1.0 / (1 << (ppd->weightQuantisation))); +#else + ptl = (RENDER_CAST_TYPE)(ppd->pixelThresholdLow); + pth = (RENDER_CAST_TYPE)(ppd->pixelThresholdHigh); + wth = (RENDER_CAST_TYPE)(ppd->weightThreshold); + weightQuant = ppd->weightQuantisation; +#endif + voxColour = (RENDER_CAST_TYPE *)(ppd->voxColour); + dest.c = (uint8*)(ge->dest) + (ge->midy - line)*(ge->lineadd) + ((ge->midx + minx) * TEXEL_BSIZE); + texture.c = (uint8*)(td->data); +#if 0 + for (back_num = ppd->segs-1; back_num >= 0; back_num--) + { + if ((ppd->ppi[back_num].left_p <= minx) && (ppd->ppi[back_num].right_p >= minx)) break; + } + for (front_num = 0; front_num < ppd->segs; front_num++) + { + if ((ppd->ppi[front_num].left_p <= minx) && (ppd->ppi[front_num].right_p >= minx)) break; + } + if (back_num == front_num) + { + printf("%d,%d | %d\n", front_num, back_num, ppd->segs); fflush(stdout); + for (i=0; i<ppd->segs; i++) + { + printf("\t[%d,%d]\n", ppd->ppi[i].left_p, ppd->ppi[i].right_p); + } + } +#endif + front = NULL; back = NULL; lastFront = NULL; lastBack = NULL; + front_z = 0.0; back_z = 0.0; pixelsDone = 0; + /* main pixel loop */ + for (posx=minx; posx <= maxx; posx++) + { + pos = (real_t)posx; + if (front != NULL) + if (front->right_p < posx) front = NULL; + if (back != NULL) + if (back->right_p < posx) back = NULL; + +#if 1 + /* + * Quite tricky: you may only use the two segments found for longer if they + * remain the same for a few pixels. For instance two segments which are + * linked, one being front and one back: at the leftmost pixel the order + * is undefined. Therefore make sure the front / back segments are static + * over more than 1 pixel (2 seem to be enough). + */ + if ((front == NULL) || (back == NULL) || (pixelsDone < 2)) + { + /* Determine front and back segment */ + front = NULL; back = NULL; + for (i=0; i<ppd->segs; i++) + { + if ((ppd->ppi[i].left_p <= posx) && (ppd->ppi[i].right_p >= posx)) + { + den = (ppd->ppi[i].deltaLR_g.x * zpro) - pos * (ppd->ppi[i].deltaLR_g.z); + if (den == 0.0) continue; + nom = (ppd->ppi[i].left_g.x * zpro) - pos * (ppd->ppi[i].left_g.z); + h = - nom / den; + if (h < 0.0) h = 0.0; if (h > 1.0) h = 1.0; + new_z = ppd->ppi[i].left_g.z + h * (ppd->ppi[i].deltaLR_g.z); + if ((front == NULL) || (new_z <= front_z)) + { + front_z = new_z; front = ppd->ppi + i; front_h = h; + } + if ((back == NULL) || (new_z >= back_z)) + { + back_z = new_z; back = ppd->ppi + i; back_h = h; + } + } + } + /* If no front/back segments found: continue loop and try again next time */ + if ((front == NULL) || (back == NULL)) continue; + + if ((lastFront != front) || (lastBack != back)) + { + lastFront = front; lastBack = back; pixelsDone = 0; + } + else + { + pixelsDone++; + } + } + else +#else + if (back == NULL) + { + /*printf("new back (%d)\n", back_num); fflush(stdout);*/ + if (back_num < 0) continue; + back = ppd->ppi + (back_num--); + } + if (front == NULL) + { + /*printf("new front (%d)\n", front_num); fflush(stdout);*/ + if (front_num >= ppd->segs) continue; + front = ppd->ppi + (front_num++); + } +#endif + { + den = (front->deltaLR_g.x) * zpro - pos * (front->deltaLR_g.z); + if (den == 0.0) continue; + nom = (front->left_g.x) * zpro - pos * (front->left_g.z); + front_h = - nom / den; + if (front_h < 0.0) front_h = 0.0; if (front_h > 1.0) front_h = 1.0; + den = (back->deltaLR_g.x) * zpro - pos * (back->deltaLR_g.z); + if (den == 0.0) continue; + nom = (back->left_g.x) * zpro - pos * (back->left_g.z); + back_h = - nom / den; + if (back_h < 0.0) back_h = 0.0; if (back_h > 1.0) back_h = 1.0; + } + + /* front texture coordinates */ + front_t.x = front->left_t.x + front_h * (front->deltaLR_t.x); + front_t.y = front->left_t.y + front_h * (front->deltaLR_t.y); + front_t.z = front->left_t.z + front_h * (front->deltaLR_t.z); + /* back texture coordinates */ + back_t.x = back->left_t.x + back_h * (back->deltaLR_t.x); + back_t.y = back->left_t.y + back_h * (back->deltaLR_t.y); + back_t.z = back->left_t.z + back_h * (back->deltaLR_t.z); + + /* Determine frontmost pixel */ + deltaFB_t.x = (back_t.x - front_t.x); + deltaFB_t.y = (back_t.y - front_t.y); + deltaFB_t.z = (back_t.z - front_t.z); + /* Step in such a way that you don't move more than one texel in each dimension per step */ + h = (deltaFB_t.x < 0) ? -deltaFB_t.x : deltaFB_t.x; depth = (int)h; + new_z = (deltaFB_t.y < 0) ? -deltaFB_t.y : deltaFB_t.y; + if (new_z > h) {depth = (int)new_z; h = new_z;} + new_z = (deltaFB_t.z < 0) ? -deltaFB_t.z : deltaFB_t.z; + if (new_z > h) depth = (int)new_z; + + if (depth == 0) + { + depth = 1; + } + else + { + h = (1<<FIXPOINT_PREC) / ((real_t)depth); + dtx = (int)(deltaFB_t.x * h); dty = (int)(deltaFB_t.y * h); dtz = (int)(deltaFB_t.z * h); + } + tx = (int)((1<<FIXPOINT_PREC)*front_t.x); + ty = (int)((1<<FIXPOINT_PREC)*front_t.y); + tz = (int)((1<<FIXPOINT_PREC)*front_t.z); + + /* + * Start depth scan to find the frontmost pixel whose brightness exceeds the pixelThreshold + * value. The resulting image is almost completely useless if you don't anti-alias by + * averaging over some pixels. The approach used here calculates a weighted average over + * pixels along the scan beam, favouring bright pixels over dark ones. + */ + wgtAccu = 0; +#if (TEXEL_BSIZE == 3) + red = 0; green = 0; blue = 0; +#else + pixel = 0; +#endif + while (depth > 0) + { + /*printf("%d : %x,%x,%x : %x,%x,%x\n", depth, tx, ty, tz, dtx, dty, dtz); fflush(stdout);*/ + /*if ((tx < 0) || (ty < 0) || (tz < 0) || ((tx >> FIXPOINT_PREC) > renderDesc->tmax.x) || ((ty >> FIXPOINT_PREC) > renderDesc->tmax.y) || ((tz >> FIXPOINT_PREC) > renderDesc->tmax.z)) + { + printf("Range!\n"); + }*/ +#if (TEXEL_BSIZE == 3) + TEXEL_FETCH; + /* Check each rgb component or the total brightness against threshold values? */ +#ifdef TEXEL_RGB_BRIGHTNESS + value = (srcPix[0] + srcPix[1] + srcPix[2]) / 3; + if ((value >= ptl) && (value <= pth)) + { +#else + if (((srcPix[0] >= ptl) && (srcPix[0] <= pth)) || + ((srcPix[1] >= ptl) && (srcPix[1] <= pth)) || + ((srcPix[2] >= ptl) && (srcPix[2] <= pth))) + { + value = (srcPix[0] + srcPix[1] + srcPix[2]) / 3; +#endif + weight = (value + ((1<<weightQuant)-1)) >> weightQuant; + red += srcPix[0] * weight; + green += srcPix[1] * weight; + blue += srcPix[2] * weight; + wgtAccu += weight; + if ((RENDER_CAST_TYPE)wgtAccu >= wth) break; + } +#else + value = (RENDER_CAST_TYPE)(TEXEL_FETCH); + if ((value >= ptl) && (value <= pth)) + { +#ifdef RENDER_FLOAT_TYPE + weight = weightScale * value; +#else + /* Use weights: bright pixels count more and make the scan terminate much faster than dark ones */ + weight = (value + ((1<<weightQuant)-1)) >> weightQuant; +#endif +#ifdef RENDER_FLOAT_TYPE + /* Especially with FP types, value and thus weight may be negative, but weight must be positive! */ + if (weight < 0) weight = -weight; +#endif + pixel += value * weight; + wgtAccu += weight; + if ((RENDER_CAST_TYPE)wgtAccu >= wth) break; + } +#endif + tx += dtx; ty += dty; tz += dtz; + depth--; + } + /* Pixel transparent? */ +#if (TEXEL_BSIZE == 3) + if (wgtAccu != 0) + { + red /= wgtAccu; green /= wgtAccu; blue /= wgtAccu; + } +# ifdef TEXEL_RGB_BRIGHTNESS + if ((red + green + blue < 3*ptl) || (red + green + blue > 3*pth)) +# else + if (((red < ptl) && (green < ptl) && (blue < ptl)) || ((red > pth) && (green > pth) && (blue > pth))) +# endif + { + dest.c += 3; + } +#else + if (wgtAccu != 0) pixel /= wgtAccu; + if ((pixel < ptl) || (pixel > pth)) + { +# if (TEXEL_BSIZE == 1) + dest.c++; +# elif (TEXEL_BSIZE == 2) + dest.s++; +# elif (TEXEL_BSIZE == 4) + dest.l++; +# else + dest.d++; +# endif + } +#endif + else + { + /* Try grey-level gradient shading */ + if (ppd->lightsAmbient >= 0) + { + real_t vx0, vx1, vy0, vy1, vz0, vz1, norm; + int offset; + + /* Position of pixel the ray aborted in */ + tx = (tx - dtx) >> FIXPOINT_PREC; + ty = (ty - dty) >> FIXPOINT_PREC; + tz = (tz - dtz) >> FIXPOINT_PREC; + if (tx < 0) tx = 0; if (tx > td->widthx-1) tx = td->widthx-1; + if (ty < 0) ty = 0; if (ty > td->widthy-1) ty = td->widthy-1; + if (tz < 0) tz = 0; if (tz > td->widthz-1) tz = td->widthz-1; + + if (ppd->kDesc->region == 0) + { + real_t center; +#if 1 +#define RENDER_NORMAL_COORD(t, width, v, delta) \ + if (t <= 0) v##0 = center; else v##0 = (center + (real_t) RENDER_TABLE_TYPE [offset - delta])/2; \ + if (t >= (width)-1) v##1 = center; else v##1 = (center + (real_t) RENDER_TABLE_TYPE [offset + delta])/2; +#else +#define RENDER_NORMAL_COORD(t, width, v, delta) \ + if (t <= 0) v##0 = 0.5; else {center = (real_t) RENDER_TABLE_TYPE [offset - delta]; v##0 = (1 + ((center >= ptl) && (center <= pth)) ? 1 : 0) / 2;} \ + if (t >= (width)-1) v##1 = 0.5; else {center = (real_t) RENDER_TABLE_TYPE [offset + delta]; v##1 = (1 + ((center >= ptl) && (center <= pth)) ? 1 : 0) / 2;} +#endif + + offset = (((tx * dimy) + ty) * dimz) + tz; + /*printf("%d,%d,%d,%d,%p\n", tx, ty, tz, offset, RENDER_TABLE_TYPE); fflush(stdout);*/ + center = (real_t) RENDER_TABLE_TYPE [offset]; + RENDER_NORMAL_COORD(tx, td->widthx, vx, dimyz); + RENDER_NORMAL_COORD(ty, td->widthy, vy, dimz); + RENDER_NORMAL_COORD(tz, td->widthz, vz, 1); + + vx1 -= vx0; vy1 -= vy0; vz1 -= vz0; + } + else + { + int xl, xh, yl, yh, zl, zh, ix, iy, iz, region, region2; + RENDER_CAST_TYPE val; + long offbase, koff, koffbase; + real_t *kernel; + + region = ppd->kDesc->region; region2 = 2*region + 1; + /* Use mid-point of kernel */ + kernel = ppd->kDesc->kernel + ((region * region2) + region)*region2 + region; + xl = tx - region; if (xl < 0) xl = 0; + xh = tx + region; if (xh > td->widthx-1) xh = td->widthx-1; + yl = ty - region; if (yl < 0) yl = 0; + yh = ty + region; if (yh > td->widthy-1) yh = td->widthy-1; + zl = tz - region; if (zl < 0) zl = 0; + zh = tz + region; if (zh > td->widthz-1) zh = td->widthz-1; + vx1 = 0.0; vy1 = 0.0; vz1 = 0.0; + koffbase = ((xl-tx)*region2 + (yl-ty))*region2; + region = region2; region2 *= region2; + offbase = (((xl * dimy) + yl) * dimz); + for (ix = xl; ix <= xh; ix++, offbase += dimyz, koffbase += region2) + { + offset = offbase; koff = koffbase; + for (iy = yl; iy <= yh; iy++, offset += dimz, koff += region) + { + for (iz = zl; iz <= zh; iz++) + { +#if (TEXEL_BSIZE == 3) + srcPix = RENDER_TABLE_TYPE + (offset + iz) * TEXEL_BSIZE; + val = (RENDER_CAST_TYPE)((srcPix[0] + srcPix[1] + srcPix[2]) / 3); +#else + val = (RENDER_CAST_TYPE) RENDER_TABLE_TYPE [offset + iz]; +#endif +#if (!defined(RENDER_RGB_BRIGHTNESS) && (TEXEL_BSIZE == 3)) + if (((srcPix[0] >= ptl) || (srcPix[1] >= ptl) || (srcPix[2] >= ptl)) && ((srcPix[0] <= pth) || (srcPix[1] <= pth) || (srcPix[2] <= pth))) +#else + if ((val >= ptl) && (val <= pth)) +#endif + { + real_t wgtval; + + /* Fold the data with a weighing kernel */ + wgtval = (real_t)(kernel[koff + iz-tz] * val); + /* + * Whether we use the central values too has no effect on the sums, so + * let's no use them for a little extra speed. + */ + if (ix < tx) vx1 -= wgtval; if (ix > tx) vx1 += wgtval; + if (iy < ty) vy1 -= wgtval; if (iy > ty) vy1 += wgtval; + if (iz < tz) vz1 -= wgtval; if (iz > tz) vz1 += wgtval; + } + } + } + } + /* Surface normal n, oriented into the volume now in vx1, vy1, vz1*/ + } + + norm = vx1*vx1 + vy1*vy1 + vz1*vz1; + /* calculate vector l from light source to voxel */ + vx0 = (real_t)tx - ppd->lights.x; vy0 = (real_t)ty - ppd->lights.y; vz0 = (real_t)tz - ppd->lights.z; + /* norm = 1 / (|| n ||^2 * || l ||^2) */ + norm *= (vx0*vx0 + vy0*vy0 + vz0*vz0); + + /* + * In case voxColour != NULL it points to a uint32 / float / double value describing the colour + * each voxel is assigned. This is good for isosurface rendering, but naturally doesn't make + * any sense when shading is disabled. + */ + if (norm != 0) + { + real_t lweight, gweight; +#if (TEXEL_BSIZE == 3) + RENDER_CAST_TYPE addGrey; +#endif + + norm = (real_t)(1/sqrt(norm)); + /* norm = n * l = cos(alpha) */ + norm = norm * (vx0 * vx1 + vy0 * vy1 + vz0 * vz1); + /* Check if the cos is within the angle specified */ + if ((lweight = norm - ppd->lightsCos) < 0) lweight = 0; else lweight *= ppd->lightsScale; + /* lweight = multiplier for pixel intensities */ + lweight = (real_t)(ppd->lightsAmbient + lweight * (1.0 - ppd->lightsAmbient)); + if ((gweight = norm - ppd->lightsScintCos) < 0) gweight = 0; else gweight *= ppd->lightsScintScale; +#if (TEXEL_BSIZE == 3) + if (voxColour != NULL) + { + red = (*voxColour) & 0xff; green = ((*voxColour) >> 8) & 0xff; blue = ((*voxColour) >> 16) & 0xff; + } + addGrey = (RENDER_CAST_TYPE)(((red + green + blue) / 3) * gweight); + red = (uint32)(lweight * red + addGrey); + green = (uint32)(lweight * green + addGrey); + blue = (uint32)(lweight * blue + addGrey); + /* Limit range */ + if (red > 0xff) red = 0xff; + if (green > 0xff) green = 0xff; + if (blue > 0xff) blue = 0xff; +#else + if (voxColour != NULL) + { + pixel = *voxColour; + } + /* Add a ``white''-component depending on scintillation angle and gain parameter */ + pixel = (RENDER_CAST_TYPE)(pixel * (lweight + gweight)); +#ifndef RENDER_FLOAT_TYPE + /* Limit range for non-floating types */ + if (pixel > ((1<<(8*TEXEL_BSIZE))-1)) pixel = ((1<<(8*TEXEL_BSIZE))-1); +#endif +#endif + } + } +#if (TEXEL_BSIZE == 3) + dest.c[0] = (uint8)red; dest.c[1] = (uint8)green; dest.c[2] = (uint8)blue; + dest.c += 3; +#else +# if (TEXEL_BSIZE == 1) + *dest.c++ = (uint8)pixel; +# elif (TEXEL_BSIZE == 2) + *dest.s++ = (uint16)pixel; +# elif (TEXEL_BSIZE == 4) +# ifdef RENDER_FLOAT_TYPE + *dest.f++ = (float)pixel; +# else + *dest.l++ = (uint32)pixel; +# endif +# else + *dest.d++ = (double)pixel; +# endif +#endif + } + } +} diff --git a/applications/rview/generate_trans.sh b/applications/rview/generate_trans.sh new file mode 100644 index 0000000..11ca279 --- /dev/null +++ b/applications/rview/generate_trans.sh @@ -0,0 +1,1013 @@ +#!/usr/bin/ksh +# +# Shell-script that creates C-source code for translating the +# pixmap translation calls used by the wxPixmap-class. +# (C) 2003 Dr. Peter Baumann + + + +# If we don't build to a local file things take forever! +TempSourceFile="/tmp/gtrans_build.x" +# Filenames to store the source under +TransSourceFile="wx_pixmap_translate.c" +TransHeaderFile="wx_pixmap_translate.h" +DitherSourceFile="wx_pixmap_dither.cpp" +DitherHeaderFile="wx_pixmap_dither.h" + + +# Modes to build translators to / from for +ConvertFromModes="0 1 2 3 5 12 15 24" +ConvertToModes="0 1 2 3 4 5 15 24" + +# Print debugging info from each function +OutputDebugInfo=0 + + +# Now machine-dependent setups +type_u8="unsigned char" +type_u16="unsigned short" +type_s16="short" +type_u32="unsigned long" + + +# Bitorder of source and destination data. 0 = lsb, 1 = msb +# These should be identical. +# palette_fill determines the fill order of the palette (also used as screen format): +# bit 0: fill order, bit 1: colour order. i.e. 0 (0bgr), 1 (bgr0), 2 (0rgb), 3 (rgb0) +# sixin16bpp determines which of the three colour-components is 6 bits long +# For typical Unix-systems use 1, 1, 0 +# for NT/LINUX use 0, 0, 2 +src_bitorder=1 +src_byteorder=1 +palette_fill=0 + +sixin16bpp=1 + +# These will be variable +#dest_bitorder=1 +#dest_byteorder=1 + + + + +# The following lines should be OK for any system. + + + +# $1, $2, $3 names of colour components. Should always be ordered red, green blue +InternalComponentsToInt() { + InternRGBToIntL="$1 | ($2<<8) | ($3<<16)" + InternRGBSToIntS15="($1>>3) | ($2<<2) | ($3<<7)" + InternRGBLToIntS15="($1>>3) | (($2&0xf8)<<2) | (($3&0xf8)<<7)" +} + +# $1, $2, $3 names of colour components. Usually (red, green, blue) or (blue, green, red) +# In case of S this means each colour component is 5 bits (top bits in 8) +InitComponentsToInt() { + # Has the destination mode the same byteorder as the source (native) machine? + if [ $src_byteorder -eq $dest_byteorder ]; then + if [ $(( $palette_fill & 1 )) -eq 0 ]; then + RGBToIntL="$1 | ($2<<8) | ($3<<16)" + RGBSToIntS15="($1>>3) | ($2<<2) | ($3<<7)" + RGBLToIntS15="($1>>3) | (($2&0xf8)<<2) | (($3&0xf8)<<7)" + else + RGBToIntL="($1<<8) | ($2<<16) | ($3<<24)" + RGBSToIntS15="($1>>2) | ($2<<3) | ($3<<8)" + RGBLToIntS15="(($1&0xf8)>>2) | (($2&0xf8)<<3) | ($3<<8)" + fi + + # 16bpp asymmetry, only depends on colour order (which is determined outside this function) + if [ $sixin16bpp -eq 0 ]; then + RGBSToIntS16="($1>>2) | ($2<<3) | ($3<<8)" + RGBLToIntS16="($1>>2) | (($2&0xf8)<<3) | (($3&0xf8)<<8)" + elif [ $sixin16bpp -eq 1 ]; then + RGBSToIntS16="($1>>3) | ($2<<3) | ($3<<8)" + RGBLToIntS16="($1>>3) | (($2&0xfc)<<3) | (($3&0xf8)<<8)" + else + RGBSToIntS16="($1>>3) | ($2<<2) | ($3<<7)" + RGBLToIntS16="($1>>3) | (($2&0xf8)<<2) | (($3&0xfc)<<8)" + fi + else + if [ $(( $palette_fill & 1 )) -eq 0 ]; then + RGBToIntL="($1<<24) | ($2<<16) | ($3<<8)" + RGBSToIntS15="($1<<5) | (($2&0x38)<<10) | ($2>>6) | ($3>>1)" + RGBLToIntS15="(($1&0xf8)<<5) | (($2&0x38)<<10) | ($2>>6) | (($3&0xf8)>>1)" + else + RGBToIntL="($1<<16) | ($2<<8) | $3" + RGBSToIntS15="($1<<6) | (($2&0x18)<<11) | ($2>>5) | $3" + RGBLToIntS15="((1&0xf8)<<6) | (($2&0x18)<<11) | ($2>>5) | ($3&0xf8)" + fi + + if [ $sixin16bpp -eq 0 ]; then + RGBSToIntS16="($1<<6) | (($2&0x18)<<11) | ($2>>5) | $3" + RGBLToIntS16="(($1&0xfc)<<6) | (($2&0x18)<<11) | ($2>>5) | ($3&0xf8)" + elif [ $sixin16bpp -eq 1 ]; then + RGBSToIntS16="($1<<5) | (($2&0x1c)<<11) | ($2>>5) | $3" + RGBLToIntS16="(($1&0xf8)<<5) | (($2&0x1c)<<11) | ($2>>5) | ($3&0xf8)" + else + RGBSToIntS16="($1<<5) | (($2&0x38)<<10) | ($2>>6) | $3" + RGBLToIntS16="(($1&0xf8)<<5) | (($2&0x38)<<10) | ($2>>6) | ($3&0xfc)" + fi + fi + +} + + +# $1, $2, $3 = component names of the three colours +InitRGBToInt() { + if [ $(( $palette_fill & 2 )) -eq 0 ]; then + WriteRGB24="*dest_ptr++ = $1, *dest_ptr++ = $2, *dest_ptr++ = $3" + InitComponentsToInt $1 $2 $3 + else + WriteRGB24="*dest_ptr++ = $3, *dest_ptr++ = $2, *dest_ptr++ = $1" + InitComponentsToInt $3 $2 $1 + fi +} + + +# $1 = cast, $2, $3, $4 component names, $5 aux var +InitIntToComponents() { +# Internal representation is constant, RGB starting from bit 0 +IntLToRGBL="$2=$1($5&0xff), $3=$1(($5>>8)&0xff), $4=$1(($5>>16)&0xff)" +IntLToRGBS="$2=$1($5&0xf8), $3=$1(($5>>8)&0xf8), $4=$1(($5>>16)&0xf8)" +IntSToRGB="$2=$1(($5<<3)&0xf8), $3=$1(($5>>2)&0xf8), $4=$1(($5>>7)&0xf8)" +} + + +# $1 = cast. This could contain spaces so make sure to enclose it in quotes! +# $2, $3, $4 = component names of the three colours, $5 = auxiliary variable +InitIntToRGB() { +# Same reason here + InitIntToComponents "$1" $2 $3 $4 $5 +} + + + +# Output functions. + +# Initialise the output: $1 = filename +InitOutput () { + rm -f $1 + CurrentFile=$1 + CurrentIndent=0 + TabChar=$(echo \\011) + CurrentPrefix="" +} + +# Output data _formatted_. Brace checks are primitive but work OK here. +output() { + if [ "$1" = '}' ] && [ $CurrentIndent -gt 0 ]; then + CurrentIndent=$(($CurrentIndent-1)) + CurrentPrefix=${CurrentPrefix%$TabChar} + fi + echo "$CurrentPrefix$1" >> $CurrentFile + if [ "$1" = '{' ]; then + CurrentIndent=$(($CurrentIndent+1)) + CurrentPrefix="$CurrentPrefix$TabChar" + fi +} + + +# Writes some information in the header + +WriteHeaderComment() { + output "/*" + output " * $CurrentFile" + output " * $CurrentDescriptor" + output " * Auto-generated $(date)" + output " * (C) 2003, Dr. Peter Baumann" + output " */" + output "" +} + + +# Get a pixel. +# $1 = src-ldbpp, $2 = dest-ldbpp, $3 = pixel number + +GetPixel() { + if [ $1 -lt 3 ]; then + if [ $src_bitorder -eq 0 ]; then + pixel_value="$(( ($3 * (1<<$1)) & 7))" + else + pixel_value="$(( ( ($src_ppc-1-$3) * (1<<$1) ) & 7))" + fi + subidx=$(( $3 >> (3-$1) )) + pixel_value="((src_ptr[subidx + $subidx] >> $pixel_value ) & $(( (1<<$sbpp)-1 )) )" + else + if [ $1 -eq 12 ]; then + pixel_value="((*src_ptr++)>>4)" + elif [ $1 -eq 24 ]; then + pixel_value="red=*src_ptr++, green=*src_ptr++, blue=*src_ptr++" + else + pixel_value="src_ptr[i + $3]" + fi + fi +} + + +# Get a pixel without the optimizations possible in GetPixel +# ( ((i >> (3-$1)) << (3-$1)) == i ) and translate pixel to RGB +# values. $1 = src-ldbpp, $2 = dest_ldbpp + +GetRGBPixel() { + if [ $1 -lt 3 ]; then + if [ $src_bitorder -eq 0 ]; then + pixel_value="( (i*$sbpp) & 7)" + else + pixel_value="( ($((8-$sbpp)) - i*$sbpp) & 7)" + fi + pixel_value="pixPal=pixmapPalette + ((src_ptr[i >> $(( 3-$1 ))] >> $pixel_value ) & $(( (1<<$sbpp)-1 )) ), red=pixPal->red, green=pixPal->green, blue=pixPal->blue" + else + if [ $1 -eq 24 ]; then + pixel_value="red=*src_ptr++, green=*src_ptr++, blue=*src_ptr++" + elif [ $1 -eq 12 ]; then + pixel_value="red=((*src_ptr++)>>4), green=red, blue=red" + else + pixel_value="src_ptr[i]" + if [ $1 -eq 3 ]; then + pixel_value="pixPal=pixmapPalette + $pixel_value, red=pixPal->red, green=pixPal->green, blue=pixPal->blue" + # Basically $1 can only be 15 + elif [ $1 -eq 15 ] || [ $1 -eq 4 ]; then + pixel_value="val=$pixel_value, $IntSToRGB" + else + pixel_value="val=$pixel_value, $IntLToRGBL" + fi + fi + fi +} + + +# Get a pixel and translate it to the destination mode. +# Arguments as for GetPixel +GetPixelTrans() { + # First create the command to actually get the pixel + GetPixel $1 $2 $3 + + # Now Translate the pixel + + # Source mode not true-colour + if [ $1 -lt 4 ]; then + if [ $2 -gt 3 ]; then + if [ $2 -eq 24 ]; then + pixel_value="val=ttl[$pixel_value], $IntLToRGBL" + else + pixel_value="ttl[$pixel_value]" + fi + else + pixel_value="tt[$pixel_value]" + fi + # Source-mode 15bpp + elif [ $1 -eq 15 ]; then + # destination mode <= 8bpp + if [ $2 -lt 4 ]; then + pixel_value="tt[$pixel_value & 0x7fff]" + # destination mode 15bpp (==> change bitorder) + elif [ $2 -eq 4 ]; then + pixel_value="(val=$pixel_value, $IntSToRGB, $RGBSToIntS16)" + elif [ $2 -eq 5 ]; then + pixel_value="(val=$pixel_value, $IntSToRGB, $RGBToIntL)" + elif [ $2 -eq 15 ]; then + pixel_value="(val=$pixel_value, $IntSToRGB, $RGBSToIntS15)" + elif [ $2 -eq 24 ]; then + pixel_value="(val=$pixel_value, $IntSToRGB)" + fi + # Source-mode 12bpp + elif [ $1 -eq 12 ]; then + if [ $2 -lt 4 ]; then + pixel_value="tt[($pixel_value)&0xff]" + elif [ $2 -eq 4 ]; then + pixel_value="(red=($pixel_value)&0xf8, green=red, blue=red, $RGBSToIntS16)" + elif [ $2 -eq 5 ]; then + pixel_value="(red=$pixel_value, green=red, blue=red, $RGBToIntL)" + elif [ $2 -eq 15 ]; then + pixel_value="(red=($pixel_value)&0xf8, green=red, blue=red, $RGBSToIntS15)" + elif [ $2 -eq 24 ]; then + pixel_value="(red=$pixel_value, green=red, blue=red)" + fi + # Source-mode 32bpp + elif [ $1 -eq 5 ]; then + if [ $2 -eq 24 ]; then + pixel_value="(val=$pixel_value, $IntLToRGBL)" + else + # Destination mode 32bpp (==> change bitorder) + if [ $2 -eq 5 ]; then + pixel_value="(val=$pixel_value, $IntLToRGBL, $RGBToIntL)" + else + if [ $2 -eq 4 ]; then + pixel_value="(val=$pixel_value, $IntLToRGBS, $RGBSToIntS16)" + else + if [ $2 -eq 15 ]; then + pixel_value="(val=$pixel_value, $IntLToRGBS, $RGBSToIntS15)" + else + pixel_value="tt[(val=$pixel_value, $IntLToRGBS, $InternRGBSToIntS15)]" + fi + fi + fi + fi + # Source mode 24bpp + elif [ $1 -eq 24 ]; then + if [ $2 -eq 5 ]; then + pixel_value="($pixel_value, $RGBToIntL)" + elif [ $2 -ne 24 ]; then + if [ $2 -eq 4 ]; then + pixel_value="($pixel_value, $RGBLToIntS16)" + else + if [ $2 -lt 4 ]; then + pixel_value="tt[($pixel_value, $InternRGBLToIntS15)]" + else + pixel_value="($pixel_value, $RGBLToIntS15)" + fi + fi + fi + fi + + # And finally shift the pixel for non-24bpp destination modes + if [ $2 -ne 24 ]; then + if [ $2 -eq 15 ]; then + usedld=4 + else + usedld=$2 + fi + if [ $2 -lt 3 ]; then + if [ $dest_bitorder -eq 0 ]; then + pixel_value="($pixel_value << $(( ( $3*(1<<$usedld) ) & 7 )) )" + else + pixel_value="($pixel_value << $(( ( ($dest_ppw-1-$3)*(1<<$usedld) ) & 7 )) )" + fi + else + if [ $src_byteorder -eq 0 ]; then + pixel_value="($pixel_value << $(( ( $3*(1<<$usedld) ) & 31 )) )" + else + pixel_value="($pixel_value << $(( ( ($dest_ppw-1-$3)*(1<<$usedld) ) & 31 )) )" + fi + fi + fi +} + + + +# Sets the sbpp and dbpp values according to $1 and $2 +SetModeBpp() { + if [ $1 -lt 6 ]; then + sbpp=$((1<<$1)) + else + sbpp=$1 + fi + if [ $2 -lt 6 ]; then + dbpp=$((1<<$2)) + else + dbpp=$2 + fi +} + + + +# Outputs a function prototype signature if the function signature is not empty +OutputFuncSig() { + if [ "$func_sig" != "" ]; then + output "$func_sig;" + output + fi +} + + +# Sets the variable func_sig to the signature of the translator function with +# $1 = src-ldbpp, $2 = dest_ldbpp +# if either is larger than 5 it's interpreted as bpp rather than ldbpp + +TranslatorSignature() { + + SetModeBpp $1 $2 + + if [ $sbpp -le 8 ] && [ $dbpp -eq 15 ]; then + func_sig="" + return + fi + + if [ $src_bitorder -eq $dest_bitorder ] && [ $src_byteorder -eq $dest_byteorder ]; then + sig_post="" + else + sig_post="i" + fi + if [ $dbpp -gt 8 ]; then + if [ $(( palette_fill & 2 )) -eq 0 ]; then + sig_post=${sig_post}rgb + else + sig_post=${sig_post}bgr + fi + fi + func_sig="void wx_pixmap_translate_${sbpp}_to_${dbpp}${sig_post} (const $type_u8 *src, $type_u8 *dest, int width, int height, int srcPitch, int destPitch, const $type_u8 *tt)" +} + + + +# Arguments: $1 = src-ldbpp, $2 = dest-ldbpp + +MakeTranslationFunction() { + + TranslatorSignature $1 $2 + + if [ "$func_sig" = "" ]; then + return + fi + + output "$func_sig" + output \{ + + # Produce translation code for ALL modes because the order of the colour components + # and the endianness might differ. + if [ $1 -ne 24 ] || [ $(( $palette_fill & 2 )) -ne 0 ]; then + need_translator=1 + else + need_translator=0 + fi + + if [ $1 -ne $2 ]; then + need_translator=1 + if [ $1 -lt 4 ] && [ $2 -eq 15 ]; then + need_translator=0 + fi + fi + + # Is a translator function necessary? + if [ $need_translator -ne 0 ]; then + + # Determines what special variables are needed + needs_val=0 + needs_rgb=0 + + # setup variable block + output "const $type_u8 *src_line = (const $type_u8 *)src;" + output "$type_u8 *dest_line = dest;" + + case $sbpp in + 1 | 2 | 4 | 8 | 24) type_srcptr=$type_u8 ;; + 12 | 15 | 16) type_srcptr=$type_u16 ;; + 32) type_srcptr=$type_u32 ;; + esac + output "register const $type_srcptr *src_ptr;" + + if [ $dbpp -ge 8 ] && [ $dbpp -ne 24 ]; then + type_destptr=$type_u32; + else + type_destptr=$type_u8; + fi + output "register $type_destptr *dest_ptr;" + + if [ $1 -gt 3 ] && [ ! $1 -eq 12 ] && [ $2 -gt 3 ]; then + needs_rgb=1; needs_val=1 + elif [ $sbpp -eq 24 ] || [ $sbpp -eq 32 ] || [ $dbpp -eq 24 ] || [ $dbpp -eq 15 ]; then + needs_rgb=1; needs_val=1 + fi + if [ $1 -eq 24 ] && [ $2 -gt 3 ]; then + needs_val=0 + fi + if [ $1 -eq 12 ] && [ $dbpp -ge 15 ]; then + needs_rgb=1; + fi + + # Cast translation table if ldsrc < 4 and lddest > 3 + if [ $1 -lt 4 ] && [ $2 -gt 3 ]; then + output "const $type_u32 *ttl = (const $type_u32 *)tt;" + fi + + output "register int i;" + output "int j, i_high;" + if [ $1 -lt 3 ]; then + output "register int subidx;" + fi + + # Evaluate pixels per char in source- and pixels per word in dest mode + src_ppc=$((8/$sbpp)) + if [ $dbpp -eq 15 ] || [ $dbpp -eq 12 ]; then + dest_ppw=2 + elif [ $dbpp -ge 8 ]; then + dest_ppw=$((32/$dbpp)) + else + dest_ppw=$((8/$dbpp)) + fi + if [ $src_ppc -gt $dest_ppw ]; then + passes=$(($src_ppc/$dest_ppw)); upper_modulo=$(($src_ppc-1)); + else + passes=1; upper_modulo=$(($dest_ppw-1)); + fi + + if [ $(($passes*$dest_ppw)) -gt 2 ]; then + needs_val=1 + fi + + # Trap some very special cases (not important, just kill ``unused variable'' warnings) + if [ $sbpp -eq 12 ]; then + if [ $dbpp -eq 15 ] || [ $dbpp -eq 24 ]; then + needs_val=0 + fi + fi + if [ $sbpp -eq 24 ] && [ $dbpp -eq 4 ]; then + needs_val=0 + fi + + if [ $needs_val -eq 1 ]; then + if [ $dbpp -lt 8 ] && [ $sbpp -ne 32 ]; then + output "$type_u8 val;" + else + output "$type_u32 val;" + fi + fi + if [ $needs_rgb -eq 1 ]; then + output "$type_u8 red, green, blue;" + fi + + output "" + if [ $OutputDebugInfo -ne 0 ]; then + output 'printf("Plot '"$sbpp - $dbpp:"' width = %d, height = %d, pitch(src) = %d, pitch(dest) = %d\\n", width, height, srcPitch, destPitch); fflush(stdout);' + fi + output "i_high = (width & ~$upper_modulo );" + + # now for the main loop + output "for (j=0; j<height; j++, src_line += srcPitch, dest_line += destPitch)" + output \{ + output "src_ptr = (const $type_srcptr *)src_line; dest_ptr = ($type_destptr *)dest_line;" + output "for (i=0; i<i_high; i+=$(($passes*$dest_ppw)) )" + output \{ + + if [ $1 -lt 3 ]; then + output "subidx = (i >> $((3-$1)) );" + fi + + pass=$passes + while [ $pass -gt 0 ]; do + if [ $2 -ne 24 ]; then + output "*dest_ptr =" + fi + loop=0 + while [ $loop -lt $dest_ppw ]; do + GetPixelTrans $1 $2 $(( ($passes - $pass)*$dest_ppw + $loop )) + if [ $dbpp -eq 24 ]; then + output "$pixel_value, $WriteRGB24;" + else + output "$pixel_value;" + if [ $loop -ne $((dest_ppw - 1)) ]; then + output "*dest_ptr |= " + fi + fi + loop=$(($loop+1)) + done + if [ $2 -ne 24 ]; then + output "dest_ptr++;" + fi + pass=$(($pass-1)) + done; + + output \} + + # Now pad the remaining bytes for modes with less than 32bpp + if [ $(($passes*$dest_ppw)) -gt 1 ]; then + output "if (i < width)" + output \{ + GetPixelTrans $1 $2 0 + if [ $1 -lt 3 ]; then + output "subidx = (i >> $((3-$1)) );" + fi + if [ $(($passes*$dest_ppw)) -eq 2 ]; then + if [ $2 -ne 24 ]; then + output "*dest_ptr++ = $pixel_value;" + else + output "$pixel_value, $WriteRGB24;" + fi + else + if [ $2 -ne 24 ]; then + output "val = $pixel_value;" + else + output "$pixel_value, $WriteRGB24;" + fi + loop=1 + while [ $loop -lt $(($passes*$dest_ppw - 1)) ]; do + GetPixelTrans $1 $2 $loop + output "if (i+$loop < width)" + output \{ + if [ $2 -ne 24 ]; then + if [ $(($loop & ($dest_ppw - 1) )) -eq 0 ]; then + output "*dest_ptr++ = val; val = $pixel_value;" + else + output "val |= $pixel_value;" + fi + else + output "$pixel_value, $WriteRGB24;" + fi + output \} + loop=$(($loop+1)) + done + if [ $2 -ne 24 ]; then + output "*dest_ptr++ = ($type_destptr)val;" + fi + fi + output \} + fi + + output \} + + if [ $OutputDebugInfo -ne 0 ]; then + output 'printf("OK.\\n"); fflush(stdout);' + fi + + # from 15->15 / 24->24 / 32->32 trap + fi + + output \} +} + + + +# Signature of a dithering function +# This is realised as wxPixmap member-functions +DithererSignature() { + + SetModeBpp $1 $2 + + if [ $src_bitorder -eq $dest_bitorder ] && [ $src_byteorder -eq $dest_byteorder ]; then + sig_post="" + else + sig_post="i" + fi + func_sig="void wxPixmap::dither_${sbpp}_to_${dbpp}${sig_post} ($type_u8 *dest, int destPad)" +} + + + + +# Ditherer from mode $1 to mode $2 + +MakeDitheringFunction() { + # Only make ditherers for displays with <= 8bpp + if [ $2 -lt 4 ]; then + + DithererSignature $1 $2 + output "$func_sig" + + output \{ + + need_idx=0 + + output "$type_s16 *errors, *err_ptr;" + output "$type_s16 red, green, blue, err_red_r, err_green_r, err_blue_r, err_red_d, err_green_d, err_blue_d;" + output "const $type_u8 *src_line = (const $type_u8 *)data;" + output "$type_u8 *dest_line = ($type_u8 *)dest;" + + case $1 in + 0 | 1 | 2 | 3 | 24) type_srcptr=$type_u8 ;; + 4 | 12 | 15) type_srcptr=$type_u16 ;; + 5) type_srcptr=$type_u32 ;; + esac + + output "register const $type_srcptr *src_ptr;" + output "$type_u8 *dest_ptr;" + output "$type_u8 r, g, b;" + output "int i, j, destPitch;" + + output "register wx_permute_cmap *pixPal;" + + if [ $1 -eq 4 ] || [ $1 -eq 15 ]; then + output "$type_u16 val;" + elif [ $1 -eq 5 ]; then + output "$type_u32 val;" + fi + if [ $2 -lt 3 ]; then + output "$type_u8 pixels;" + output "int shift;" + fi + + if [ $OutputDebugInfo -ne 0 ]; then + output "*errorstr << \"Dither $1 to $2\" << endl;" + fi + + output "" + output "if ((errors = ($type_s16 *)malloc(3*width*sizeof($type_s16))) == NULL) return;" + output "memset((void*)errors, 0, 3*width*sizeof($type_s16));" + + # Pixels per char in src / dest mode + src_ppc=$(( 8 >> $1 )) + dest_ppc=$(( 8 >> $2 )) + + if [ $dest_bitorder -eq 0 ]; then + shift_start=0; shift_step=$dbpp; shift_end=8; + else + shift_start=$((8-$dbpp)); shift_step=$((-$dbpp)); shift_end=$((0-$dbpp)) + fi + + output "destPitch = ((width*$dbpp + destPad-1) & ~(destPad-1)) >> 3;" + + output "for (j=0; j<height; j++, src_line += pitch, dest_line += destPitch)" + output \{ + output "src_ptr = (const $type_srcptr *)src_line; dest_ptr = ($type_u8 *)dest_line;" + output "err_red_r = 0; err_green_r = 0; err_blue_r = 0;" + output "err_red_d = 0; err_green_d = 0; err_blue_d = 0;" + if [ $2 -lt 3 ]; then + output "pixels = 0; shift = $shift_start;" + fi + output "for (i=0, err_ptr=errors; i<width; i++, err_ptr += 3)" + output \{ + GetRGBPixel $1 $2 + output "$pixel_value;" + output "red += err_red_r + err_ptr[0]; green += err_green_r + err_ptr[1]; blue += err_blue_r + err_ptr[2];" + output "if (red < 0) r=0; else if (red > 255) r=255; else r=red;" + output "if (green < 0) g=0; else if (green > 255) g=255; else g=green;" + output "if (blue < 0) b=0; else if (blue > 255) b=255; else b=blue;" + output "pixPal = parentPalette + (this->*colour_matcher)(r, g, b);" + output "red = ($type_s16)r - ($type_s16)(pixPal->red); green = ($type_s16)g - ($type_s16)(pixPal->green); blue = ($type_s16)b - ($type_s16)(pixPal->blue);" + output "err_red_r = (3*red)/8; err_green_r = (3*green)/8; err_blue_r = (3*blue)/8;" + output "err_ptr[0] = err_red_r + err_red_d; err_ptr[1] = err_green_r + err_green_d; err_ptr[2] = err_blue_r + err_blue_d;" + output "err_red_d = red/4; err_green_d = green/4; err_blue_d = blue/4;" + output "err_red_r = red - err_red_d - err_red_r; err_green_r = green - err_green_d - err_green_r; err_blue_r = blue - err_blue_d - err_blue_r;" + + if [ $2 -eq 3 ]; then + output "*dest_ptr++ = pixPal->number;" + else + output "pixels |= (pixPal->number << shift);" + output "shift += $shift_step;" + output "if (shift == $shift_end)" + output \{ + output "*dest_ptr++ = pixels; pixels = 0; shift = $shift_start;" + output \} + fi + output \} + if [ $2 -ne 3 ]; then + output "if (shift != $shift_start) *dest_ptr++ = pixels;" + fi + output \} + output "free(errors);" + + output \} + + # ($2 < 4) + fi +} + + + + +# Customizations +if [ "$1" = "0" ] || [ "$1" = "1" ]; then + src_bitorder=$1 +fi +if [ "$2" = "0" ] || [ "$2" = "1" ]; then + src_byteorder=$2 +fi +if [ "$3" = "0" ] || [ "$3" = "1" ] || [ "$3" = "2" ] || [ "$3" = "3" ]; then + palette_fill=$3 +fi + +localdisplay=0 +if [ "$4" = "-local" ]; then + localdisplay=1 +fi + +echo "Using src_bitorder=$src_bitorder, src_byteorder=$src_byteorder, palette_fill=$palette_fill" +if [ $localdisplay -eq 0 ]; then + echo "Create translators for different endianness" +else + echo "Local displays only, one setting." +fi + +echo "Creating ditherers for modes $ConvertFromModes --> $ConvertToModes:" + +CurrentDescriptor="Dithering functions from/to various depths." + +echo "Creating source file $DitherSourceFile ..." + +InitOutput $TempSourceFile + +WriteHeaderComment + +output "// This file must be included from wx_pixmap.cpp" +output "" + +# Make translation functions + +InternalComponentsToInt red green blue + +# We have to init these macros for native and inverted byteorder + +dest_bitorder=$src_bitorder; dest_byteorder=$src_byteorder +InitRGBToInt red green blue +InitIntToRGB "($type_s16)" red green blue val + +for source_mode in $ConvertFromModes; do + for dest_mode in $ConvertToModes; do + MakeDitheringFunction $source_mode $dest_mode + output "" + done +done + +# Create additional plotters for network displays? +if [ $localdisplay -eq 0 ]; then + + dest_bitorder=$(( $src_bitorder ^ 1 )); dest_byteorder=$(( $src_byteorder ^ 1)) + InitRGBToInt red green blue + InitIntToRGB "($type_s16)" red green blue val + + for source_mode in $ConvertFromModes; do + for dest_mode in $ConvertToModes; do + MakeDitheringFunction $source_mode $dest_mode + output "" + done + done + +fi # localdisplay + +mv $TempSourceFile $DitherSourceFile + +echo "Creating header file $DitherHeaderFile ..." + +InitOutput $TempSourceFile + +for source_mode in $ConvertFromModes; do + for dest_mode in $ConvertToModes; do + if [ $dest_mode -lt 4 ]; then + dest_bitorder=$src_bitorder; dest_byteorder=$src_byteorder + DithererSignature $source_mode $dest_mode + output "$func_sig;" + if [ $localdisplay -eq 0 ]; then + dest_bitorder=$(( $src_bitorder ^ 1 )); dest_byteorder=$(( $src_byteorder ^ 1)) + DithererSignature $source_mode $dest_mode + output "$func_sig;" + fi + fi + done +done + +mv $TempSourceFile $DitherHeaderFile + + + + +# Now down to business: create all translation functions as +# C code. + + +echo "Creating converters for modes $ConvertFromModes --> $ConvertToModes:" + +CurrentDescriptor="Translator functions for bitmaps from/to various depths." + +echo "Creating source file $TransSourceFile ..." + +InitOutput $TempSourceFile + +if [ $OutputDebugInfo -ne 0 ]; then + output "#include <stdio.h>" +fi +output "#include \"$TransHeaderFile\"" +output "" + +WriteHeaderComment + +dest_bitorder=$src_bitorder; dest_byteorder=$src_byteorder +InitRGBToInt red green blue +InitIntToRGB "($type_u8)" red green blue val + +for source_mode in $ConvertFromModes; do + for dest_mode in $ConvertToModes; do + MakeTranslationFunction $source_mode $dest_mode + output "" + done +done + +palette_fill=$(( $palette_fill ^ 2 )) + +InitRGBToInt red green blue +InitIntToRGB "($type_u8)" red green blue val + +for source_mode in $ConvertFromModes; do + for dest_mode in $ConvertToModes; do + if [ $dest_mode -gt 3 ]; then + MakeTranslationFunction $source_mode $dest_mode + output "" + fi + done +done + +palette_fill=$(( $palette_fill ^ 2 )) + +if [ $localdisplay -eq 0 ]; then + dest_bitorder=$(( $src_bitorder ^ 1 )); dest_byteorder=$(( $src_byteorder ^ 1 )) + + InitRGBToInt red green blue + InitIntToRGB "($type_u8)" red green blue val + + for source_mode in $ConvertFromModes; do + for dest_mode in $ConvertToModes; do + MakeTranslationFunction $source_mode $dest_mode + output "" + done + done + + palette_fill=$(( $palette_fill ^ 2 )) + + InitRGBToInt red green blue + InitIntToRGB "($type_u8)" red green blue val + + for source_mode in $ConvertFromModes; do + for dest_mode in $ConvertToModes; do + if [ $dest_mode -gt 3 ]; then + MakeTranslationFunction $source_mode $dest_mode + output "" + fi + done + done + + palette_fill=$(( $palette_fill ^ 2 )) + +fi # localdisplay + +InitRGBToInt red green blue +InitIntToRGB "($type_u8)" red green blue + + +mv $TempSourceFile $TransSourceFile + + +# Then write the header file + +echo "Creating header file $TransHeaderFile ..." + +InitOutput $TempSourceFile + +output "#ifndef _WX_PIXMAP_TRANSLATE_H_" +output "#define _WX_PIXMAP_TRANSLATE_H_" +output "" +output "#ifdef __cplusplus" +output 'extern "C" {' +output "#endif" +output "" + +WriteHeaderComment + +output "#define WX_PIXMAP_SRC_BITORDER $src_bitorder" +output "#define WX_PIXMAP_SRC_BYTEORDER $src_byteorder" +output "#define WX_PIXMAP_PALETTE_FILL $palette_fill" +output "" + + +dest_bitorder=$src_bitorder; dest_byteorder=$src_byteorder; +InitRGBToInt "(red)" "(green)" "(blue)" +InitIntToRGB "" "(red)" "(green)" "(blue)" "(val)" + +output "/* The following macros are for internal use only */" +output "#define _RGB_TO_PALETTE_LONG(red,green,blue) ($RGBToIntL)" +output "#define _RGBS_TO_PALETTE_SHORT15(red,green,blue) ($RGBSToIntS15)" +output "#define _RGBL_TO_PALETTE_SHORT15(red,green,blue) ($RGBLToIntS15)" +output "#define _RGBS_TO_PALETTE_SHORT16(red,green,blue) ($RGBSToIntS16)" +output "#define _RGBL_TO_PALETTE_SHORT16(red,green,blue) ($RGBLToIntS16)" + +if [ $dest_bitorder -eq 0 ]; then dest_bitorder=1; else dest_bitorder=0; fi +if [ $dest_byteorder -eq 0 ]; then dest_byteorder=1; else dest_byteorder=0; fi +InitRGBToInt "(red)" "(green)" "(blue)" +InitIntToRGB "" "(red)" "(green)" "(blue)" "(val)" +output "#define _RGB_TO_PALETTE_LONGi(red,green,blue) ($RGBToIntL)" +output "#define _RGBS_TO_PALETTE_SHORT15i(red,green,blue) ($RGBSToIntS15)" +output "#define _RGBL_TO_PALETTE_SHORT15i(red,green,blue) ($RGBLToIntS15)" +output "#define _RGBS_TO_PALETTE_SHORT16i(red,green,blue) ($RGBSToIntS16)" +output "#define _RGBL_TO_PALETTE_SHORT16i(red,green,blue) ($RGBLToIntS16)" +output "" + +output "/* Use these macros if you use wxPixmap class with translations turned on */" +output "#define PALETTE_LONG_TO_RGB(val,red,green,blue) ($IntLToRGBL)" +output "#define PALETTE_SHORT_TO_RGB(val,red,green,blue) ($IntSToRGB)" +output "#define RGBS_TO_PALETTE_SHORT(red,green,blue) (((red)>>3) | ((green)<<2) | ((blue)<<7))" +output "#define RGBL_TO_PALETTE_SHORT(red,green,blue) (((red)>>3) | (((green)&0xf8)<<2) | (((blue)&0xf8)<<7))" +output "#define RGB_TO_PALETTE_LONG(red,green,blue) ((red) | ((green)<<8) | ((blue)<<16))" +output "" + +for source_mode in $ConvertFromModes; do + for dest_mode in $ConvertToModes; do + dest_bitorder=$src_bitorder; dest_byteorder=$src_byteorder + TranslatorSignature $source_mode $dest_mode + OutputFuncSig + if [ $dest_mode -gt 3 ]; then + palette_fill=$(( $palette_fill ^ 2 )) + TranslatorSignature $source_mode $dest_mode + OutputFuncSig + palette_fill=$(( $palette_fill ^ 2 )) + fi + if [ $localdisplay -eq 0 ]; then + dest_bitorder=$(( src_bitorder ^ 1 )); dest_byteorder=$(( src_byteorder ^ 1 )) + TranslatorSignature $source_mode $dest_mode + OutputFuncSig + if [ $dest_mode -gt 3 ]; then + palette_fill=$(( $palette_fill ^ 2 )) + TranslatorSignature $source_mode $dest_mode + OutputFuncSig + palette_fill=$(( $palette_fill ^ 2 )) + fi + fi + done + output "" +done + +output "#ifdef __cplusplus" +output "}" +output "#endif" +output "" + +output "#endif" + +mv $TempSourceFile $TransHeaderFile diff --git a/applications/rview/labelManager.cpp b/applications/rview/labelManager.cpp new file mode 100644 index 0000000..4134922 --- /dev/null +++ b/applications/rview/labelManager.cpp @@ -0,0 +1,314 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * The label manager class is used to lookup label:value pairs + * stored in a text file. The constructor reads in the file whose + * name is passed to it and sorts the non-empty, non-commentary + * lines (commentary lines have a '#' as the first non-white + * character in a line) in ascending order to make binary searching + * possible. The binary search is performed by the lookup member + * function. + * + * COMMENTS: None + */ + + + + +#include <stdio.h> +#include <string.h> +#include "iostream.h" + + + +#include "labelManager.hh" + + + + + +/* + * Load the resource file, parse and sort it. + */ + +labelManager::labelManager(const char *resourceFile) +{ + FILE *fp; + size_t filesize; + int i; + char c, *b, *upper; + + buffer = NULL; lineTable = NULL; lines = 0; + strncpy(badSymbol, "???", 4); + + if ((fp = fopen(resourceFile, "r")) == NULL) + { + cerr << "Unable to open resource file " << resourceFile << endl; + return; + } + + // Position to end of file to determine its length. + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + + if ((buffer = new char[filesize+1]) == NULL) + { + cerr << "Not enough memory for buffer!" << endl; + } + + fseek(fp, 0, SEEK_SET); + // Read in the file and make sure it's terminated by a newline char. + fread((void*)buffer, 1, filesize, fp); + buffer[filesize] = '\n'; + fclose(fp); + + upper = buffer + filesize; b = buffer; + + // Pass 1: calculate how much memory is needed for the lineTable. + do + { + // for (i=0; b[i] != '\n'; i++) {cout << b[i];} cout << endl; + // Ignore leading whitespace + do {c = *b++;} while ((c == ' ') || (c == '\t')); + // Commentary line? + if (c == '#') + { + do {c = *b++;} while (c != '\n'); + } + // Line not empty? + else if (c != '\n') + { + lines++; i = 0; i = (b - buffer) - 1; + // Make sure it contains a colon! + do {c = *b++; if (c == ':') {i = -1;}} while (c != '\n'); + if (i >= 0) + { + cerr << "Bad line format (missing colon): "; + while (buffer[i] != '\n') {cout << buffer[i]; i++;} cout << endl; + delete [] buffer; buffer = NULL; lines = 0; return; + } + } + } + while (b < upper); + + if ((lineTable = new char*[lines]) == NULL) + { + cerr << "Not enough memory for lineTable!" << endl; + delete [] buffer; buffer = NULL; lines = 0; return; + } + + b = buffer; i = 0; + + // Pass 2: fill in lineTable + do + { + do {c = *b++;} while ((c == ' ') || (c == '\t')); + if (c == '#') + { + do {c = *b++;} while (c != '\n'); + } + else if (c != '\n') + { + lineTable[i++] = b-1; + do {c = *b++;} while (c != '\n'); + // terminate all non-empty lines with '\0' + *(b-1) = '\0'; + } + } + while (b < upper); + + if ((unsigned int)i != lines) + { + cerr << "Fatal error: passes incompatible!" << endl; + } + + sortResources(0, lines-1); + + /*cout << lines << " label definitions found." << endl; + for (i=0; i<lines; i++) + { + cout << lineTable[i] << endl; + }*/ +} + + + +/* + * Quicksort, using the label identifiers as key. + */ + +void labelManager::sortResources(int from, int to) +{ + int i, j; + char *swap, *l, *m; + + while (from < to) + { + i = (from + to) / 2; + swap = lineTable[from]; lineTable[from] = lineTable[i]; lineTable[i] = swap; + j = from; + + for (i=from+1; i<=to; i++) + { + l = lineTable[from]; m = lineTable[i]; + while ((*l == *m) && (*l != ':')) {l++; m++;} + // End of first idf reached ==> second string can at best be =, not < + if (*l == ':') continue; + // If end of second idf was reached the second string is <. Otherwise check chars. + if ((*m == ':') || (*m < *l)) + { + j++; + swap = lineTable[j]; lineTable[j] = lineTable[i]; lineTable[i] = swap; + } + } + swap = lineTable[from]; lineTable[from] = lineTable[j]; lineTable[j] = swap; + + // Select cheaper recursion branch + if ((j - from) < (to - j)) + { + sortResources(from, j-1); + from = j+1; + } + else + { + sortResources(j+1, to); + to = j-1; + } + } +} + + + + +/* + * Destructor: just frees all dynamically allocated memory. + */ + +labelManager::~labelManager(void) +{ + if (lineTable != NULL) delete [] lineTable; + if (buffer != NULL) delete [] buffer; +} + + + + +/* + * Look up a symbol and return a read-only pointer to the symbol value. If + * this symbol doesn't exist it returns a pointer to the badSymbol-member. + */ + +char *labelManager::lookup(const char *symbol) +{ + int at, step, iter; + char *s, *l; + + if (lines == 0) return badSymbol; + at = (lines+1)/2; step = (at+1)/2; iter = lines << 1; + if (at >= (int)lines) at = lines-1; + // Do a binary search over the sorted items + while (iter != 0) + { + // The symbol may be terminated by a colon or any character <= 32 + s = (char*)symbol; l = lineTable[at]; + while ((*s == *l) && (*l != ':')) {s++; l++;} + // Was the symbol's end reached? + if (((unsigned char)(*s) <= 32) || (*s == ':')) + { + // Both read to the end. Success. + if (*l == ':') return (l+1); + // Symbol's end reached but not label's ==> label >, i.e. step down + at -= step; if (at < 0) at = 0; + } + else + { + // label's end reached ==> label <, i.e. step up. Otherwise use chars. + if ((*l == ':') || (*l < *s)) + { + at += step; if (at >= (int)lines) at = lines-1; + } + else + { + at -= step; if (at < 0) at = 0; + } + } + step = (step+1)/2; iter >>= 1; + //cout << at << ", " << step << ", " << iter << ": " << lineTable[at] << endl; + } + return badSymbol; +} + + + + +/* + * Returns the number of label definitions. + */ + +int labelManager::numberOfLabels(void) +{ + return lines; +} + + + +/* + * Returns a read-only pointer to the index-th label in the sorted table. + * Normally this shouldn't be used from an outside application, but it + * may be handy for debugging. + */ + +char *labelManager::returnLabelNumber(unsigned int index) +{ + if (index >= lines) + { + return NULL; + } + return lineTable[index]; +} + + + + +/* TEST +int main(int argc, char *argv[]) +{ + int i; + char *b; + labelManager lman("labels.txt"); + + for (i=0; i<lman.numberOfLabels(); i++) + { + b = lman.returnLabelNumber(i); + while (*b != ':') cout << *b++; + cout << ": " << lman.lookup(lman.returnLabelNumber(i)) << endl; + } + + cout << lman.lookup("hubba") << endl; + + return 0; +} +*/ diff --git a/applications/rview/labelManager.hh b/applications/rview/labelManager.hh new file mode 100644 index 0000000..a4a81de --- /dev/null +++ b/applications/rview/labelManager.hh @@ -0,0 +1,69 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * The label manager class is used to lookup label:value pairs + * stored in a text file. The constructor reads in the file whose + * name is passed to it and sorts the non-empty, non-commentary + * lines (commentary lines have a '#' as the first non-white + * character in a line) in ascending order to make binary searching + * possible. The binary search is performed by the lookup member + * function. + * + * COMMENTS: None + */ + + + +#ifndef _LABEL_MANAGER_H_ +#define _LABEL_MANAGER_H_ + + + +class labelManager +{ + public: + + labelManager(const char *resourceFile); + ~labelManager(void); + char *lookup(const char *symbol); + // These two should be handled with care. + int numberOfLabels(void); + char *returnLabelNumber(unsigned int index); + + + private: + + void sortResources(int from, int to); + + char *buffer; + char **lineTable; + unsigned int lines; + char badSymbol[4]; +}; + + + +#endif diff --git a/applications/rview/labels.txt b/applications/rview/labels.txt new file mode 100644 index 0000000..37037ae --- /dev/null +++ b/applications/rview/labels.txt @@ -0,0 +1,451 @@ +# Text used in rview +# +# Commentary lines have a '#' as the first non-white character. +# Non-empty lines where the first character differs from '#' +# are interpreted as "identifier:text" combinations. If the colon +# is missing an error is generated. + +# Window titles +titleRview:rView +titleCollLook:Lookup a collection +titleLookScaleColl:Lookup scaled collection +titleCollDel:Delete a collection +titleCollCrt:Create a collection +titleInsertMdd:Insert MDD object +titleInsertMddPro:Insert projected MDD object +titleResult:Results +titlePrefs:Edit preferences +titleImageFlat:Flat image viewer +titleImageVolume:Volumetric image viewer +titleImageHeight:Height field image viewer +titleImageScaled:Scaled image viewer +titleImageOrtho:Orthosection viewer +titleChart:Chart display mode +titleTable:Tabular display mode +titleThumb:Thumbnail images +titleSound:Sound player +titleQuery:Query Editor +titleQueryLoad:Load query +titleQuerySave:Save query +titleImgSetup:Image settings +titleColourspace:Colourspace setup +titleAbout:About rView... +titleRendererCtrl:Renderer controls +titleRendererView:Renderer view +titleStringSet:String result set +titleTypeMan:Type manager +titleEnterType:Resolve unknown base type +titleOrthoLook:Lookup orthosection + +# Menu items in the main frame menu +menMainFile:File +menMainView:Viewers +menMainColl:Collections +menMainHelp:Help + +menMainFileQuery:&Query +menMainFilePrefs:&Prefs +menMainFileExit:E&xit + +menMainViewOpen:&Open +menMainViewClose:&Close all + +menMainCollLook:&Lookup +menMainCollLkScale:LU &scaled +menMainCollLkOrtho:LU &ortho +menMainCollCrt:&Create +menMainCollDel:&Delete + +menMainHelpAbt:&About + +# Help text for those menu items +helpMainFileQuery:Opens a query dialogue box. +helpMainFilePrefs:Edit the preferences. +helpMainFileExit:Exit rView. + +helpMainViewOpen:Opens a viewer from a file. +helpMainViewClose:Close all viewers. + +helpMainCollLook:Lookup a collection. +helpMainCollLkScale:Lookup a scaled collection. +helpMainCollLkOrtho:Lookup an orthosection (3D) +helpMainCollCrt:Create a collection. +helpMainCollDel:Delete a collection. + +helpMainHelpAbt:Information about this program. + + +# Menu items in the results frame menu +menRsltItem:Item +menRsltSlct:Selection + +menRsltItemOpAll:Open &All +menRsltItemThumbAll:&Thumbnail All +menRsltItemClose:&Close + +menRsltSlctSlctAll:Select &All +menRsltSlctClear:&Clear +menRsltSlctOpen:&Open +menRsltSlctThumb:&Thumbnails +menRsltSlctDel:&Delete +menRsltSlctEndian:&Endian change +menRsltSlctTypeMan:Type &Manager +menRsltSlctInfo:&Info + +# Help text for those menu items +helpRsltItemOpAll:Open all objects. +helpRsltItemThumbAll:Display all images as thumbnails. +helpRsltItemClose:Close this window. + +helpRsltSlctSlctAll:Select all objects. +helpRsltSlctClear:Clears the selection. +helpRsltSlctOpen:Open the selected objects. +helpRsltSlctThumb:Display thumbnails of the selected object(s). +helpRsltSlctDel:Delete the selected object(s). +helpRsltSlctEndian:Change the endianness of the selected object(s). +helpRsltSlctTypeMan:Call type manager for the selected object(s). +helpRsltSlctInfo:Display information about the selected object(s). + + +# Menu items in the display window menus +menDispData:Data +menDispDataIsrt:&Insert +menDispDataIsrtPro:Insert &Proj +menDispDataSave:&Save +menDispDataSaveTIFF:Save &TIFF +menDispDataClose:&Close +menDispView:View +menDispViewSave:Save +menDispViewLoad:Load +menDispViewShow:Show + +# Additional menus in chart mode +menChartMode:Modes +menChartModeBar:Bar +menChartModeLine:Line +menChartModeSpline:Spline + +# Additional menus in image mode +menImgMode:Modes +menImgModeFlat:Flat +menImgModeSurf:Surface +menImgModeVoxel:Voxel +menImgModeHeight:Height +menImgSetup:Settings +menImgSetupRender:Renderer... +menImgSetupMovie:Movie playback +menImgMovieOnce:Once +menImgMovieStart:Same direction +menImgMovieSwitch:Switch direction +menImgSetupRctrl:Renderer controls... + +# Additional menus in table mode +menTabMode:Base +menTabModeDec:Decimal +menTabModeOct:Octal +menTabModeHex:Hex + + +# Menu items in query window +menQueryFile:File +menQueryEdit:Edit +menQueryHotlist:List +menQueryHelp:Help +menQueryFileOpen:&Open +menQueryFileSave:&Save +menQueryFileClose:&Close +menQueryEditCut:Cu&t +menQueryEditCopy:&Copy +menQueryEditPaste:&Paste +menQueryHelpHelp:&Help +helpQueryFileOpen:Load a query from disc. +helpQueryFileSave:Save this query to disc. +helpQueryFileClose:Close this window. +helpQueryEditCut:Cut the selected area. +helpQueryEditCopy:Copy the selected area. +helpQueryEditPaste:Insert copy buffer at cursor position. +helpQueryHelpHelp:Information about queries. + + +# Thumbnails menu +menThumbData:Data +menThumbDataClose:&Close +menThumbSetup:Setup + + +# colourspace submenu +menCspaceTitle:Colourspace +menCspaceOn:Enable +menCspaceFull:Full range +menCspaceProj:Proj range +menCspaceEdit:Editor... + + +# Text in widgets +serverName:Server name: +serverPort:Server port: +dbName:Database name: +userName:User: +userPassword:Password: + +# General text in buttons +textOpen:Open +textClose:Close +textOK:OK +textCancel:Cancel +textApply:Apply +textDefaults:Defaults +textResult:Results +textProjString:Projection +textClear:Clear +textExec:Execute +textUpdt:Update Data +textStepx:Step X +textStepy:Step Y +textStepC:Step +textCosys:CO-System +textCoStep:Y markers +textDataStep:X markers +textScale:Scale +textBaseType:Base type +textThumb:Thumbnails +textThumbWidth:ThumbWidth +textThumbColumns:ThumbCols +textThumbProjDim:ProjDim +textThumbProjStep:ProjStep +textBBox:BBox +textImages:Images +textImageMode:Image mode +textCharts:Charts +textChartMode:Chart mode +textTables:Tables +textTableMode:Table mode +textMiscPrefs:Misc prefs +textResample:Resample +textCspace:RGB space +textCrange:Full range +textRotX:Rot X +textRotY:Rot Y +textRotZ:Rot Z +textOff:Off +textStart:Start +textStop:Stop +textTime:Time +textConvert:Convert +textOpTime:Time taken for +textScaleFactor:Scaling factor +textZoomBox:To box +textLastZoom:Back +textZoomIn:Zoom in +textZoomOut:Zoom out +textCommunication:Communication +textTransferFormats:Transfer format +textTransferParams:Transfer parameters +textStorageFormats:Storage format +textStorageParams:Storage parameters +textSliceX:Slice xy +textSliceY:Slice yz +textSliceZ:Slice zx +textOrthoThickness:Thick +textOrthoDragRelease:Auto +textOrthoFireButton:Load +textZOffset:Z Offset + +# Sound window +soundStart:Start +soundStop:Stop +soundOff:Off +soundPlayTime:Play time +soundFrequency:Frequency +soundLatency:Latency +soundFormat:Format +soundFmtLin8:8 bit sl +soundFmtUlin8:8 bit usl +soundFmtUlaw8:8 bit ulaw +soundFmtLin16:16 bit sl + +# Collection group box in results window +textCollHeader:Collection: +textCollName:Name +textCollType:Type + +# Text in lookup scaled window +lkScaleName:Collection name +lkScaleScale:Scaling factor + +# MDD operations (results window) +operationUpsamp:Upsample +operationDownsamp:Downsample +operationScale:Simple scale +operationTypeProj:Type project +operationEndian:Change endian + +# Text for display modes +dispModeLabel:Display mode +dispModeImage:Flat image +dispModeVolume:Volumetric +dispModeOrtho:Orthosection +dispModeHeight:Height field +dispModeChart:Chart +dispModeTable:Table +dispModeSound:Sound +dispModeString:String + +# Text in preferences window +prefsFilePath:Pathname of data files +prefsQueryPath:Pathname of query files +prefsQueryFont:Font used for queries +prefsImgDither:Dithering +prefsDitherBest:Dither best +prefsMaxDWidth:Maximum width +prefsMaxDHeight:Maximum height +prefsVffParams:Convertor parameters +prefsCspace:RGB space +prefsCspaceEdit:Edit +prefsCspaceAct:Cube range +prefsCspaceFull:Full range +prefsCspaceProj:Proj range +prefsMovieMode:Movie mode +prefsMovieOnce:Once +prefsMovieStart:Same dir +prefsMovieSwitch:Switch dir +prefsSound:Sound defaults +prefsSndLoop:Loop sound +prefsLight:Lights +prefsLightAngle:Light angle +prefsLightAmbient:Ambient light +prefsLightGain:Light gain +prefsKernSize:Kernel size +prefsKernType:Kernel type +prefsLightScAngle:Scint angle +prefsLightDir:Direction +prefsLightDist:Distance +prefsUseVCol:Ignore voxel colour +prefsVoxColour:Voxel colour +prefsHeightGroup:Height fields +prefsHgtGrid:Grid size +prefsHgtScale:Height scale +prefsOrthoGroup:Orthosections +prefsOrthoDragRel:On release +prefsOrthoThick:Thickness + +# Text in image settings window +imgSetRender:Renderer settings +imgSetVoxel:Voxel settings +imgSetHeight:Height field +imgSetRenZpro:Proj plane +imgSetRenClipz:Clip Z +imgSetRenUseLight:Use lighting +imgSetRenLightAn:lAngle +imgSetRenLightSc:sAngle +imgSetRenLightAm:Ambient +imgSetRenLightGn:Gain +imgSetRenLightDr:Direction +imgSetRenLightDs:Distance +imgSetVoxPixThreshLow:Pixel threshold Low +imgSetVoxPixThreshHigh:Pixel threshold High +imgSetVoxWgtThresh:Weight threshold +imgSetVoxWgtQuant:Weight quantisation +imgSetVoxRgbBright:RGB brightness +imgSetVoxForType:For type +imgSetKernSize:K-size +imgSetKernType:K-type +imgSetUseVCol:Ignore colours +imgSetVoxCol:Voxel colour +imgSetGridSize:Grid +imgSetHgtScale:Height +kernelTypeAvg:Average +kernelTypeLin:Linear +kernelTypeGauss:Gauss + +# text in colourspace mapper window +cspacePeakRed:E(r) +cspacePeakGreen:E(g) +cspacePeakBlue:E(b) +cspaceSigmaRed:s(r) +cspaceSigmaGreen:s(g) +cspaceSigmaBlue:s(b) +cspaceImmUpdt:Update +cspaceDrawSum:Draw sum +cspaceMinVal:Min +cspaceMaxVal:Max +cspaceTypeGauss:Gaussian +cspaceTypeLin:Linear +cspaceTypeRect:Rectangle +cspaceTypeAsympt:Asymptotic + +loadFile:Load a file +saveTIFF:Save as TIFF +saveView:Save current view to file +loadView:Load a view from file + +# Prompting messages +promptEnterColl:Please enter the collection name. +promptEnterType:Please enter a base type name for type +promptEnterOrtho:Please enter the orthosection collection name. + + +# Text appearing in the About window +rviewAboutLineNum:4 +rviewAboutLine0:*** rView 2.0 +rviewAboutLine1: +rviewAboutLine2:Visual frontend for the RasDaMan DBMS. +rviewAboutLine3:(C) 1999-2001 Active Knowledge GmbH. + + +# Text appearing in the StrinSet window +strSetList:Result set + + +# Errors +errorFrom:Error from rView: + +# RasDaMan errors: +errorUnknown:Object unknown error. +errorHostInvalid:Host invalid. +errorServerInvalid:Server invalid. +errorClientUnknown:Client unknown. +errorDatabaseUnknown:Database unknown. +errorDatabaseOpen:Database is already open. +errorRpcInterface:RPC Interface is incompatible with server. +errorDatabaseClosed:Database closed. +errorCollCreate:Error creating collection. +errorCollDelete:Error deleting collection. +errorInsertObj:Error inserting object into collection. +errorChangeOpen:You can't change the database while it's open. +errorMemory:Not enough memory left for this operation. +errorQueryFailed:Error executing query +errorQueryUnknown:Collection doesn't exist. +errorQueryParamNum:Number of query parameters is invalid. +errorTransferFailed:The transfer failed. +errorProjection:Error in projection string. +errorProjFree:Projection incompatible with display mode. +errorTypeSize:Unsupported base type size error. +errorBaseType:Unsupported base type. +errorUnknownBase:Unknown base type. +errorModeDim:Unsupported number of dimensions for this mode. +errorModeBase:Unsupported base type for this mode. +errorProjectFree:Bad number of free dimensions in projection string. +errorUpdtObject:Missing object for update query. +errorFileOpen:Error opening file +errorFileWrite:Error writing to file +errorFileRead:Error reading from file +errorQueryFile:Unrecognized query file format. +errorProjThumb:Error building thumbnail. +errorSoundFormat:Unsupported sound sample format. +errorSoundDevice:Unable to open sound device. +errorScaledObjType:Unsupported base type for scaled object. +errorScaledObjSize:Object scaled to empty spatial domain. +errorOrthoViewer:Unable to open orthosection viewer on object. +errorViewType:Wrong viewer type + +# Progress reports +messageFrom:Message from rView + +progOpenDb:Opening database... +progCloseDb:Closing database... +progLookup:Looking up collection... +progInsert:Inserting object into collection... +progDelete:Deleting collection... +progCreate:Creating collection... +progQuery:Executing query... diff --git a/applications/rview/rview.cpp b/applications/rview/rview.cpp new file mode 100644 index 0000000..5e9c63b --- /dev/null +++ b/applications/rview/rview.cpp @@ -0,0 +1,548 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView main application (class rView) and main frame (class rviewMainFrame). + * The main application includes the database object which has to be accessed + * through rView-member functions. + * + * COMMENTS: + * none + */ + +// #ifdef __GNUG__ +// #pragma implementation +// #endif + +// # define __GNUC__ +// # define __GNUC_MINOR__ + +// changed in wxWindows 2.6.2: +// #include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + +// #include "wb_timer.h" + + +#include <stdlib.h> +#include <string.h> +#include <sstream> + + +#include "labelManager.hh" + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#ifdef __GNUG__ +#include "raslib/template_inst.hh" +#endif +#endif + +#include "raslib/rmdebug.hh" +#define DEBUG_MAIN +#include "debug/debug.hh" + +#include "rviewUtils.hh" +#include "rview.hh" +#include "rviewQuery.hh" + + + + +const int rviewMainFrame::main_width = 300; +const int rviewMainFrame::main_height = 400 + 2*rview_window_extra_height; +const int rviewMainFrame::main_border = 8; +const int rviewMainFrame::main_theight = 50; +const int rviewMainFrame::main_bwidth = 60; +const int rviewMainFrame::main_bheight = 30; + + + + +/* + * Global data + */ + + +// Start the app. +static rView rview; + + + + +// For some compilers... +IMPLEMENT_WXWIN_MAIN + + + + +#if 0 +#include "rviewTypeMan.hh" +static void testTypeManager(void) +{ + r_Type *newType = r_Type::get_any_type("struct {char red, struct {short s1, long l1}, struct {short s2, long l2}, char green, char blue, struct {float u, float v, float w}}"); + new rviewTypeMan(NULL, newType); + delete newType; +} +#endif + + + + + + +/* + * Labels all the widgets and menus in the main frame using the label manager + * to lookup the values associated with the symbolic names. + */ + +void rviewMainFrame::label(void) +{ + mBar->SetLabel(MENU_MAIN_FILE_QUERY, lman->lookup("menMainFileQuery")); + mBar->SetLabel(MENU_MAIN_FILE_PREFS, lman->lookup("menMainFilePrefs")); + mBar->SetLabel(MENU_MAIN_FILE_EXIT, lman->lookup("menMainFileExit")); + mBar->SetHelpString(MENU_MAIN_FILE_QUERY, lman->lookup("helpMainFileQuery")); + mBar->SetHelpString(MENU_MAIN_FILE_PREFS, lman->lookup("helpMainFilePrefs")); + mBar->SetHelpString(MENU_MAIN_FILE_EXIT, lman->lookup("helpMainFileExit")); + + mBar->SetLabel(MENU_MAIN_VIEW_OPEN, lman->lookup("menMainViewOpen")); + mBar->SetLabel(MENU_MAIN_VIEW_CLOSE, lman->lookup("menMainViewClose")); + mBar->SetHelpString(MENU_MAIN_VIEW_OPEN, lman->lookup("helpMainViewOpen")); + mBar->SetHelpString(MENU_MAIN_VIEW_CLOSE, lman->lookup("helpMainViewClose")); + + mBar->SetLabel(MENU_MAIN_COLL_LOOK, lman->lookup("menMainCollLook")); + mBar->SetLabel(MENU_MAIN_COLL_LKSCL, lman->lookup("menMainCollLkScale")); + mBar->SetLabel(MENU_MAIN_COLL_LKORTH, lman->lookup("menMainCollLkOrtho")); + //FIXME create collection not implemented yet + // mBar->SetLabel(MENU_MAIN_COLL_CREATE, lman->lookup("menMainCollCrt")); + mBar->SetLabel(MENU_MAIN_COLL_DELETE, lman->lookup("menMainCollDel")); + mBar->SetHelpString(MENU_MAIN_COLL_LOOK, lman->lookup("helpMainCollLook")); + mBar->SetHelpString(MENU_MAIN_COLL_LKSCL, lman->lookup("helpMainCollLkScale")); + mBar->SetHelpString(MENU_MAIN_COLL_LKORTH, lman->lookup("helpMainCollLkOrtho")); + //mBar->SetHelpString(MENU_MAIN_COLL_CREATE, lman->lookup("helpMainCollCrt")); + mBar->SetHelpString(MENU_MAIN_COLL_DELETE, lman->lookup("helpMainCollDel")); + + mBar->SetLabel(MENU_MAIN_HELP_ABOUT, lman->lookup("menMainHelpAbt")); + mBar->SetHelpString(MENU_MAIN_HELP_ABOUT, lman->lookup("helpMainHelpAbt")); + + mBar->SetLabelTop(0, lman->lookup("menMainFile")); + mBar->SetLabelTop(1, lman->lookup("menMainView")); + mBar->SetLabelTop(2, lman->lookup("menMainColl")); + mBar->SetLabelTop(3, lman->lookup("menMainHelp")); + + server->SetLabel(lman->lookup("serverName")); + port->SetLabel(lman->lookup("serverPort")); + database->SetLabel(lman->lookup("dbName")); + username->SetLabel(lman->lookup("userName")); + userpassword->SetLabel(lman->lookup("userPassword")); + + openBut->SetLabel(lman->lookup( (dbOpen) ? "textClose" : "textOpen")); + openBut->SetSize(-1, -1, 80, 30); +} + + + + +/* + * This is called via the frame manager. Events are broadcast to all the + * existing frames. If a frame recognises the object as one of its + * children it must return a value != 0. + */ + +int rviewMainFrame::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + //cout << "rviewMainFrame: Received event" << endl; + + if ((((&obj == (wxObject*)server) || (&obj == (wxObject*)database) || + (&obj == (wxObject*)username)|| (&obj == (wxObject*)userpassword) || + (&obj == (wxObject*)port)) + && (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND)) + || ((&obj == (wxObject*)openBut) && (type == wxEVENT_TYPE_BUTTON_COMMAND))) + { + rView *app = (rView*)rmanClientApp::theApp(); + app->OpenCloseServer(); + return 1; + } + return 0; +} + + + + + +/* + * Initialises the main window's frame, containing the main menus and the + * widgets for server and database. + */ + +rviewMainFrame::rviewMainFrame(wxFrame *frame, char *title, int x, int y, int w, int h): rviewFrame(frame, title, x, y, w, h) +{ + wxMenu *mbarMenus[4]; + char buffer[STRINGSIZE]; + + aboutWindow = NULL; + + CreateStatusLine(1); + dbOpen = FALSE; + + // Create the menus and the menu bar. All labelling is done in the label() member function. + mbarMenus[0] = new wxMenu; + mbarMenus[0]->Append(MENU_MAIN_FILE_QUERY, ""); + mbarMenus[0]->Append(MENU_MAIN_FILE_PREFS, ""); + mbarMenus[0]->Append(MENU_MAIN_FILE_EXIT, ""); + + mbarMenus[1] = new wxMenu; + mbarMenus[1]->Append(MENU_MAIN_VIEW_OPEN, ""); + mbarMenus[1]->Append(MENU_MAIN_VIEW_CLOSE, ""); + + mbarMenus[2] = new wxMenu; + mbarMenus[2]->Append(MENU_MAIN_COLL_LOOK, ""); + mbarMenus[2]->Append(MENU_MAIN_COLL_LKSCL, ""); + mbarMenus[2]->Append(MENU_MAIN_COLL_LKORTH, ""); + //FIXME create collection not implemented yet + //mbarMenus[2]->Append(MENU_MAIN_COLL_CREATE, ""); + mbarMenus[2]->Append(MENU_MAIN_COLL_DELETE, ""); + + mbarMenus[3] = new wxMenu; + mbarMenus[3]->Append(MENU_MAIN_HELP_ABOUT, ""); + + // Note: the titles will be overwritten later on by label() but we need some text + // here for keyboard-shortcuts and correct alignment (help flush right). + // See wxWindows documentation: the submenus mustn't be used any more after they + // were appended to the MenuBar. They are still accessible via the MenuBar through + // their id, however. + mBar = new wxMenuBar; + sprintf(buffer, "&%s", lman->lookup("menMainFile")); + mBar->Append(mbarMenus[0], buffer); + sprintf(buffer, "&%s", lman->lookup("menMainView")); + mBar->Append(mbarMenus[1], buffer); + sprintf(buffer, "&%s", lman->lookup("menMainColl")); + mBar->Append(mbarMenus[2], buffer); + sprintf(buffer, "&%s", lman->lookup("menMainHelp")); + mBar->Append(mbarMenus[3], buffer); + + // create panel + panel = new wxPanel((wxWindow*)this, main_border, main_border, 256, 100, wxBORDER); + panel->SetLabelPosition(wxVERTICAL); + + // Create the text widgets + server = new rviewText(panel); + panel->NewLine(); + port = new rviewText(panel); + panel->NewLine(); + database = new rviewText(panel); + panel->NewLine(); + username = new rviewText(panel); + panel->NewLine(); + userpassword = new rviewText(wxTE_PASSWORD, panel); + panel->NewLine(); + // ... and the button + openBut = new rviewButton(panel); + + SetMenuBar(mBar); + + newDBState(dbOpen); + + label(); + + // Resize everything correctly + frameWidth = -1; frameHeight = -1; // force resize + + OnSize(w, h); + OnSize(w, h); //twice is done correct + +} + + +const char *rviewMainFrame::getFrameName(void) const +{ + return "rviewMainFrame"; +} + +rviewFrameType rviewMainFrame::getFrameType(void) const +{ + return rviewFrameTypeMain; +} + + + +void rviewMainFrame::newDBState(bool newState) +{ + openBut->SetLabel(lman->lookup( (newState) ? "textClose" : "textOpen")); + openBut->SetSize(-1, -1, main_bwidth, main_bheight); +#ifndef DUMMY_MDD_OBJECT + mBar->Enable(MENU_MAIN_COLL_LOOK, newState); +#endif + mBar->Enable(MENU_MAIN_COLL_LKSCL, newState); + mBar->Enable(MENU_MAIN_COLL_LKORTH, newState); + //FIXME create collection not implemented yet + //mBar->Enable(MENU_MAIN_COLL_CREATE, newState); + mBar->Enable(MENU_MAIN_COLL_DELETE, newState); + + //set editable mode foe dbOpen + server->SetEditable(!newState); + port->SetEditable(!newState); + database->SetEditable(!newState); + username->SetEditable(!newState); + userpassword->SetEditable(!newState); + //reset to default when database is closed + if(!newState) + userpassword->SetValue(""); + + dbOpen = newState; +} + + +int rviewMainFrame::userEvent(const user_event &ue) +{ + if ((ue.type == usr_db_opened) || (ue.type == usr_db_closed)) + { + newDBState((ue.type == usr_db_opened)); + return 1; + } + return 0; +} + + + + +/* + * Dummy desctructor. + */ +rviewMainFrame::~rviewMainFrame(void) +{ +} + + + +/* + * Called from wxWindows when a menu item in the main window is selected. + */ + +void rviewMainFrame::OnMenuCommand(int id) +{ + switch (id) + { + case MENU_MAIN_FILE_EXIT: + { + if (frameManager != NULL) + { + if (frameManager->broadcastQuit(0) == 0) + { + cerr << "Some windows refuse to die." << endl; + return; + } + frameManager->broadcastQuit(1); + } + rmanClientApp::theApp()->SavePreferences(); + this->Close(TRUE); + } + break; + case MENU_MAIN_FILE_PREFS: prefs->edit(); break; + case MENU_MAIN_FILE_QUERY: + { + rView *app = (rView*)rmanClientApp::theApp(); + app->OpenQueryWindow(); + } + break; + case MENU_MAIN_VIEW_OPEN: rmanClientApp::theApp()->OpenFile(0, NULL, TRUE); break; + case MENU_MAIN_VIEW_CLOSE: + if (frameManager != NULL) + { + user_event usr; + + usr.type = usr_close_viewers; + frameManager->broadcastUserEvent(usr); + } + break; + case MENU_MAIN_COLL_LOOK: rmanClientApp::theApp()->LookupCollection(); break; + case MENU_MAIN_COLL_LKSCL: rmanClientApp::theApp()->LookupScaledCollection(); break; + case MENU_MAIN_COLL_LKORTH: rmanClientApp::theApp()->LookupOrthosection(); break; + case MENU_MAIN_COLL_CREATE: rmanClientApp::theApp()->CreateCollection(); break; + case MENU_MAIN_COLL_DELETE: rmanClientApp::theApp()->DeleteCollection(); break; + case MENU_MAIN_HELP_ABOUT: + if (aboutWindow == NULL) + { + aboutWindow = new rviewAbout(); + } + aboutWindow->Show(TRUE); + break; + default: break; + } +} + +/* + * Only one exit + */ +bool +rviewMainFrame::OnClose() +{ + return FALSE; +} + + +/* + * Called from wxWindows when the main window is resized. + */ + +void rviewMainFrame::OnSize(int w, int h) +{ + int x, y, d; + + GetClientSize(&x, &y); + + //need to resize? + if (( main_width != x) || ( main_height != y)) + { + frameWidth = main_width; + frameHeight = main_height; + x = main_width; + y = main_height; + SetClientSize(x, y); + return; + } + + x -= 2*main_border; y -= 2*main_border; + panel->SetSize(main_border, main_border, x, y); + + x -= 2*main_border; y -= 2*main_border; + d = (y - (5*main_theight + main_bheight)) / 6; + + server->SetSize(main_border, main_border + d/2, x, main_theight); + port->SetSize(main_border, main_border + main_theight + (3*d)/2, x, main_theight); + database->SetSize(main_border, main_border + 2*main_theight + (5*d)/2, x, main_theight); + username->SetSize(main_border, main_border + 3*main_theight + (7*d)/2, x, main_theight); + userpassword->SetSize(main_border, main_border + 4*main_theight + (9*d)/2, x, main_theight); + + openBut->SetSize((x - main_bwidth)/2, main_border + 5*main_theight + (11*d)/2, main_bwidth, main_bheight); +} + + + +void rviewMainFrame::SetDatabaseInfo(const char *srvName, int srvPort, const char *dbName, const char *usrName) +{ + server->SetValue(srvName); + port->SetValue(srvPort); + database->SetValue(dbName); + username->SetValue(usrName); + userpassword->SetValue(""); // empty password string +} + + + +void rviewMainFrame::GetDatabaseInfo(DynamicString &srvName, int& srvPort, DynamicString &dbName, + DynamicString &usrName, DynamicString &usrPassword) const +{ + server->GetValue(srvName); + port->GetValue(srvPort); + database->GetValue(dbName); + username->GetValue(usrName); + userpassword->GetValue(usrPassword); +} + + + + +/* + * rView specifics; not too much code, really ;-) + */ + +const char rView::labels_filename[] = "labels.txt"; +const char rView::prefs_filename[] = ".rviewrc"; +const double rView::version = RVIEW_VERSION; + + +rView::rView(void) : rmanClientApp("RVIEWHOME", prefs_filename, labels_filename) +{ + mainFrame = NULL; +} + + +rView::~rView(void) +{ +} + + +/* + * Initialises the GUI of the program. Most of the real stuff is done in + * the rviewMainFrame constructor. + */ + +wxFrame *rView::OnInit(void) +{ + char buffer[STRINGSIZE]; + + sprintf(buffer, "%s %.1f", lman->lookup("titleRview"), version); + mainFrame = new rviewMainFrame(NULL, buffer, 0, 0, rviewMainFrame::main_width, rviewMainFrame::main_height); + + mainFrame->SetDatabaseInfo(prefs->serverName, prefs->serverPort, prefs->databaseName, prefs->userName); + + mainFrame->Show(TRUE); + + return (wxFrame*)mainFrame; +} + + +/* + * This function is called from the global callback function mainFrameOpenServer + * upon receiving a <RETURN> in one of the text widgets or a click on the + * Open button. + */ + +void rView::OpenCloseServer(void) +{ + if (database.isOpen()) + { + CloseServer(); + } + else + { + DynamicString userPassword; + mainFrame->GetDatabaseInfo(prefs->serverName, prefs->serverPort, prefs->databaseName, + prefs->userName, userPassword); + prefs->markModified(); + if (OpenServer(prefs->serverName, prefs->serverPort, prefs->databaseName, prefs->userName, userPassword)) + mainFrame->newDBState(FALSE); + } +} + + +void rView::OpenQueryWindow(void) +{ + rviewQuery *newQuery = new rviewQuery(&database); +} diff --git a/applications/rview/rview.hh b/applications/rview/rview.hh new file mode 100644 index 0000000..1b26cc8 --- /dev/null +++ b/applications/rview/rview.hh @@ -0,0 +1,139 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView main application (class rView) and main frame (class rviewMainFrame). + * The main application includes the database object which has to be accessed + * through rView-member functions. + * + * COMMENTS: + * none + */ + + + +#ifndef _RVIEW_H_ +#define _RVIEW_H_ + + +#ifdef __GNUG__ +#pragma interface +#endif + + + + + +// RasDaMan includes +#include "rasodmg/ref.hh" +#include "rasodmg/marray.hh" + + +#include "rviewApp.hh" +#include "rviewUtils.hh" +#include "rviewPrefs.hh" + + + +/* + * rView's main frame + */ +class rviewMainFrame: public rviewFrame +{ + public: + + rviewMainFrame(wxFrame *frame, char *title, int x, int y, int w, int h); + ~rviewMainFrame(void); + void OnMenuCommand(int id); + void OnSize(int w, int h); + void SetDatabaseInfo(const char *srvName, int srvPort, + const char *dbName, const char*usrName); + void GetDatabaseInfo(DynamicString &srvName, int& srvPort, DynamicString &dbName, + DynamicString &usrName, DynamicString &usrPassword) const; + void newDBState(bool newState); + bool OnClose(); + + // Implementations of the rviewFrame virtual functions + void label(void); + int process(wxObject &obj, wxEvent &evt); + + int userEvent(const user_event &ue); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + // constants + // Width and height of main window + static const int main_width; + static const int main_height; + // Border around panel in main frame + static const int main_border; + // Height of server / database text widgets + static const int main_theight; + // Dimensions of button(s) + static const int main_bwidth; + static const int main_bheight; + + + protected: + + rviewText *server; + rviewText *port; + rviewText *database; + rviewText *username; + rviewText *userpassword; + rviewButton *openBut; + wxPanel *panel; + wxMenuBar *mBar; + bool dbOpen; + rviewAbout *aboutWindow; +}; + + + +/* + * rView application specifics + */ +class rView: public rmanClientApp +{ + public: + + rView(void); + virtual ~rView(void); + wxFrame *OnInit(void); + void OpenCloseServer(void); + void OpenQueryWindow(void); + + + private: + + rviewMainFrame *mainFrame; + + static const char labels_filename[]; + static const char prefs_filename[]; + static const double version; +}; + +#endif diff --git a/applications/rview/rview.tex b/applications/rview/rview.tex new file mode 100644 index 0000000..daab380 --- /dev/null +++ b/applications/rview/rview.tex @@ -0,0 +1,1479 @@ +% Documentation for rView +% (C) 1998-2002 FORWISS, Andreas Dehmel + +\def\rview{\textsf{rView}} +\def\rman{\textsf{RasDaMan}} +\def\wxwin{\textsf{wxWindows}} +\def\dollar{\$} % stupid emacs colouring problem with $ +\def\realnumbers{\mbox{I}\!\mbox{R}} + +\documentclass[11pt]{article} +\usepackage{a4wide} +\usepackage{parskip} + +\title{\hrule \vspace{10mm} \Huge rView 2.0\\A visual frontend to the \rman\ DBMS} + +\author{Andreas Dehmel} + +\date{07 Jan 2002} + +\frenchspacing +\sloppy +\setcounter{secnumdepth}{5} +\setcounter{tocdepth}{5} + +\begin{document} + +\maketitle + +\vspace{10mm} +\hrule + +\thispagestyle{empty} + +%\newpage + +\tableofcontents + +\newpage + +\section{Introduction} + +\rview\ is a frontend to the \rman\ DBMS for visualizing multidimensional raster +data and generally making it easier to use the database. It is based on the +freely available, portable \wxwin\ GUI +(\texttt{http://web.ukonline.co.uk/julian.smart/wxwin/}).\\ +No text displayed in any of its windows is hard-coded into the program, instead +it uses labels which are read in on startup and dereferenced at run-time. The +labels are looked up in a file called \texttt{labels.txt} which \rview\ tries to open as +\texttt{\dollar RVIEWHOME/labels.txt} or, failing that, \texttt{./labels.txt}. If +no \texttt{labels.txt} file could be found, all text displayed in \rview's windows +will default to \texttt{???}. The \texttt{labels.txt} file can contain three types of +lines: + +\begin{description} +\item[Empty lines:] only whitespace (space, tabs) +\item[Commentary lines:] a \# Symbol preceded by nothing or whitespace and followed +by any text. +\item[Label definitions:] These have the form "\texttt{\textsl{label}:\textsl{text associated +with label}}". There must be no whitespace between the label and the colon; whitespace +following the colon will not be stripped. Being line-based, label text may not contain +linefeeds, all other characters are legal. +\end{description} + +Configurations are stored in the file \texttt{.rviewrc} which will be looked for in +\texttt{\dollar HOME}, the current directory and \texttt{\dollar RVIEWHOME} in that order. +The current configurations are saved to \texttt{\dollar HOME/.rviewrc} automatically on +exit. The old \texttt{.rviewrc} file (if existent) will be renamed to \texttt{.rviewrc$\sim$} +before being overwritten so in case the new preferences file is corrupted for some +reason the last settings can be easily restored. Serious errors like segmentation violations +are handled by \rview\ and result in any open databases being closed before the program +is aborted. + + +\section{The Main Window} +\label{MainWindow} + +The main window allows you to enter information about a database and open or +close it; furthermore the following menus are available: + +\subsection{The \texttt{File} Menu} + +The \texttt{File} Menu offers functionality that is available even with the +database closed: + +\begin{description} + +\item[Query:] Opens a query shell. See \ref{QueryWindow}. + +\item[Prefs:] Opens the preferences editor. See \ref{PreferencesEditor}. + +\item[Exit:] Exit \rview. If a database was opened, close it first. + +\end{description} + + +\subsection{The \texttt{Viewers} Menu} + +The \texttt{Viewers} Menu allows you to open / close viewers directly: + +\begin{description} + +\item[Open:] Opens a file. Currently this allows you to open TIFF images +and display them in an image viewer window (\ref{ImageDisplayMode}). + +\item[Close all:] Closes all open viewers (\ref{ObjectViewers}) and \texttt{Results} +windows (\ref{ResultWindow}). + +\end{description} + + +\subsection{The \texttt{Collections} Menu} + +The \texttt{Collections} Menu combines operations on database collections. Its +members can therefore only be called once a database has been opened. After +selecting an item a small dialog-window pops up in which you have to enter +the collection name. + +\begin{description} + +\item[Lookup:] Load a collection of objects from the database into client +memory. On success a \emph{Results}-Window containing all the objects +in the collection is opened (see \ref{ResultWindow}). + +\item[LU scaled:] Load a scaled object from the database. On success a viewer +window similar to flat image mode is opened (see \ref{ImageModeScaled}). + +\item[LU ortho:] Load a 3D object from the database into a partial +\emph{Orthosection} viewer (see \ref{ImageModeOrtho}). In contrast, a +full orthosection viewer is available from the \emph{Results} window. + +\item[Create:] Create an empty collection. + +\item[Delete:] Delete a named collection. + +\end{description} + + +\section{The Query Window} +\label{QueryWindow} + +The query window allows you to write and edit database queries as well as load +and save them. On disk, queries are identified by a \texttt{.ql} extension as well +as the headline "\texttt{-- rview-Query}" which will not be displayed in the query +window.\\ +The \texttt{File} menu allows you to \texttt{Open} a query on disk, \texttt{Save} the +current query to disk or \texttt{Close} the query window. The \texttt{Edit} menu +operates on selections, allowing you to \texttt{Cut}, \texttt{Copy} or \texttt{Paste} the +current selection.\\ +The \texttt{List} menu provides a shortcut to all the queries stored in the +directory described by the \texttt{query path} variable (see \ref{PreferencesEditor}). +To achieve this the entire directory is scanned for files named +\texttt{*.ql}. The resulting set of files is then appended to the \texttt{List} menu +in alphabetical order. The \texttt{List} menu is built every time a query +window is opened or a query is loaded from / saved to a different directory. +This behaviour is new in version 1.8, formerly it was only built when a new +query window was opened.\\ +At the bottom of the query editor are three buttons for query control: +\texttt{Clear} deletes the current query and provides you with an empty window. +\texttt{Execute} executes the query (requires an open database). Errors +during execution are reported to the user; the error position is displayed +by selecting the erroneous parts of the query. If the query was executed +without any errors and the resulting collection is not empty, a result window is +opened (see \ref{ResultWindow} for MDD, \ref{ResultStrings} for scalar return +types). \texttt{Update Data} opens a TIFF image for use as \texttt{\dollar 1} argument +in an update query. If a query window has update data associated with it a string +of the form \texttt{q$n_q$d$n_d$} is appended to its title where \texttt{$n_q$} is a +decimal number representing the query window and \texttt{$n_d$} is a decimal number +representing the image viewer. Likewise a string of the form \texttt{d$n_d$q$n_q$} is +appended to the image viewer's title. If a query window or its update data +window is closed this information string is removed from the remaining window's +title bar. If new update data is opened the title bars of the old data viewer and +the query window are updated accordingly. + + +\section{The Preferences Window} +\label{PreferencesEditor} + +The preferences window can be used to configure many aspects of the program. +Its upper part is a scrollable window with the actual configuration items +while the bottom row contains \texttt{OK} and \texttt{Cancel} buttons. Changes +made to the preferences only become active after clicking on the \texttt{OK} +button or pressing \texttt{<return>} in one of the text widgets. Clicking on +\texttt{Cancel} discards all changes made to the preferences since the window +was opened. Preferences are automatically saved when \rview\ is quit. + +\subsection{Misc Prefs} + +\begin{description} + +\item[Pathname of data files:] the full pathname of the directory \rview\ will +use by default when loading / saving images and similar data. This field will +also be updated automatically to the last directory accessed. + +\item[Pathname of query files:] the full pathname of the directory +containing the query files (file extension \texttt{.ql}). This path will also +be updated automatically to the last directory accessed in a query load +or save operation. + +\item[Font used for queries:] here you can specify the font that should +be used in the query windows (\ref{QueryWindow}). Fonts are specified like this:\\ +\begin{tabular}{rcl} +\textsl{font} & = & \lbrack \textsl{keyword} \rbrack * \\ +\textsl{keyword} & = & \textsl{family} $\vert$ \textsl{style} $\vert$ \textsl{weight} $\vert$ \textsl{psize}\\ +\textsl{family} & = & \lbrack decorative $\vert$ default $\vert$ modern $\vert$ roman $\vert$ script $\vert$ swiss $\vert$ teletype \rbrack\\ +\textsl{style} & = & \lbrack italic $\vert$ slant \rbrack\\ +\textsl{weight} & = & \lbrack bold $\vert$ light \rbrack\\ +\textsl{psize} & = & \lbrack 0-9 \rbrack +\\ +\end{tabular} +\\ +where \texttt{psize} is the point-size of the font. The default font is \texttt{default 12}. +Changes take effect the next time a query window is opened. See also \ref{Platforms}. + +\item[Maximum width, Maximum height:] The maximum dimensions of viewer windows. +At the moment this is used to limit the size of an image viewer window (\ref{ImageDisplayMode}). +The size specified here should under no circumstances exceed the screen size. + +\item[Convertor parameters:] parameter string for convertors (TIFF, VFF). This allows +to e.g.\ determine the TIFF compression type (e.g.\ \texttt{comptype=lzw}) or the default +VFF dimension ordering (e.g.\ \texttt{dorder=xyz}). For a list of parameters see the +conversion module documentation. +\end{description} + +\subsection{Images} + +\begin{description} + +\item[Dither images:] This option makes a difference on displays with $\le$ 8bpp only. +When selected, images are converted to the display using error-diffused dithering +instead of a simple match to the closest colour. This results in much better quality +at the expense of speed. Changes take effect the next time an image viewer is opened +or the size of an existing image changes. +\item[Dither best:] If this option is disabled, dithering may not have the highest +possible quality but will be fast enough even for animations. Using \texttt{Dither best} +involves a substantial amount of computational overhead and is therefore \emph{not} +recommended for large images. +\item[RGBSpace:] The default settings for colourspace mapping (see \ref{ColourspaceMapping}). +You can choose between three range models which will be explained in \ref{ColourspaceMapping}, +or switch it off by default. +\item[Edit:] This lets you edit the default settings of the colourspace mapping +(see \ref{ColourspaceMapping}). + +\item[Image mode:] The default mode to use in image viewers. See +\ref{ImageDisplayMode}. + +\item[Movie mode:] The default action to take when a movie has ended. The options are +\texttt{Once} (stop playback), \texttt{Same dir} (restart the movie using the same playback +direction) and \texttt{Switch dir} (switch playback direction). See \ref{ImageDisplayMode}. + +\item[Scaling factor:] The default scaling factor to use when opening a viewer for +scaled images (see \ref{ImageModeScaled}). + +\item[Renderer:] Various settings for the renderers (surface, voxel) in image viewers. +If the \texttt{For type} box is checked the voxel settings specified here (threshold values, +weight quantisation and voxel colours) are ignored and base type specific defaults are used +instead. See \ref{Renderers}. + +\item[Height fields:] Default settings for heightfield rendering. See +\ref{HeightFieldRenderer}. + +\item[Orthosections:] Default settings for orthosection viewers. See +\ref{ImageModeOrtho}. + +\item[Thumbnails:] Default settings for the thumbnail viewers. See +\ref{ThumbnailDisplayMode}. + +\end{description} + +\subsection{Charts} +Default settings for the chart viewer. See \ref{ChartDisplayMode}. + +\subsection{Tables} +Default settings for the table viewer. A value of -1 for \texttt{Step X} and \texttt{Step Y} +means the viewer will make a guess at the grid size depending on the base type of the +object. See \ref{TableDisplayMode}. + +\subsection{Sound} +Default settings for the sound player. See \ref{SoundDisplayMode}. + +\subsection{Communication} + +Settings for the data formats to use for data transfer and data storage. The +transfer data format is always used to send MDD \emph{to} the server. When +loading data from the server, it's normally a suggested format that's used +in case the data is uncompressed, but can also be enforced if the transfer +parameters contain the tuple \texttt{exactformat=1}. The available options are +the same for transfer and storage. If both data formats differ, the data +will be converted in the server to the storage format prior to actual +storage. Usually a light-weight data format should be chosen as transfer +format (e.g.\ \texttt{RLE} or even raw data \texttt{Array}). It's important to +note that no lossy compression scheme should be used for transfering MDD +to the server because the server might have to retile the data, meaning +it'll be decompressed and recompressed with a potential accumulation of +loss. Using a lossy format as storage format only avoids this problem +because the storage format will only be applied to the actual tiles +stored.\\ +The data formats currently available are the following, but they may be extended +any time: + +\begin{description} +\item[Lossless:]\ \\ +\vspace{-6mm} +\begin{description} +\item[Array:] raw data transfer, no compression +\item[RLE:] Run Length Encoding +\item[ZLib:] ZLib compression +\item[SepRLE:] Run Length Encoding with base type separation +\item[SepZLib:] ZLib compression with base type separation +\item[HaarWave:] Haar Wavelet compression +\end{description} +\item[Lossy:] (the numbers shown in the list widget are the filter lengths)\\ +\vspace{-6mm} +\begin{description} +\item[Daubechies*Wavelet:] the standard Daubechies wavelet series +\item[LeastAsym*Wavelet:] the least asymmetric Daubechies wavelet series +\item[Coiflet*Wavelet:] the Coiflet wavelet series +\end{description} +\end{description} + +In addition, the compression algorithms can be configured considerably by +writing parameters as a comma-separated list over primitive tuples of the +form \textsl{keyword=value} into the corresponding parameter window. Newlines +are allowed and there is no length limit on the parameter list. String +values can be written without quotes if they don't contain spaces or commas, +otherwise the string must be embedded in (double) quotes. Currently +available parameters can be seen in figures +\mbox{\ref{FigureParamsComm} -- \ref{FigureParamsZerotree}}. + +\newcommand{\BeginKeyTable}{% + \begin{figure}[hptb]\begin{center}\begin{tabular}{|c|c|c|p{90mm}|}% + \hline% + \textbf{Keyword} & \textbf{Type} & \textbf{Default} & \textbf{Description}\\% + \hline% +} +\newcommand{\EndKeyTable}[2]{% + \hline% + \end{tabular}% + \caption{#1} #2% + \end{center}\end{figure}% +} + +\BeginKeyTable +\texttt{exactformat} & int & 0 & +If \texttt{exactformat} is 0, the transfer format will be used by the server for +data stored in uncompressed format only; it it's $\ne$ 0, the server will always +repack the data to the exact format requested by the client\\ +\EndKeyTable{Keywords for client-server communication}{\label{FigureParamsComm}} + +\BeginKeyTable +\texttt{predinter} & string & --- & +The name of the interchannel predictor which correlates cells across channels +($\rightarrow$ atomic types within structured types). Possible values: +\texttt{delta} (normal difference) and \texttt{scaledelta} (rescale to same +dynamic range, then use difference)\\ +\texttt{predintra} & string & --- & +The name of the intrachannel predictor which correlates cells to its spatial +neighbours. Possible values: \texttt{hyperplane} (predict from previous hyperplane, +e.g.\ previous scanline in a 2D image), \texttt{neighbours} (predict from arithmetic +average of all neighbouring cells already seen) and \texttt{weighted} (additionally +weigh the neighbouring cell values according to their offset in each dimension)\\ +\texttt{intermap} & string & --- & +Comma-separated list of channel mappings of the form +\texttt{\textit{ch-num}:\textit{pred-num}} where \texttt{\textit{ch-num}} is the +number of the channel being mapped and \texttt{\textit{pred-num}} is the number +of the channel it's predicted by. e.g.\ typical for RGB images where green predicts +red and blue: \texttt{0:1,2:1}\\ +\texttt{intralist} & string & --- & +Comma-separated list of channel numbers where intrachannel prediction is used. +If the first character is an exclamation mark, the inverse list is used.\\ +\texttt{hypernorm} & int & 0 & +For intrachannel \texttt{hyperplane} prediction: the number of the dimension +orthogonal to the (moving) predictor hyperplane, i.e.\ the direction in which +prediction takes place\\ +\texttt{predweight} & double & 0.5 & +For intrachannel \texttt{weighted} prediction: the amount by which a cell weight +decreases for each offset $\ne$ 0 in a dimension\\ +\EndKeyTable{Keywords for predictors}{\label{FigureParamsPredict}} + +\BeginKeyTable +\texttt{zlevel} & int & 6 & +The zlib compression level ($0\ldots9$)\\ +\EndKeyTable{Keywords for ZLib compression stream}{\label{FigureParamsZLib}} + +\BeginKeyTable +\texttt{wavestr} & string & \texttt{zlib} & +The compression stream name to use: \texttt{none}, \texttt{rle}, \texttt{zlib} or +\texttt{arith}\\ +\texttt{mrlevels} & int & 0 & +Maximum number of hierarchical levels to do multiresolution analysis on. +0 means all levels\\ +\texttt{banditer} & string & \texttt{leveldet} & +The name of the band iterator. Possible values: \texttt{isolevel}, \texttt{leveldet} +and \texttt{isodetail}\\ +\EndKeyTable{Keywords for wavelets}{\label{FigureParamsWavelet}} + +\BeginKeyTable +\texttt{wqtype} & string & \texttt{zerotree} & +The quantization type to use. Possible values are \texttt{perband} and \texttt{zerotree}. +\texttt{zerotree} is much more efficient regarding storage space but involves rather +much overhead. \texttt{perband} is simpler but compresses worse and is harder to +configure.\\ +\texttt{enhancelv} & int & 0 & +Number of hierarchical levels (starting from coarsest one) over which to enhance +wavelet-coefficients at the boundary (to avoid boundary artifacts at low rates). +0 disables it.\\ +\texttt{qwavdbg} & int & 0 & +Level for debug-version: perform additional statistics during encoding; +higher levels do everything lower levels do. 1: residuum and logarithmic +histogram; 2: linear histogram for all bands; 3: effect of zeroing each +band on signal-to-noise ratio\\ +\EndKeyTable{Keywords for lossy wavelets}{\label{FigureParamsQWavelet}} + +\BeginKeyTable +\texttt{qrtype} & string & \texttt{const} & +The quantizer type (statistics module), setting the bits per band. Values +are \texttt{const}, \texttt{linear}, \texttt{expnt}, \texttt{gauss} and \texttt{custom} +where \texttt{custom} can't be selected directly\\ +\texttt{qntype} & string & \texttt{linear} & +The quantization type, either \texttt{linear} or \texttt{expnt}\\ +\texttt{bitscale} & double & 1.0 & +Set the scaling factor of the bit allocation curve relative to the size of +the base type\\ +\texttt{cutoff} & double & 1.0 & +The band number relative to the total number of wavelet subbands starting +from which all bands are ignored (quantized to 0)\\ +\texttt{nullzone} & double & 0.0 & +Coefficients whose absolute value is smaller than \texttt{nullzone} are set to 0\\ +\texttt{relqbits} & string & --- & +The string is a comma-separated ($\Rightarrow$ the string must be enclosed in +double quotes) list of floating point values, encoding the number of bits +relative to the number of bits in the base type each wavelet band should be +encoded with. Implicitly sets the quantizer type to \texttt{custom}.\\ +\EndKeyTable{Keywords for \emph{perband} lossy wavelet compression}{\label{FigureParamsPerband}} + +\BeginKeyTable +\texttt{zttype} & string & \texttt{band1} & +The type of the zerotree encoding to use. \texttt{band1} is one-pass with a four symbol +alphabet whereas \texttt{band2} is two-pass with dominant pass (four symbol alphabet) and +a subordinate pass (two symbol alphabet).\\ +\texttt{psnr} & double & 100.0 & +The signal-to-noise ratio to use during encoding. Higher values mean better +quality\\ +\EndKeyTable{Keywords for \emph{zerotree} lossy wavelet compression}{\label{FigureParamsZerotree}} + +The \texttt{exactformat} parameter must be added to the parameters for the +transfer format, not the one for the storage format. Transfer- and storage +formats are used like this: + +\begin{center} +\begin{tabular}{|c|p{70mm}|p{50mm}|} +\hline +\textbf{Direction} & \textbf{Transfer format} & \textbf{Storage format}\\ +\hline +Insert & Used for transfer to the server & Used for actually storing the data\\ +\hline +Retrieval & Depending on the value of \texttt{exactformat}: +\begin{itemize} +\item += 0: used for data stored in uncompressed format, ignored for all +other data. +\item +$\ne$ 0: used for all data, repacking on the server if necessary +\end{itemize} +& ignored\\ +\hline +\end{tabular} +\end{center} + +\pagebreak[4] + + +\section{The Results Window} +\label{ResultWindow} + +This window is the starting point for visualizing MDD objects downloaded from +the database as a result of selecting \texttt{Collections->Lookup} from the +main window (\ref{MainWindow}) or executing a query (\ref{QueryWindow}). All +MDD objects are owned by this window, so closing viewers does not remove any +data but the viewers' own from memory. Double-clicking on an entry in the results +list opens a viewer of the type set by \texttt{Display Mode}. Currently available +display modes are + +\begin{description} + +\item[Image modes:]\ \\ +\begin{description} +\item[Flat image] is a 2D mode and means a simple orthogonal projection that works +with data of any dimensionality (see \ref{ImageModeFlat}). +\item[Volumetric] can only be used to visualize volumetric data (i.e.\ 3D) like tomograms. +It consists of two renderer types, a surface-oriented renderer which only displays the +textured surfaces of the data\-cube and is therefore very fast and a voxel renderer that +can also display the inner structure of a data cube at the expense of a substantial amount +of computational overhead. You may switch between these two modes any time (see +\ref{ImageModeVolumetric}). +\item[Orthosection] can only be used to visualize volumetric data which is rendered +as three orthogonal slices with user-configurable thickness through the volume +(see \ref{ImageModeOrtho}). +\item[Height field] is a heightfield renderer that displays arbitrarily dimensioned data as a +shaded 3D heightfield (see \ref{HeightFieldRenderer}). +\end{description} + +\item[Other modes:]\ \\ +\begin{description} +\item[Chart] is a 1D mode that can display data as simple chart diagrams (see +\ref{ChartDisplayMode}). +\item[Table] is the most generic of all modes in that can display data of any +dimensionality and base type as a table of numeric values (see \ref{TableDisplayMode}). +\item[Sound] can be used to play MDD objects as sound samples (see \ref{SoundDisplayMode}). +\end{description} + +\end{description} + +There can only be one open viewer for each object for each display mode, so opening an +object in flat image and height field or chart mode is possible whereas opening two flat +image viewers on one object is not.\\ +There are two menus available here:\\ + +\subsection{Item} + +This menu offers three items: \texttt{Open all} opens viewers of the type specified by +\texttt{Display Mode} for all objects in the results list. \texttt{Thumbnail All} opens +a thumbnail viewer window containing all objects in the results list. \texttt{Close} +closes the results window and all its viewer windows and frees all memory +allocated by the objects in the results list. + +\subsection{Selection} + +The operations in this menu deal with selections of objects. The available menu +items are + +\begin{description} +\item[Select all:] Mark all objects in the collection as selected. +\item[Clear:] Clear the current selection. +\item[Open:] Open the selected objects in the viewer mode specified by \texttt{Display +Mode}. +\item[Thumbnails:] Open a thumbnail viewer containing only the selected objects +rather than the entire collection. +\item[Delete:] Delete the selected objects and thus free the memory allocated to +them. Use this if you're short on memory and only need a part of the collection +for future work. This does not affect persistent objects in the database. +\item[Change Endian:] Changes the endianness of the selected objects. +\item[Type Manager:] Allows you to change the base types of the selected objects +by building new objects consisting only of selected type members. See +\ref{TypeManager}. +\item[Info:] Additional information about the selected objects. Not yet implemented. +\end{description} + + +\subsection{Other options} + +You can also scale the selected objects using the \texttt{Scale/Resample} widgets to the +right of the control field. The writable field contains comma- or space-separated +scaling factors for each dimension. If there are fewer scaling factors than dimensions, +the last scaling factor available will be used for all dimensions that don't have +an explicit scaling factor assigned to them. e.g.\ resampling a 3D object using +\texttt{"2.0"} scales the entire object by 2 in all dimensions, \texttt{"2.0, 3.0, 4.0"} +scales the object by 2 in the first dimension, by 3 in the second and by 4 in the +third while \texttt{"2.0, 1.5"} scales the object by 2 in the first dimension and +by 1.5 in the second and third dimension.\\ +Different algorithms are used for simple scaling, upsampling and downsampling. +Simple scaling just uses nearest neighbours and is therefore very fast. The downsides +are blockiness when scaling up and aliasing when scaling down. Resampling addresses +these problems at the expense of speed. Downsampling averages over subcubes whereas +upsampling performs n-linear interpolation. Therefore care should be taken when +resampling that the scaling factors specified are either all larger or all smaller +than 1.0. Also note that especially upsampling is very time- and memory-consuming. + +\subsection{The Type Manager} \label{TypeManager} + +The Type Manager allows you to build new mdd objects out of existing ones by +copying selected member variables of the source objects only. The Type Manager +can be opened by selecting a menu entry from the \texttt{Results window} +(\ref{ResultWindow}). This will open a new window which graphically represents +the base type of the selected object(s). There is a checkbox for each member +variable; each checkbox is labeled "\textsl{varname} (\textsl{type})" where \textsl{varname} +is the name of the member variable (if available) and \textsl{type} is the type of +this member variable. Structures are recursively grouped together and bounded by +a rectangular box each. The member variables that should be copied into the +newly created object have to be selected using the mouse. If the conversion +is executed and the resulting base type is not known to \rview, the user is +requested to enter a type name. Unless the resulting object is intended to +be written into the database again, the name doesn't matter. \rview's visualization +techniques recognize all atomic types and a structure containing 3 bytes +(interpreted as RGB). + + +\section{Scalar results} \label{ResultStrings} + +If the result of a query is a (collection of) scalar(s) or an interval, +it will be displayed as a scrollable window where each line shows the +string-representation of a value. There are no further operations possible +on results of this type. + + +\section{Object viewers} +\label{ObjectViewers} + +All object viewers share certain basic components which will be explained here before +the individual modes are examined in more detail.\\ +Most importantly there's the projection string which lets you specify the dimensions +to visualize as well as pick areas of interest only rather than the entire object. +The projection string is of the form +\\ + +\begin{tabular}{rcl} +\textsl{projection} & = & $\underbrace{\mbox{\textsl{dim\_desc}}, \ldots , \mbox{\textsl{dim\_desc}}}_{\mbox{\textsl{dimension} times}}$ \\ +\textsl{dim\_desc} & = & \textsl{desc} $\vert$ \textsl{desc:desc} \lbrack\textsl{map\_dim}\rbrack\\ +\textsl{desc} & = & \textsl{coordinate} $\vert$ *\\ +\textsl{coordinate} & = & \lbrack 0-9 \rbrack +\\ +\textsl{map\_dim} & = & \lbrack 0-9 \rbrack +\\ +\end{tabular} + +where * and *:* are synonymous, meaning the entire range of this dimension. If +$\mbox{\textsl{dim\_desc}}_i$ is of the form \textsl{coordinate} a projection to this coordinate in +dimension $i$ is performed; dimension $i$ is a \emph{fixed dimension}. If +$\mbox{\textsl{dim\_desc}}_i$ is of the form \textsl{desc:desc} this is interpreted as \textsl{low:high} +pair of coordinates in dimension $i$ and the entire range between \textsl{low} and \textsl{high} +is selected; dimension $i$ is a \emph{free dimension}. The optional addition of \textsl{map\_dim} +in square brackets allows re-ordering dimensions in \emph{some modes} (e.g.\ flat images and +tables); in this case \textsl{map\_dim} is the number of the dimension that should be associated +with this interval, starting from 0. This mechanism allows things like transposing. If the +number of free dimensions is not equal to the dimensionality of the display mode an error +in the projection string is reported.\\ +\textbf{Examples:} Assuming a 3D-object with the spatial domain +\lbrack 0:200, 100:400, 200:600 \rbrack\ the following projection strings result in \ldots\\ + +\begin{description} +\item[*:*, *:*, *:*] -- Everything (3D). +\item[0, *:*, *:*] -- A projection to 0 in the first dimension and all data in the other +two dimensions, i.e.\ an object [100:400, 200:600] (2D). +\item[0, 200, *:*] -- A projection to 0 in the first and 200 in the second dimension, +all data in the third dimension (1D). +\item[100:150, *:300, 300:*] -- The coordinates 100 to 150 in the first dimension, +100 to 300 in the second dimension and 300 to 600 in the third dimension (3D). +\item[0, *:*\lbrack 2\rbrack, *:*\lbrack 1\rbrack] -- Like the 2nd example but transposed, +i.e.\ an object [200:600, 100:400]. +\end{description} + +Pressing \texttt{<return>} in the projection string widget or clicking on the \texttt{OK}-button +next to it updates the display accordingly. If there is at least one fixed dimension, clicking +on the + and - buttons increases or decreases the value of a fixed dimension, allowing you +to move through a datacube along a given axis. If there is more than one fixed dimension, +the writable field between the + and - buttons can be used to specify which one should +be affected by the + and - buttons. Enter the number of the dimension along which you want +to move here, starting from 0.\\ +\\ +All but the thumbnail viewers (see \ref{ThumbnailDisplayMode}) also provide the following +menu entries, listed under \texttt{Data}: + +\begin{description} +\item[Insert:] Insert the entire object into the database. This will pop up a +dialog box asking you for the name of the collection the object should be stored in. + +\item[Insert proj:] Insert the object's current projection only into the +database. + +\item[Save:] Save the raw MDD to disk. The resulting file is just the array data +without any spatial information, therefore this option should only be used on +1-dimensional data, e.g.\ TIFF-encoded images. + +\item[Close:] Close the viewer. + +\end{description} + +Additionally, all but the thumbnail viewer have a \texttt{View} menu with at +least two entries \texttt{Load} and \texttt{Save}. A "view" contains all meta +information required to visualize the current MDD as it is displayed in the +viewer's visualization area, e.g.\ renderer configuration, colourspace mapping +parameters, current rotation etc.; this meta information can be saved to and loaded +from a file with these two menu entries. A view is specific to one viewer type, +so a volumetric viewer's view file can not be loaded into a height field renderer. +Views can be used to easily memorize efficient visualization parameters for later +use (such as a good viewing angle for voxel rendering), or for visualizing the +effects of signal processing algorithms on an MDD as a sequence of images of the +same size/rotation/lighting/$\ldots$.\\ +Note that a view may contain very MDD-specific properties, such as the +MDD's numeric range used by the colourspace mapper or the spatial extent +for the projection string. Therefore a view should only be used for either +the same MDD or MDD with sufficiently similar properties, otherwise unwanted +side-effects may occur.\\ +Most modes also offer additional menus for specific features, such as saving the +current visualization to a TIFF image, or displaying the current rotation +numerically.\\ +Now follows a detailed description of the currently available display modes. + + +\subsection{Image viewers} +\label{ImageDisplayMode} + +The image display modes are the primary modes for graphical representation of MDD +objects. Next to simple orthogonal projections for displaying 2D images it also +includes 3D renderers which are described in more detail in \ref{Renderers}.\\ +There are various additional menus in this mode. \texttt{Data} contains the new +entry \texttt{Save TIFF} which saves the currently visible image to disk as a TIFF file. +The \texttt{Settings} menu contains configuration options dependent on the image +mode. The one entry available in all modes is the \textbf{Colourspace} menu, see +\ref{ColourspaceMapping}. Another possible entry, \textbf{Movie playback}, is +shared in flat and height field modes (sections \ref{ImageModeFlat}, +\ref{HeightFieldRenderer}) and allows the following choices on what to do +when the end of the movie has been reached (using \texttt{a = \textsl{start}} and +\texttt{b = \textsl{end}} of the movie): + +\begin{description} +\item[Once:] Just stop playback. +\item[Same direction:] Rewind the movie and restart using the same playback direction, +thus the sequence is \texttt{a $\rightarrow$ b, a $\rightarrow$ b, $\ldots$} or +\texttt{b $\rightarrow$ a, b $\rightarrow$ a, $\ldots$}, depending on the initial playback +direction. +\item[Switch direction:] Switch playback direction so the movie oscillates between +end-points and the sequence is \texttt{a $\rightarrow$ b, b $\rightarrow$ a, $\ldots$} or +\texttt{b $\rightarrow$ a, a $\rightarrow$ b, $\ldots$}, depending on the initial playback +direction. +\end{description} + +Movie mode is possible whenever the dimensionality of the MDD object exceeds the +dimensionality of the visualization mode (both flat and height field images are 2D, +therefore movies are possible if the MDD object has dimensionality three or higher). +The actual movie playback can be controlled with additional buttons created below +the projection string's +/- buttons. These new buttons, +which can only be used in \texttt{Flat} and \texttt{Height} mode, behave like auto-repeating ++/- buttons and allow displaying a datacube as a movie. "$<$" corresponds to "-" +and "$>$" corresponds to "+". In case of more than one free dimension the same +rules apply that are used for the +/- buttons (see \ref{ObjectViewers}). + +In all image modes except for the one for scaled images (see \ref{ImageModeScaled}) you +can mark a rectangle by dragging its outline while holding down the \texttt{ctrl} key. +The pixel coordinates of the dragged box relative to the upper left corner of the image +will be displayed in the upper left corner of the box in the format +$x_{low}:x_{high}, y_{low}:y_{high}$. Using the left mouse button for the +drag will always start a new box, using the right mouse button will adjust the size of +the existing box by moving the nearest corner to the position of the mouse pointer. To +remove the box simply click inside the image with the \texttt{ctrl} key held down. + + +\subsubsection{Flat Images} \label{ImageModeFlat} + +This mode visualizes orthogonal 2D projections of the MDD object. There are no +restrictions on dimensionality, but only two dimensions can be displayed +simultaneously. The scale slider can be used to scale the image by any factor +between 0\% and 500\% using a simple nearest neighbour algorithm which is +much faster than the resampling procedure available in the Results window +(\ref{ResultWindow}) but also of considerably worse quality. The size of the image +and hence the redraw time is determined by the size given by the projection string +times the scaling factor and is therefore independent of the view window size. +Flat mode is the recommended viewing mode for actual 2D images or movies +whereas for 3D data one the the volumetric renderers is the better choice +(see \ref{ImageModeVolumetric}). +%In flat mode the orientation of the horizontal axis is from top-to-bottom. + + +\subsubsection{Scaled Images} \label{ImageModeScaled} + +This class is very similar to flat images (see \ref{ImageModeFlat}) in that it +performs orthogonal 2D projections of the MDD object. This mode is intended +for very large MDD objects that are only transfered in part and/or scaled down +from the server but never reside in client memory in their entirety. Hence there +is no scale slider nor movie controls.\\ +In contrast to the other image modes, a box is dragged without holding down the +\texttt{ctrl} key and the dragged box will always have the same aspect ratio +as the visible image. The available functionality is represented by four buttons +below the projection string, + +\begin{description} +\item[To Box:] scale up the image within the dragged box to fill the entire +area currently visible. +\item[Back:] return to the previous view. +\item[Zoom In:] magnify the currently visible image by 2 without changing the +size of the resulting image, i.e.\ the area of the unscaled image covered by the +new view is only $\frac{1}{4}$th that of the original view. The upper left corner +remains the same for both views. +\item[Zoom Out:] the inverse operation of \texttt{Zoom In}. The visible image +of the new view can become smaller than in the previous view, however. +\end{description} + +A \emph{view} comprises all meta data that uniquely identifies a visualization +of an MDD object, i.e.\ a scaling factor, a bounding box (n-D interval) and +the two free dimensions. Whenever either parameter is changed, the currently +active view is saved on a stack before the new view is activated. Pressing the +\texttt{Back} button retrieves just this meta data from the stack in client memory +whereas the corresponding data is reloaded from the \rman\ server; except for +the view meta data no data is cached by \rview. There is no limit on the depth +of the view stack.\\ +Note that the projection string is for the entire, unscaled object, whereas the +drag box coordinates are relative to the current view. Any changes to the projection +string will be translated to the current view before they're applied. Also, +changing the projection string equals the definition of a new view and +stacking of the previous one. + + +\subsubsection{The Renderers} +\label{Renderers} + +For the various renderers, some new functionality is available in the viewer +window. The \texttt{BBox} checkbox determines whether the renderer should draw the object's +bounding box as well. Also there are two new menu entries in the \texttt{Settings} +menu: + +\begin{description} +\item[Renderer:] Allows changing parameters needed by the 3D renderers. See +\ref{RendererModel}. +\item[Renderer controls:] Allows animating rendered images by continuous +rotation (see \ref{RendererControls}). + +\end{description} % settings menu + +Renderers also have an additional entry \texttt{Show} in the \texttt{View} menu +which displays the current rotation matrix, z-offset and scale in a small window. +The rotation matrix ($3\times 3$) is represented by three rotations around the +coordinate axes, first $x$, then $y$, then $z$; the angles are in units of $\pi$ +rather than degrees. All entries in this window can also be altered manually.\\ +The renderers always use an image the size of the view window, so it's not a good +idea to make the view window substantially larger than the area covered by the +object's bounding box, because although the renderers don't operate outside the +object's bounding box the image will be larger than necessary and therefore take +longer to display, in particular when the display is remote over a network. + +\paragraph{Renderer Basics} \label{RendererModel} % hardspace to make linefeed possible +\ \\ +First let's specify the coordinate system: x is the +horizontal axis, oriented left to right, y is the vertical axis, oriented bottom to +top and z is the depth axis (parallel to the screen normal), oriented into the screen. +The observer is at position $(0, 0, 0)$ where the line $(0, 0, x)$ is projected to +the pixel in the image center. All renderers use perspective projection of +the data cube to the \emph{projection plane}, a plane parallel to the screen surface +that intersects the z-axis at position $(0, 0, z_p), \quad z_p > 0$. The value of $z_p$ +determines how much effect perspective projection will have, i.e.\ how quickly an object +gets smaller as its $z$ component increases. The smaller the value of $z_p$ +the higher the impact of perspective projection. Values between $256$ and $512$ +generally produce good results, with larger values recommended for large objects. +The value of $z_p$ can be changed in the \texttt{Image settings} window which is +opened by selecting the \texttt{Settings $\rightarrow$ Renderer} menu available +in the image viewer.\\ +Parts of the data cube lying too close to (or even behind) the viewer can't be +rendered, therefore the cube has to be clipped. In order to do this the intersection +of the cube with the \emph{clipping plane} has to be calculated. The clipping plane +is a plane parallel to the projection plane, intersecting the z-axis at position +$(0, 0, c_z), \quad c_z > 0$. In effect the cube is "cut open" so data formerly inside the +cube will be mapped to the new surface. Generally speaking, the value of $c_z$ should +be smaller than $z_p$. Values in the area of $\frac{z_p}{2}$ give good results, but +if you want to slice through the cube without moving it closer to the viewer this +can be achieved easily by incrementing the value of $c_z$ in the \texttt{Image +Settings} window.\\ +Using the \texttt{Scale} slider scales the data cube itself, not the rendered +image which means that you can also get z-clipping effects by scaling the +cube. It also means that you still get smooth edges in an upscaled renderer +display and the texels aren't as noticable as their edges are parallel +to the data cube's edges rather than parallel to the coordinate system.\\ +You can switch on/off lighting in the \texttt{Image settings} window; currently +this will only have an effect in voxel mode. Let $l$ be the normalized vector +from the voxel to the light source, $n$ the outward-oriented surface normal and +$\alpha$ the angle between the two ($\cos\alpha = l \cdot n$). Furthermore let +$\beta$ be the \emph{light angle} and $\gamma$ the \emph{scintillation angle} +specified in the \texttt{Image settings} window; both describe the range of angles +$\alpha$ for which the corresponding parameter should have any effect: + +\begin{center} +\begin{tabular}{rlrl} +$\lbrack \beta, 180 \rbrack$ & ambient light only & $\lbrack \gamma, 180 \rbrack$ & no scintillation \\ +$\lbrack 0, \beta )$ & ambient light + shading & $\lbrack 0, \gamma )$ & scintillation \\ +\end{tabular} +\end{center} + +One way to express this with normalized weighting factors would be + +\begin{displaymath} +w_l(\alpha,\beta) = \left\lbrace\begin{array}{rl} +\frac{\cos\alpha - \cos\beta}{1 - \cos\beta} & \mbox{if} \cos\alpha > \cos\beta\\ +0 & \mbox{otherwise}\\ +\end{array} \right. , \qquad +w_s(\alpha,\gamma) = \left\lbrace\begin{array}{rl} +\frac{\cos\alpha - \cos\gamma}{1 - \cos\gamma} & \mbox{if} \cos\alpha > \cos\gamma\\ +0 & \mbox{otherwise}\\ +\end{array} \right. +\end{displaymath} + +Note that values greater than 90 (degrees) for $\beta$ or $\gamma$ are unphysical; +they do help to bring out details in dark areas, however. It is advisable to +decrease the level of ambient light when increasing the light angle. +Using these weighting factors a voxel is shaded according to the equation +\begin{displaymath} +pixel = voxel \cdot (amb + w_l(\alpha,\beta) \cdot (1 - amb)) + voxel_{grey} \cdot gain \cdot w_s(\alpha,\gamma). +\end{displaymath} +The $amb$ parameter determines the level of ambient light and should be in the +interval \hbox{\lbrack 0, 1\rbrack} where 0 means no ambient light (surfaces not hit +by the light source are totally black) and 1 means full intensity provided by ambient +light (which is equivalent to shading off). The first part of the formula thus +performs a darkening of the actual voxel colour by giving it a base intensity +determined by $amb$ and additional intensity depending on the surface orientation. +$voxel_{grey}$ is the greyscale value associated with the voxel colour which, +when multiplied by $w_s(\alpha,\gamma)$, produces a shaded greyscale version of the voxel, +so the second part of the formula adds a weighted version of the shaded greyscale +voxel scaled by $gain$ to the shaded voxel. The $gain$ parameter has to be $\ge 0$ and the +higher it is the brighter those surfaces where $\alpha < \gamma$ will become. This +also influences the actual colour of a voxel by forcing a shift towards white the +more directly the voxel is facing the light source.\\ +The position of the light source is described by the \texttt{Direction} and \texttt{Distance} +fields in the \texttt{Image settings} window. The possible directions are encoded as a string +consisting of arbitrary combinations of direction aliases or $\epsilon$ (the empty +word) for each dimension. Those aliases are \texttt{l, r} (left, right) for the first dimension, +\texttt{d, u} (down, up) for the second dimension and \texttt{f, b} (front, back) for the +third dimension. Only one direction alias may be specified for each dimension, so \texttt{l} +and \texttt{r} are mutually exclusive. Thus the direction string lets you specify the +26 vertices on the surface of a 3D cube bisected in each dimension through its center +(plus the 27th vertex +in the center, but that choice wouldn't be very sensible). Examples would be \texttt{ur} +(corresponding to $(1,1,0)^T$), \texttt{uf} ($(0,1,-1)^T$) or \texttt{dlb} ($(-1,-1,1)^T$). +Use the \texttt{Distance} field to determine at which distance from the origin along the +direction vector the light source should be situated.\\ +For more tuning parameters regarding normal approximation see section \ref{VoxelRenderer} +about the voxel renderer. + +\paragraph{Navigating} \label{RenderNavigation} +\ \\* +The data cube can be rotated by moving the mouse while holding down the left +mouse button. Moving the mouse horizontally rotates around the y-axis (positive +angles by moving to the right), moving the mouse vertically rotates around the +x-axis (positive angles by moving to the bottom). This system may be improved +to something more intuitive in the future.\\ +The cube can be moved along the z-axis by moving the mouse vertically while +holding down the right mouse button. Moving upwards brings the cube closer +to the viewer while moving downwards takes to object further away. + +\paragraph{The Renderer Controls} \label{RendererControls} +\ \\* +This window is opened using the \texttt{Settings $\rightarrow$ Renderer Controls} menu +in an image viewer and makes it possible +to animate rendered images. The window consists of three sliders representing the +three rotation axes with a button labeled "0" to the right of each. Clicking on one of +the "0" buttons resets the corresponding slider to 0, thus terminating rotation around +that axis. Rotation starts when you click the lower left button labeled \texttt{Start} which +will then change to \texttt{Stop}. The slider values can be changed at any time, even +after animation has been started; dragging the cube directly with the mouse is +still possible when animation is in progress, too. You can stop the animation by clicking +on the \texttt{Stop} button in the \texttt{Renderer controls} window or by clicking on the +button labeled \texttt{\lbrack \rbrack} in the image viewer window which is also used +for stopping movie playback. + +\paragraph{Volumetric renderers} \label{ImageModeVolumetric} +\ \\ +The volumetric renderers can only handle 3D data with base type sizes of 1 to 4 or +8 bytes where base types of size 4 may be \texttt{long} or \texttt{float} and a base type +of size 8 is interpreted as double. Two volumetric renderers are combined in +one image viewer and the user may freely switch between modes by using the new +menu \texttt{Modes}. The third is an orthosection viewer (see \ref{ImageModeOrtho}) +which is uses a separate window. + +\subparagraph{Surface renderer} \noindent \label{SurfaceRenderer} +\ \\* +The surface renderer only visualizes the surfaces of the data cube by texture-mapping +the data on the cube's surface to the polygons representing it (which may be +rotated and scaled in any way). While this is very fast it has the drawback that +data inside the cube can not be visualized at all unless z-clipping is applied +to the cube (see the basic model). This mode is recommended for rotating or +positioning the cube before switching to a better but more time-consuming +mode like the voxel renderer, for instance. It's also the mode of choice for +data where not all three dimensions are spatial (e.g.\ movies). + +\subparagraph{Voxel renderer} \noindent \label{VoxelRenderer} +\ \\* +The voxel renderer is recommended for visualizing data where all three dimensions +are spatial (i.e.\ volume data). Data is visualized by casting rays through +the volume and calculating the frontmost intersection of each ray with a +non-empty pixel. In order to get good results over a wide range of input +data a lot of configuration options are available in the \texttt{Renderer} +menu.\\ +A few definitions first: an \emph{empty pixel} is one whose value lies outside +the threshold interval +$$ti = \lbrack pixel\_threshold\_low, pixel\_threshold\_high \rbrack .$$ +These threshold values are base type dependent; if you specified \texttt{For mode} +in the preferences window, \rview\ tries to find sensible defaults for these +parameters, depending on the type, otherwise the values given in the preferences +are used. A special case is the RGB base type where there are two thresholding +models: a pixel is interpreted as empty if all its colour components are outside +the threshold interval (\texttt{RGB brightness} off) or it's considered empty if the +average of its colour components is outside the threshold interval +(\texttt{RGB brightness} on).\\ +Since data obtained by computer tomograms and similar techniques usually +includes a fair amount of noise the naive approach of simply using the +frontmost non-empty pixel intersected by the ray usually doesn't produce +any meaningful results. What's needed is some kind of averaging procedure that +makes the ray penetrate deeper into the data until a weight criterium is met +and then average over the non-empty pixels that were intersected. Simply +adding these pixel values produced poor results as well, however. A technique +that gave very good results was first calculating a weight for each pixel +using the formula $w = \vert \frac{v + 2^q - 1}{2^q} \vert$ for integers and +$w = \vert \frac{v}{2^q} \vert$ for floating point types, where $v$ is the +pixel value (in RGB mode this is the average of the colour components) and $q$ +is the \emph{weight quantisation}. Thus the weight is proportional to the pixel +value and inversely proportional to $2^q$. The weighted sum of pixels $s$ is updated +using $s = s + w*p$ where $p$ is the pixel value; note that in RGB mode $s$ and +$p$ are 3D vectors over the RGB colourspace. Since $w$ was proportional +to $v$ the update value is proportional to $v^2$, i.e.\ "brighter" pixels have +a much stronger influence on $s$ than "darker" ones. Additionally the sum of +weights $n$ is updated using $n = n + w$. If $n$ overflows the \emph{weight +threshold} $w_{max}$, the ray is terminated and the pixel value used in this +position is $\frac{s}{n}$, otherwise the search for the next non-empty pixel +continues. If there are no more non-empty pixels and the pixel value $\frac{s}{n}$ +computed so far is bigger than the lower pixel threshold, this pixel value is +used, otherwise the pixel is considered transparent.\\ +Summarizing: $q$ determines how much the weight is correlated with the pixel +value where small values of $q$ mean a strong dependence and large values +a weak dependence (since +$\lim_{q \longrightarrow \infty}{\vert \frac{v + 2^q - 1}{2^q} \vert} = 1$ and +$\lim_{q \longrightarrow \infty}{\vert \frac{v}{2^q} \vert} = 0$, both of which +are monotonous decreasing in $q$). +The default value of 4 gives good results with most tomograms. The weight threshold +$w_{max}$ determines how far the ray will penetrate into the volume. If that +value is too small, normally invisible noise will prematurely abort the ray and +thus have a noticable effect on the display (seemingly random, dark blotches instead +of a smooth surface). Another problem is that typically the pixel colour doesn't +change rapidly from one pixel to its neighbour but rather slowly over a number +of pixels, so for instance a white object on a black background is usually surrounded +by several layers of grey-level pixels. When $w_{max}$ is too small the ray will stop +in those grey-levels rather than in the white object. Trying to remedy this by just increasing +the lower pixel threshold doesn't work well and even with large values the results +still look poor. So if you can't see far enough into the cube you should first try +increasing $w_{max}$. If $w_{max}$ is too large, on the other hand, you get an x-ray +effect where the entire object becomes transparent. Also note that the further the +ray penetrates into the volume the longer the rendering takes.\\ +If you're using lighting there are some more tuning parameters available from the +\texttt{Image settings} window in the voxel section which deal with normal approximation, +namely \texttt{Kernel size} and \texttt{Kernel type}. The kernel is a symmetric +function that will be folded with the sample values, thus producing a smoothed +normal vector that can compensate for sample noise. The kernel type determines how +this smoothing is performed. \texttt{Average} gives each sample the same weight, the other +two are functions over the distance $d$ (L$_2$-norm) of a cell from the kernel center. +\texttt{Linear} is the function $1 - \frac{d}{s \sqrt{3}}$ and \texttt{Gauss} is the function +$e^{-\frac{d}{2}}$ where $s$ is the kernel size.\\ +The kernel size parameter is the maximum distance of a point in the kernel from the +kernel center using the L$_{\infty}$ norm. Therefore a kernel size of $n$ results in a +kernel cube of size $(2n + 1)^3$. A kernel size of 0 means no smoothing (and hence the +kernel type is irrelevant), the normal is approximated directly from the sample values, +therefore this mode produces very bad results with noisy data. For typical samples a +kernel size of 1 or 2 is much more recommended, although the rendering will take +substantially longer due to the kernel cube's size (27 cells for a kernel size of 1, +125 cells for a kernel size of 2). In theory the normal vectors could be cached, however +this would require 12 bytes of storage for each cell, so a greyscale tomogram of size +10MB would require 120MB for the normal vector field which seems excessive.\\ +One problem when shading volumetric data is that the entire image tends to darken +considerably. This is a problem especially when plotting isosurfaces. To overcome +this limitation there's the option to ignore the voxel colours and assign the same +intensity to each voxel instead, which should be a bright colour (e.g.\ white); in this case +the sample values are only used to approximate the object's surface but not for texturing +it as well. Of course this only makes sense when shading is active since otherwise only +the object's outline could be seen, therefore this option is ignored with lighting +deactivated. The colour to assign to each voxel can be specified in the \texttt{Voxel colour} +field; this is a floating point number in order to be able to handle all base types. +The default value of this parameter depends upon the setting of +\texttt{For mode} in the preferences window (i.e.\ when on \rview\ tries to find a sensible +default for the MDD's base type). A value of 255 for a greyscale image is pure white, +for instance. A more complicated example is RGB: in that case the colour is encoded as +a number with the red component in bits 0-7, the green component in bits 8-15 +and the blue component in bits 16-23, so red is 255, green is 65280, blue is +16711680 and pure white (the default) is 16777215. Alternatively you can specify +the voxel colour as a hexadecimal value starting with a \texttt{0x} prefix which is +easier for selecting RGB colours (e.g.\ \texttt{0xff00} for green).\\ +\emph{Tuning}: bad image quality when rendering with lighting is usually the cause of the +kernel size being too small or the pixel/weight threshold values being wrong. If the +ray doesn't penetrate deep enough you get the aliasing effects described above. If +on the other hand the ray penetrates too deep the surface normal will get approximated +wrongly because the ray terminated a substantial distance away from the actual surface. +If the image is too dark you should try ignoring the voxel colour and using a very +bright default colour instead. Only experimentation will result in the best compromise of all +parameters.\\ +Examples:\\ +For \texttt{tomo\_cubed} use \texttt{pixelThresholdLow = 16}, \texttt{weightThreshold = 16} and +\texttt{kernelSize = 2}. When ignoring the voxel colours set \texttt{lAngle = 180}, +\texttt{Ambient = 0} and \texttt{Gain = 0.5}. For \texttt{frog\_cubed} set \texttt{pixelThresholdLow = 32}, +\texttt{weightThreshold = 8} and \texttt{kernelSize = 2}. When ignoring the voxel colours set +\texttt{lAngle = 180}, \texttt{Ambient = 0.0} and \texttt{Gain = 0.0}. + + +\subparagraph{Orthosection renderer} \label{ImageModeOrtho} +\ \\ +This viewer mode renders volume images as three orthogonal sections through the +volume, as commonly used in the visualization of medical images. The resulting +image can be rotated and translated along the z-axis by using the mouse like in +the other rendered modes.\\ +This viewer comes in two variants, depending on how it's opened: + +\begin{description} +\item[partial:] when opened from the main window's \emph{Collections} menu +(see \ref{MainWindow}); in this mode only the data for the three sections currently +visible is held in main memory and new data is loaded from the database +when required (but not during a drag operation). +\item[full:] when opened from the results window (see \ref{ResultWindow}); in +this mode the data for the entire MDD is present in main memory and therefore +sections can be changed arbitrarily without requiring further database accesses. +\end{description} + +For both modes there are several new widgets in the control area: + +\begin{itemize} +\item +For each of the three dimensions there's a slider representing the position where each +hyperplane intersects the axis orthogonal to it. To the right of the slider is a +text widget where positions can be entered manually. As soon as the mouse pointer +touches the slider's well (that area within which the bar slides), the section it +represents is crossed out in the display to facilitate navigation; the cross is +removed when the mouse pointer leaves the well. The slider is dragged as usual +by holding down the left (or right) mouse button. Depending on the mode (\emph{partial} +or \emph{full}), the dragged section is displayed as a solid grey area or the actual +image data. +\item +Towards the bottom right is a text widget labelled \emph{Thick} where the thickness +of a section in cells can be entered. Note that the handling of thick sections is +not optimized in any way and in case the viewer is used in partial mode the cells +intersected by several sections are loaded from the database for each slice, leading +to an expansion of the data volume transferred by a factor of 3 in the worst case +(although the amount of overlap is neglectable for thin slices). If you want to +work with thick sections extensively, use the viewer in \emph{full} mode. +\item +Below the thickness widget are two more widgets labelled \emph{Auto} and +\emph{Load}. These determine how the display is updated in partial mode +when sections have changed. Dragging a section in partial mode displays +the section as a filled grey surface to show that the cell values are +unknown. If \emph{Auto} is checked, the new section is automatically loaded +from the database after the mouse button used to drag the slider is released, +otherwise the display is not updated automatically. The \emph{Load} button can +be used to explicitly request an update at any time; this is necessary in case +\emph{Auto} isn't checked or the database was closed during the drag and the +new data couldn't be loaded when the mouse button was released. +\end{itemize} + +\textbf{Current limitations of the Partial mode:} +\begin{itemize} +\item +There is no section cache, so moving the slider will always access the database, +no matter whether the sections were already loaded at an earlier time in the session. +\item +Colourspace mapping in any but \emph{type range} mode is problematic because due to +the partial character, there is no information about the value range of the entire +MDD, so some defaults are assumed. This can be easily remedied manually by opening +the colourspace editor and entering min/max values to use for the translation, but +it isn't done automatically yet; automatic updates could also lead to unwanted +side-effects because if a new slice changed the min/max values currently used, +the colours of the other two sections would be affected as well. +\end{itemize} + + + +\paragraph{Height field renderer} \label{HeightFieldRenderer} +\ \\ +This mode can be used on data of any dimensionality $d$ with an atomic base type. It +performs a mapping of the form $y_i = f(x_{1,i}, \ldots, x_{d,i})$ where all but two +of the $x_{j,i}$ are constant, namely $x_{m,i}$ and $x_{n,i}$ (i.e.\ $m$ and $n$ are +free dimensions as described in \ref{ObjectViewers} and have to be given by the +projection string). In other words the value of the cell at position +$(x_{1,i}, \ldots, x_{d,i})$ is interpreted as height information. +When connecting all the vertices $(x_{m,i},y_i,x_{n,i})$ +thus obtained, a surface in 3D space is created which is rendered using shaded +polygons, the colour of which can be set via \texttt{VoxelColour} (see the +\texttt{Image settings} window). Note that this field also accepts hexadecimal +numbers starting with a \texttt{0x} prefix. If the value of the colour is less then +256 it will be interpreted as a grey level, otherwise a colour of the form +\texttt{0xbbggrr}. You can force RGB mode on by setting bit 24 of the colour +value (required for pure reds). Forcing on RGB mode overrides colourspace mapping, +if it was turned on.\\ +A primitive light model is always used, namely $pixel = colour \cdot \frac{cos(\alpha)+1}{2}$ +where $\alpha$ is the angle between the surface normal and the light source. When +switching on lighting the full lighting model described in section \ref{RendererModel} is used. +Further options for this mode are the grid size $g$ and the height scaling factor $s$ +which are used to create the set of surface vertices +$S = \lbrace (i \cdot g, j \cdot g, s \cdot m(i,j) \quad \vert \quad l_0 \le i \le h_0, l_1 \le j \le h_1 \rbrace$ +where $m$ is the given 2D projection with the domain $\lbrack l_0:h_0, l_1:h_1 \rbrack$ +of the original nD data $M$ ($m \subseteq M$).\\ +You can also do animations with this mode when the dimensionality of the MDD object +is higher than 2. Using the movie playback buttons (or +/-) you can slice through the +data like in \texttt{flat} mode, but with a different rendering technique.\\ +This mode should not be used on large data sets because the number of polygons to render +is $2 (h_0 - l_0) (h_1 - l_1)$, so even a moderately sized image containing 512*512 +pixels would result in over half a million polygons which take several seconds to +render. As a general rule of thumb a size of 100*100 should not be exceeded (which +still requires about 20000 polygons). In case of bigger data it's recommended to scale +it down prior to using the height field renderer, e.g.\ by using the scaling functionality +provided in the \texttt{Results Window} (\ref{ResultWindow}). + + +\subsubsection{Colourspace mapping} +\label{ColourspaceMapping} + +Colourspace mapping can be used for visualizing MDD objects of all atomic base types. +The idea is to map an object's data $d$ of the data type $D$ with a potentially very +large range to the RGB colour space using a function $f_{cm}: D \rightarrow (r,g,b)$, +often with the restriction that small changes in the object values result in small +changes of the resulting colour, i.e.\ for a given $C \in \realnumbers$ + +$$\| f_{cm}(v_2) - f_{cm}(v_1) \|_2 \le C \vert v_2 - v_1 \vert \qquad \forall v_1, v_2 \in d .$$ + +This is achieved by using a transfer function $f_{tf}(x, \mu, \sigma)$ for each colour component +that maps a number $x \in D$ to the interval $\lbrack 0, 1 \rbrack$ where $\mu$ is the +position of the peak and $\sigma$ is the variation. The colourspace mapping is then +described by the equation + +\begin{displaymath} +f_{cm}(x) = +\left( \begin{array}{c} +r(x) \\ g(x) \\ b(x) +\end{array} \right) += +\left( \begin{array}{c} +f_{tf}(x, \mu_r, \sigma_r) \\ +f_{tf}(x, \mu_g, \sigma_g) \\ +f_{tf}(x, \mu_b, \sigma_b) \\ +\end{array} \right) . +\end{displaymath} + +In most cases it is desirable that $f_{tf}$ has a single, global maximum so a colour can +be easily associated with an intensity range. These thoughts led to modelling the default +transfer function $f^{gauss}_{tf}$. All in all there are four transfer functions available, +but not all of them meet all the criteria mentioned above: + +\begin{itemize} +\item +$f^{gauss}_{tf}(x, \mu, \sigma) = e^\frac{-(x - \mu)^2}{\sigma^2}$\\ +a standard gaussian. In this case +$C = \sqrt{\frac{6}{e}} \mbox{max}_{i \in \{r,g,b\}} \vert \frac{1}{\sigma_i} \vert$ +(see appendix \ref{CalculateC}). This function is symmetric, smooth and has a distinct +peak. + +\item +$f^{linear}_{tf}(x, \mu, \sigma) = \left\lbrace \begin{array}{rl} +1 - \left|\frac{x - \mu}{\sigma} \right| & | x - \mu | < \sigma\\ +0 & \mbox{otherwise}\\ +\end{array} \right.$\\ +a triangular spike that can for instance be used to perform +a mapping to greyscales directly proportional to the input value. The function +is symmetric and has a distinct peak but isn't smooth. + +\item +$f^{rect}_{tf}(x, \mu, \sigma) = \left\lbrace \begin{array}{rl} +1 & | x - \mu | < \sigma\\ +0 & \mbox{otherwise}\\ +\end{array} \right.$\\ +a rectangular pulse in case quantisation effects are +explicitly wanted. The function is symmetric, has an extended peak but is +neither smooth nor continuous. + +\item +$f^{asympt}_{tf}(x, \mu, \sigma) = \left\lbrace \begin{array}{rl} +0 & x < \mu\\ +1 - e^\frac{-(x - \mu)}{\sigma} & \mbox{otherwise} +\end{array} \right.$\\ +an asymmetric mapping function that starts at position +$\mu$ and converges to 1 for $x \rightarrow \infty$. The function is smooth and +continuous but asymmetric and without a peak. + +\end{itemize} + +You can choose the mapping function you wish by using the widget in the lower right +of the colourspace mapping editor. It's quite easy to extend \rview\ to support +many more mapping functions.\\ +The default values for the $\mu$ and $\sigma$ are + +$$\mu_r = max, \quad \mu_g = min + \frac{2(max-min)}{3}, \quad \mu_b = min + \frac{max-min}{3} +\qquad \mbox{and}$$ +$$\sigma_r = \sigma_g = \sigma_b = \frac{max-min}{6 \sqrt{\ln 2}}$$ + +where $max$ and $min$ can be either the extreme values present in the entire object +(default), the extreme values the base type can represent (\texttt{Full range} on) or the +extreme values that occur in the currently selected projection of the object (\texttt{Proj range} +on). The default values of the $\sigma$-parameters are chosen in such a way that the sum of two +neighbouring gaussians halfway between their mean values is 1, i.e.\ apart from areas of the +input range that don't lie between two means the intensity doesn't change drastically.\\ +Colourspace mapping is available in Image and Thumbnail viewers (\ref{ImageDisplayMode}, +\ref{ThumbnailDisplayMode}). The parameters can be changed using the +\texttt{Settings $\rightarrow$ Colourspace} menu which will become available once colourspace +mapping has been enabled and results in the \texttt{Colourspace setup} window being opened. +This window contains a canvas showing the three mapping functions that make up the normalized +mapping function $f_{cm}^{norm}: \lbrack 0,1 \rbrack \rightarrow (r,g,b)$, each +plotted in the colour it encodes. If \texttt{Draw sum} is checked the sum of the three +mapping functions will also be plotted in black, scaled to $\frac{1}{3}$ the height of +each of the mapping functions with the dotted line representing the value 1. The relation +between $f_{cm}^{norm}$ and $f_{cm}$ is that $\mu_i = min + (max - min)*\mu_i^{norm}$ and +$\sigma_i = (max - min)*\sigma_i^{norm}$ for $i \in \{r, g, b\}$.\\ +In the lower half of the +window are widgets displaying the values of the $\mu_i^{norm}$ (E$(x)$), the +$\sigma_i^{norm}$ (s$(x)$), $i \in \{r,g,b\}$ and the values of $max$ and $min$ currently +used. Values can be changed by either entering the new values in the widgets directly +and pressing \texttt{<return>} or by dragging the curves. For a drag the mouse pointer has +to be close to the mean value of the curve you want to drag; if the curves are very close +together, red takes precedence over green, which in turn takes precedence over blue. +Holding down the left mouse button and moving left/right moves the function's mean value. +Holding down the right mouse button and moving up/down increases/decreases the function's +variance. The functions will be immediately redrawn to reflect your latest changes.\\ +If the \texttt{Update} checkbox in the lower half of the window is checked these changes +will also be propagated to the viewer window owning this colourspace mapper. If this +viewer's image is very big or the display mode very time-consuming (e.g.\ the Voxel +renderer) this might take too long so it is advisable to disable the immediate update +in these situations.\\ +Clicking \texttt{OK} makes the current setup permanent for this viewer and closes the +\texttt{Colourspace setup} window. \texttt{Cancel} discards the changes, reverts the +viewer to the state it had before editing and also closes the window. + + +\subsection{Chart viewers} +\label{ChartDisplayMode} + +This is a 1D mode that visualizes the data as a $(x, f(x))$ graph. There are three +modes available from the \texttt{Modes} menu: + +\begin{description} +\item[Bar:] Bar chart, every value is represented by a filled rectangle. +\item[Line:] Consecutive values are connected by straight lines. +\item[Spline:] The values are connected using spline interpolation, thereby +making it a smooth curve. +\end{description} + +\texttt{Chart} mode is available for all atomic base types. A special case is +the RGB type where the three components are plotted slightly shifted against +each other in their corresponding colours.\\ +\texttt{Step} is the width in pixels of two consecutive values on the horizontal +axis, so larger values will stretch the graph horizontally; in \texttt{Bar} mode this +is also the width of each bar. A coordinate system is plotted optionally, depending +on the value of \texttt{CO-System}. In case a coordinate system is requested it can +be configured in the following ways: \texttt{Y markers} is the step size of the markers +on the vertical axis. Since the base type may be in a floating point format this is +a floating point number. \texttt{X markers} does the same for the markers on the horizontal +axis but is an integer number because all coordinates are integers. You have to press +\texttt{<return>} in the corresponding widget for changes to have any effect. + + +\subsection{Table viewers} +\label{TableDisplayMode} + +This is a 2D mode that displays the data numerically. In contrast to the other display +modes this one is totally independent of the base type and can display any MDD object +for which schema information exists. The first free dimension is mapped to the horizontal, +the second free dimension to the vertical axis by default. The current grid size in pixels +is displayed as \texttt{Step X} and \texttt{Step Y}. If the default values given in the +preferences (\ref{PreferencesEditor}) are -1, \rview\ will +make a guess at the grid size based on font size and base type. The grid size can +be changed by explicitly entering new values into the corresponding widgets and +pressing \texttt{<return>}. If \texttt{CO-System} is checked the coordinate system will +be displayed.\\ +There are three number bases available from the \texttt{Base} menu: \texttt{Decimal}, \texttt{Octal} +and \texttt{Hex}. The grid size will be adapted automatically if the default grid size +was -1. \texttt{Octal} and \texttt{Hex} are also available for floating point types; in +the case of a \texttt{double} base type the format is two numbers separated by a colon. + + +\subsection{Sound viewers} +\label{SoundDisplayMode} + +This is a 1D mode that can play digital sound samples. 2D data is allowed and will +be interpreted as multichannel sound where the second free dimension describes the +channels. There are six buttons for controlling playback. The meaning of each, from left +to right, is: + +\begin{description} +\item[{\boldmath $\ll$}] Set playback position to start +\item[{\boldmath $\|$}] Pause/resume playback +\item[{\boldmath $>$}] Start playback (from beginning) +\item[{\boldmath $\lbrack\rbrack$}] Stop playback +\item[{\boldmath $\gg$}] Set playback position to end +\item[{\boldmath $\longrightarrow$ \mbox{\rm or} $\longleftrightarrow$}] Toggle between normal and looped playback. When in +loop mode the sample will be repeated indefinitely (until loop mode is switched off +or playback is stopped manually). +\end{description} + +Below these playback controls are options concerning the sample format and the latency. +Enter the \texttt{Frequency} in Hz in the writable field to the left. Standard sample frequencies +are 8000Hz, 11025Hz, 22050Hz, 44100Hz and 48000Hz and the sound hardware may have trouble +handling anything else. The \texttt{Format} widget can be used to specify the sample format. +You can only select formats where the size of a sample is the same as the size of the MDD +base type. Currently four formats are supported: + +\begin{description} +\item[8 bit sl:] 8 bit signed linear, sample midpoint is at \texttt{0x00} +\item[8 bit usl:] 8 bit unsigned linear, sample midpoint is at \texttt{0x80} +\item[8 bit ulaw:] 8 bit $\mu$-law (a logarithmic format used e.g.\ in ISDN) +\item[16 bit sl:] 16 bit signed linear, sample midpoint is at \texttt{0x0000} +\end{description} + +The \texttt{Latency} widget determines how many samples will be created ahead of the +actual playback. Small values have the advantage that playback reacts to user +input with no noticeable delays; however they make playback very vulnerable to +jitter in the buffer fill code which will be most noticeable on a machine +under load, leading to audible dropouts. Large values have the advantage of +being very stable regarding dropouts but are slow to react to user input +(i.e.\ the sound will keep playing a little after a stop or pause). A good compromise +is usually between 100ms and 300ms, depending on the machine and its load.\\ +The slider at the bottom of the window represents the position of the currently +audible signal in the sample. You can also drag it to an arbitrary position during +playback to set the playback position there. Like with stop and pause there will +be a small delay between the dragging and the playback actually changing due to +latency. + + +\subsection{Thumbnail viewers} +\label{ThumbnailDisplayMode} + +This mode is unusual in that it can contain images of any number of objects as well +as a series of images created from one object. By default one thumbnail image of each object +is displayed, scaled to be \texttt{Thumbnail width} pixels wide while maintaining the +aspect ratio (i.e.\ an object of size 400*600 and a thumbnail width of 100 will result +in a thumbnail image of 100*150 pixels). It's also possible to restrict the thumbnails +to part of the objects using the projection string (e.g.\ \texttt{30:70, 20:80} instead +of \texttt{*:*, *:*}). \texttt{Thumbnail width} may have any value between 10 and 2000, so +in most cases it can be used to produce magnified views as well as small thumbnails. +\texttt{Thumbnail columns} specifies how many thumbnails should be fitted on one +row.\\ +Objects with more than two dimensions can also be displayed as a series of thumbnails, +similar to the movie mode in the image viewers (see \ref{ImageDisplayMode}). In order +to do this the number of free dimensions has to be three, where two are needed for the +image and one for the dimension to iterate over. By default the first two free +dimensions are used for the image and the third for the iteration. This behaviour +can be changed by setting \texttt{ProjDim} to the number of the dimension that should +be iterated over, starting from 0 (e.g.\ with a projection string of +\texttt{10:100, *:*, 5, 1:*} the default dimension to iterate over is described by +\texttt{1:*}, setting \texttt{ProjDim} to 1 would change that to \texttt{*:*}). The iteration +step can be set using \texttt{ProjStep}; this is the number the coordinate of the +iteration dimension should be incremented by after each thumbnail image. The larger +this number the fewer thumbnails will be created for each object.\\ +Beneath each thumbnail is a small descriptive text of the form \texttt{MDD \textsl{mdd} +\lbrack \textsl{view} \rbrack} where \textsl{mdd} is the number of the MDD object in +the same order as listed in the results window (\ref{ResultWindow}), starting from 0, +and \textsl{view} is the number of the view in case the object was displayed as a series +of thumbnails. Note that \texttt{ProjStep} will also be reflected by \textsl{view}.\\ +Thumbnail viewers also provide colourspace mapping for objects of atomic base types +(\ref{ColourspaceMapping}); use and configuration is identical to image viewers +(\ref{ImageDisplayMode}) but due to the nature of the thumbnail mode as a container +of many objects, \texttt{Proj range} is not available. + + +\section{Platform specifics} +\label{Platforms} + +\subsection{Unix} + +\begin{itemize} +\item +Changing the font to use for query windows (\ref{QueryWindow}) has no effect. This is +a bug of the X-version of \wxwin. +\end{itemize} + +\subsection{NT} + +\begin{itemize} +\item +Exiting \rview\ with an open database crashes the program. +\end{itemize} + + +\hrule + + +% Appendix +\newpage + +\begin{appendix} + +\section{Colourspace mapping smoothness constant C} +\label{CalculateC} + +Let $g_i(x) = e^{-\frac{(x - \mu_i)^2}{\sigma_i^2}}, \quad i \in \{r,g,b\}$. Then + +$$\| f_{cm}(v_2) - f_{cm}(v_1) \|_2 = \sqrt{\sum\limits_{i \in \{r,g,b\}} (g_i(v_2) - g_i(v_1))^2}$$ + +According to the mean value theorem $g_i(v_2) - g_i(v_1) = g'_i(\xi) (v_2 - v_1), \quad +\xi \in \lbrack v_1, v_2 \rbrack$, so the above can be rewritten to +$$\sqrt{\sum\limits_{i \in\{r,g,b\}} (g'_i(\xi_i)(v_2 - v_1))^2} \le +\sqrt{3} \cdot \vert g'_{max} \vert \cdot \vert v_2 - v_1 \vert, \quad +\mbox{i.e.} \quad C = \sqrt{3} \cdot \vert g'_{max} \vert \quad$$ +$$\mbox{where} \quad \vert g'_{max} \vert = \mbox{max}_{i \in \{r,g,b\}, x \in \realnumbers} \vert g'_i(x) \vert.$$ + +$g'_i(x) = -2\frac{x - \mu_i}{\sigma_i^2} g_i(x)$ and +$g''_i(x) = -\frac{2}{\sigma_i^2} g_i(x) + 4\frac{(x - \mu_i)^2}{\sigma_i^4}g_i(x) = +\frac{2}{\sigma_i^2} g_i(x) (2\frac{(x - \mu_i)^2}{\sigma_i^2} - 1)$. $g'_i(x)$ has +a local extreme value where $g''_i(x) = 0$, +i.e.\ $2\frac{(x - \mu_i)^2}{\sigma_i^2} = 1 \Longleftrightarrow x_{0,1} = \mu_i \pm \frac{\sigma_i}{\sqrt{2}}.$ +Due to the symmetry $\vert g'_i(\mu_i + x) \vert = \vert g'_i(\mu_i - x) \vert$ it suffices to +examine the value of $g'_i(x)$ on one of these points only, e.g.\ $x_0 = \mu_i + \frac{\sigma_i}{\sqrt{2}}$: +$\vert g'_i(x_0) \vert = \vert \frac{\sqrt{2}}{\sigma_i} e^{-\frac{1}{2}} \vert = +\sqrt{\frac{2}{e}} \vert \frac{1}{\sigma_i} \vert$. Therefore the solution to the problem is +$C = \sqrt{\frac{6}{e}} \mbox{max}_{i \in \{r,g,b\}} \vert \frac{1}{\sigma_i} \vert$. + +\end{appendix} + +\end{document} diff --git a/applications/rview/rviewApp.cpp b/applications/rview/rviewApp.cpp new file mode 100644 index 0000000..b0323c3 --- /dev/null +++ b/applications/rview/rviewApp.cpp @@ -0,0 +1,828 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView main application (class rView) and main frame (class rviewMainFrame). + * The main application includes the database object which has to be accessed + * through rView-member functions. + * + * COMMENTS: + * none + */ + + + +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + + + +#include <stdlib.h> +#include <string.h> +#include <iostream.h> +#include <signal.h> +#include <ctype.h> + + +#include "labelManager.hh" + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + + +#include "raslib/rminit.hh" +#include "raslib/rmdebug.hh" + +#include "rviewApp.hh" +#include "rviewDb.hh" +#include "rviewPrefs.hh" +//#include "rviewQuery.hh" +#include "rviewIO.hh" +#include "rviewDModes.hh" +#include "rviewOSection.hh" + + + + + +/* + * Class for the lookup of scaled images, used internally only + */ + +class rviewLookScaleDialog: public rviewFrame +{ + public: + + rviewLookScaleDialog(rmanClientApp *parentApp, const char *name, double scale); + ~rviewLookScaleDialog(void); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + void OnSize(int w, int h); + + // constants + static const int lkscale_border; + static const int lkscale_theight; + static const int lkscale_bwidth; + static const int lkscale_bheight; + static const int lkscale_width; + static const int lkscale_height; + + + protected: + + wxPanel *panel; + rviewText *collName; + rviewText *scaleString; + rviewButton *okBut; + rviewButton *cancelBut; + + rmanClientApp *parent; +}; + + +const int rviewLookScaleDialog::lkscale_border = 8; +const int rviewLookScaleDialog::lkscale_theight = 50; +const int rviewLookScaleDialog::lkscale_bwidth = 50; +const int rviewLookScaleDialog::lkscale_bheight = 30; +const int rviewLookScaleDialog::lkscale_width = 300; +const int rviewLookScaleDialog::lkscale_height = 2*rviewLookScaleDialog::lkscale_border + 2*rviewLookScaleDialog::lkscale_theight + rview_window_extra_height; + + +rviewLookScaleDialog::rviewLookScaleDialog(rmanClientApp *parentApp, const char *name, double scale) : rviewFrame(NULL, lman->lookup("titleLookScaleColl"), -1, -1, lkscale_width, lkscale_height) +{ + panel = new wxPanel(this, 0, 0, lkscale_width, lkscale_height); + panel->SetLabelPosition(wxVERTICAL); + collName = new rviewText(panel, name); + scaleString = new rviewText(panel, scale); + okBut = new rviewButton(panel); + cancelBut = new rviewButton(panel); + parent = parentApp; + label(); + + frameWidth=-1; + frameHeight=-1; + + OnSize(lkscale_width, lkscale_height); + OnSize(lkscale_width, lkscale_height); + + Show(TRUE); +} + +rviewLookScaleDialog::~rviewLookScaleDialog(void) +{ +} + +void rviewLookScaleDialog::OnSize(int w, int h) +{ + int x, y, aux, boff; + + GetClientSize(&x, &y); + panel->SetSize(0, 0, x, y); + aux = x - 3*lkscale_border - lkscale_bwidth; + collName->SetSize(lkscale_border, lkscale_border, aux, lkscale_theight); + scaleString->SetSize(lkscale_border, lkscale_border + lkscale_theight, aux, lkscale_theight); + aux += 2*lkscale_border; + boff = lkscale_border + lkscale_theight - lkscale_bheight; + okBut->SetSize(aux, boff, lkscale_bwidth, lkscale_bheight); + cancelBut->SetSize(aux, boff + lkscale_theight, lkscale_bwidth, lkscale_bheight); +} + +void rviewLookScaleDialog::label(void) +{ + SetTitle(lman->lookup("titleLookScaleColl")); + collName->SetLabel(lman->lookup("lkScaleName")); + scaleString->SetLabel(lman->lookup("lkScaleScale")); + okBut->SetLabel(lman->lookup("textOK")); + cancelBut->SetLabel(lman->lookup("textCancel")); +} + +int rviewLookScaleDialog::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + int readAndClose=0; + + if (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND) + readAndClose = 1; + else if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)okBut) + readAndClose = 1; + else if (&obj == (wxObject*)cancelBut) + { + parent->LookupScaledCollection(NULL, 0.0); + Close(TRUE); + return 1; + } + } + if (readAndClose != 0) + { + double scale; + char *b, *rest; + + b = scaleString->GetValue(); + scale = strtod(b, &rest); + if (b == rest) + { + cerr << "Bad scaling parameter..." << endl; + } + else + { + parent->LookupScaledCollection(collName->GetValue(), scale); + Close(TRUE); + } + return -1; + } + return 0; +} + + + + + +//#define DUMMY_MDD_OBJECT r_UShort + +//FILE *logfile; // WIN32 debug!!! + + +// Emergency exit signal handler +void rmanAbort(int code) +{ + char *sig; + + // First things first + rmanClientApp::theApp()->Shutdown(); + + switch (code) + { + case SIGINT: sig = "SIGINT"; break; + case SIGABRT: sig = "SIGABRT"; break; + case SIGSEGV: sig = "SIGSEGV"; break; + case SIGFPE: sig = "SIGFPE"; break; +#ifndef __VISUALC__ + case SIGBUS: sig = "SIGBUS"; break; +#endif + default: sig = "SIG?"; break; + } + + cerr << "Aborted " << code << " (" << sig << " )" << endl; + //fprintf(logfile, "Aborted (%s)\n", sig); + + exit(-1); +} + + +/* + * A RasDaMan client application using wxWindows. This can be used as base class + * to other RasDaMan apps as well. + */ + +rmanClientApp *rmanClientApp::theclientapp = NULL; + +const char *rmanClientApp::vffFileName = "vff-file"; + +rmanClientApp::rmanClientApp(const char *homevar, const char *prefsname, const char *labelname) +{ + char *rvh; + char *b; +#ifdef __VISUALC__ + char *d; +#endif + char labels[STRINGSIZE]; + + theclientapp = this; + + //logfile = fopen("rview.log", "w"); + + rvh = getenv(homevar); + + // labels.txt is looked for in $RVIEWHOME or failing that . + if (rvh == NULL) + sprintf(homeDir, "."); + else + sprintf(homeDir, "%s", rvh); + + sprintf(labels, "%s"DIR_SEPARATOR"%s", homeDir, labelname); + + lman = new labelManager(labels); + + frameManager = new rviewFrameMgr(); + + rviewInitCharacterTables(); + + strcpy(prefsFileLeafname, prefsname); + + // Preferences are looked for in $HOME, ., $RVIEWHOME +#ifdef __VISUALC__ + if (((b = getenv("HOMEDRIVE")) != NULL) && ((d = getenv("HOMEPATH")) != NULL)) + sprintf(prefsFile, "%s%s", b, d); +#else + if ((b = getenv("HOME")) != NULL) + sprintf(prefsFile, "%s", b); +#endif + else + strcpy(prefsFile, "."); + + // always save prefs to $HOME/.rviewrc + sprintf(prefsSaveFile, "%s"DIR_SEPARATOR"%s", prefsFile, prefsname); + + if (!findPreferencesOnPath(prefsFile)) + { + strcpy(prefsFile, "."); + if (!findPreferencesOnPath(prefsFile) && (rvh != NULL)) + { + strcpy(prefsFile, homeDir); + findPreferencesOnPath(prefsFile); + } + } + + if (prefsFile[0] == 0) + { + cerr << "Couldn't find any preferences files." << endl; + cout << "New prefs will be written to " << prefsSaveFile << endl; + } + + prefs = new rviewPrefs(prefsFile); + + // Install signal handlers (open databases are ANNOYING!) + signal(SIGILL, (void(*)(int))rmanAbort); + signal(SIGINT, (void(*)(int))rmanAbort); +#if !defined(RMANDEBUG) + signal(SIGABRT, (void(*)(int))rmanAbort); + signal(SIGSEGV, (void(*)(int))rmanAbort); +#endif + signal(SIGFPE, (void(*)(int))rmanAbort); +#ifndef __VISUALC__ + signal(SIGBUS, (void(*)(int))rmanAbort); +#endif +} + + +rmanClientApp::~rmanClientApp(void) +{ + delete lman; lman = NULL; + delete frameManager; frameManager = NULL; + theclientapp = NULL; + rviewIO::terminate(); + //prefs->save(prefsSaveFile); + //fclose(logfile); +} + + +bool rmanClientApp::findPreferencesOnPath(char *path) +{ + sprintf(path + strlen(path), DIR_SEPARATOR"%s", prefsFileLeafname); + if (::wxFileExists(path)) return TRUE; + path[0] = 0; return FALSE; +} + + +int rmanClientApp::SavePreferences(void) const +{ + return prefs->save(prefsSaveFile); +} + + +rmanClientApp *rmanClientApp::theApp(void) +{ + return theclientapp; +} + + +bool rmanClientApp::ReadDBState(void) +{ + return database.isOpen(); +} + + + + +/* + * Called when something crashes. + */ +void rmanClientApp::Shutdown(void) +{ + if (database.isOpen()) + { + database.close(); + } +} + + + + +int rmanClientApp::OpenServer(const char *srvname, int srvport, const char *dbname, + const char *usrname, const char *usrpassword) +{ + if (database.isOpen()) + return -1; + + RMDBGONCE(3, RMDebug::module_applications, "rviewApp", "OpenServer( " << srvname << ", " << dbname << "," << usrname << "," << usrpassword << " )" ); + + if (database.open(srvname, srvport, dbname, usrname, usrpassword) != 0) + { + user_event ue; + + ue.type = usr_db_opened; + if (frameManager != NULL) + frameManager->broadcastUserEvent(ue); + + return 0; + } + return -1; +} + + +int rmanClientApp::CloseServer(void) +{ + if (!database.isOpen()) + return -1; + + user_event ue; + + database.close(); + ue.type = usr_db_closed; + + if (frameManager != NULL) + frameManager->broadcastUserEvent(ue); + + RMDBGONCE(3, RMDebug::module_applications, "rviewApp", "CloseServer() Database closed." ); + + return 0; +} + + + +rviewFrame *rmanClientApp::OpenFile(unsigned int flags, r_Ref<r_GMarray> *newMddObj, bool resultwin) +{ + char *s; + char *prefPath = (char*)(prefs->filePath.ptr()); + + s = wxFileSelector(lman->lookup("loadFile"), (::wxDirExists(prefPath)) ? prefPath : NULL , NULL, NULL, "*"); + + if (s) + { + int status; + r_Ref<r_GMarray> mddPtr; + + RMDBGONCE(3, RMDebug::module_applications, "rviewApp", "OpenFile(...) " << s ); + + prefs->filePath = ::wxPathOnly(s); + prefs->markModified(); + + // load TIFF + if (rviewIO::isTIFF(s)) + { + status = rviewIO::loadTIFF(s, mddPtr, prefs->vffParams.ptr()); + if (status == RVIEW_IO_OK) + { + rviewDisplay *newImage; + mdd_frame mddObj; + // receiver makes a copy. + mddObj.mdd = mddPtr; mddObj.flags = 0; + newImage = new rviewFlatImage(&mddObj, flags | rviewDisplay::display_flag_standalone); + if (newMddObj != NULL) *newMddObj = mddPtr; + if (newImage->openViewer() != 0) + { + delete newImage; newImage = NULL; + } + return newImage; + } + } + // load VFF -- whether this is supported is sorted out in rviewIO.cpp + else if (rviewIO::isVFF(s)) + { + status = rviewIO::loadVFF(s, mddPtr, prefs->vffParams.ptr()); + if (status == RVIEW_IO_OK) + { + if (resultwin) + { + // load in results window + rviewResult *result; + const char *file = ::wxFileNameFromPath(s); + + collection_desc *cdesc = new collection_desc; + cdesc->collName = new char[strlen(file)+1]; + strcpy(cdesc->collName, file); + cdesc->collType = new char[strlen(mddPtr->get_type_name())+1]; + strcpy(cdesc->collType, mddPtr->get_type_name()); + cdesc->collInfo = new char[strlen(vffFileName)+1]; + strcpy(cdesc->collInfo, vffFileName); + cdesc->number = 1; + cdesc->mddObjs = new mdd_frame[1]; + cdesc->strObjs = NULL; + cdesc->mddObjs[0].mdd = mddPtr; + cdesc->mddObjs[0].flags = 0; + + result = new rviewResult(cdesc); + return result; + } + else + { + // load in default viewer + rviewDisplay *newImage; + mdd_frame mddObj; + mddObj.mdd = mddPtr; mddObj.flags = 0; + newImage = new rviewVolumeImage(&mddObj, flags | rviewDisplay::display_flag_standalone); + if (newMddObj != NULL) *newMddObj = mddPtr; + if (newImage->openViewer() != 0) + { + delete newImage; newImage = NULL; + } + return newImage; + } + } + } + } + return (rviewFrame*)NULL; +} + + +int rmanClientApp::LookupCollection(void) +{ + collection_desc *desc; + char *name; + + name = ::wxGetTextFromUser(lman->lookup("promptEnterColl"), lman->lookup("titleCollLook"), (char*)(prefs->lastColl.ptr())); + + if (name == NULL) return 0; + + if ((desc = new collection_desc) == NULL) + { + cerr << lman->lookup("errorMemory") << endl; + return 0; + } + memset(desc, 0, sizeof(collection_desc)); + + if ((desc->collName = new char [strlen(name) + 1]) == NULL) + { + cerr << lman->lookup("errorMemory") << endl; + delete desc; + return 0; + } + + strcpy(desc->collName, name); + prefs->lastColl = name; + prefs->markModified(); + + RMDBGENTER(3, RMDebug::module_applications, "rviewApp", "LookupCollection() " << desc->collName ); + +#ifdef DUMMY_MDD_OBJECT + desc->number = 1; + r_Minterval iv(3); + iv << r_Sinterval(r_Range(0),r_Range(300)) << r_Sinterval(r_Range(200),r_Range(400)) << r_Sinterval(r_Range(10), r_Range(12)); + desc->mddObjs = new mdd_frame[1]; + desc->mddObjs[0].mdd = (r_Ref <r_GMarray>) new r_Marray<DUMMY_MDD_OBJECT>(iv); + desc->collType = new char[16]; strcpy(desc->collType, "r_UShort"); + r_Ref < r_Marray < DUMMY_MDD_OBJECT > > mddPtr = (r_Ref < r_Marray < DUMMY_MDD_OBJECT > >) desc->mddObjs[0].mdd; + mddPtr->set_type_by_name("UShortImage"); + r_Point point(3); + cout << mddPtr->spatial_domain() << endl; + for (point[0]=0; point[0]<=300; point[0]++) + for (point[1]=200; point[1]<=400; point[1]++) + for (point[2]=10; point[2]<=12; point[2]++) + (*mddPtr)[point] = ((DUMMY_MDD_OBJECT)(point[0] + point[1] + point[2])) << (8*(sizeof(DUMMY_MDD_OBJECT) - 1)); + + if (1) +#else + if (database.lookupCollection(desc) != 0) +#endif + { + + RMDBGEXIT(3, RMDebug::module_applications, "rviewApp", "LookupCollection() OK." ); + + /*for (int i=0; i<desc->number; i++) + { + cout << "Object #" << i << ": " << desc->mddArrays[i]->spatial_domain() << endl; + }*/ + + rviewResult *result = new rviewResult(desc); + + return 1; + } + else + { + rviewDeleteCollection(desc); + } + + RMDBGEXIT(3, RMDebug::module_applications, "rviewApp", "LookupCollection() FAILED." ); + return 0; +} + + +int rmanClientApp::LookupScaledCollection(void) +{ + rviewLookScaleDialog *lsd = new rviewLookScaleDialog(this, (char*)(prefs->lastScColl.ptr()), prefs->imgScale); + + return 0; +} + +int rmanClientApp::LookupScaledCollection(const char *name, double scale) +{ + if (name != NULL) + { + collection_desc *desc; + + RMDBGENTER(3, RMDebug::module_applications, "rviewApp", "LookupScaledCollection( " << name << " at scale " << scale << " )"); + + prefs->imgScale = scale; + + if ((desc = new collection_desc) == NULL) + { + cerr << lman->lookup("errorMemory") << endl; + return 0; + } + memset(desc, 0, sizeof(collection_desc)); + + if ((desc->collName = new char [strlen(name) + 1]) == NULL) + { + cerr << lman->lookup("errorMemory") << endl; + delete desc; + return 0; + } + + strcpy(desc->collName, name); + prefs->lastScColl = name; + prefs->markModified(); + r_Fast_Base_Scale *scaler; + + if ((scaler = database.lookupScaledObject(desc, scale)) != NULL) + { + // don't call in standalone mode, viewer sorts it out itself. + rviewScaledImage *image = new rviewScaledImage(desc, scaler); + if (image->openViewer() != 0) + { + image->Close(TRUE); + RMDBGEXIT(3, RMDebug::module_applications, "rviewApp", "LookupScalledCollection() FAILED" ); + return 0; + } + RMDBGEXIT(3, RMDebug::module_applications, "rviewApp", "LookupScalledCollection() OK" ); + return 1; + } + else + { + rviewDeleteCollection(desc); + } + } + + RMDBGEXIT(3, RMDebug::module_applications, "rviewApp", "LookupScalledCollection() FAILED" ); + return 0; +} + + +int rmanClientApp::LookupOrthosection(void) +{ + char *name; + + name = ::wxGetTextFromUser(lman->lookup("promptEnterOrtho"), lman->lookup("titleOrthoLook"), (char*)(prefs->lastOrthoColl.ptr())); + + if ((name != NULL) && (strlen(name) != 0)) + { + char buffer[STRINGSIZE]; + const char *d = name; + char *b = buffer; + const double *lp = NULL; + double loid; + + // search the collection name for a trailing ,<local oid> + while ((*d != '\0') && (*d != ',')) + { + *b++ = *d++; + } + while ((b > buffer) && (isspace((unsigned int)(b[-1])))) b--; + *b++ = '\0'; + if (*d == ',') + { + loid = atof(d+1); + lp = &loid; + } + + return LookupOrthosection(buffer, lp); + } + return -1; +} + + +int rmanClientApp::LookupOrthosection(const char *collname, const double *loid) +{ + rviewOSectionPartImage *newImage = rviewOSectionPartImage::createViewer(collname, loid); + + if (newImage == NULL) + { + rviewErrorbox::reportError(lman->lookup("errorOrthoViewer"), "rmanClientApp", "LookupOrthosection"); + return -1; + } + + prefs->lastOrthoColl = collname; + + return 0; +} + + +int rmanClientApp::CreateCollection(void) +{ + char *name; + + name = ::wxGetTextFromUser(lman->lookup("promptEnterColl"), lman->lookup("titleCollCrt"), (char*)(prefs->lastOrthoColl.ptr())); + + if (name != NULL) + { + char buffer[STRINGSIZE]; + + strncpy(buffer, name, STRINGSIZE); + return database.createCollection(buffer, rbt_none); + } + return 0; +} + + + +int rmanClientApp::DeleteCollection(void) +{ + char *name; + char buffer[STRINGSIZE]; + + name = ::wxGetTextFromUser(lman->lookup("promptEnterColl"), lman->lookup("titleCollDel"), (char*)(prefs->lastColl.ptr())); + + if (name != NULL) + { + strncpy(buffer, name, STRINGSIZE); + return database.deleteCollection(buffer); + } + return 0; +} + + + +int rmanClientApp::insertMDD(r_Ref<r_GMarray> srcMdd, char *collName, r_Minterval *domain) +{ + char useName[STRINGSIZE]; + char *title; + int status; + + if (domain == NULL) + title = lman->lookup("titleInsertMdd"); + else + title = lman->lookup("titleInsertMddPro"); + + if (collName == NULL) + { + title = ::wxGetTextFromUser(lman->lookup("promptEnterColl"), title, (char*)(prefs->lastColl.ptr())); + if (title == NULL) return 0; + strncpy(useName, title, STRINGSIZE); + } + else + strncpy(useName, collName, STRINGSIZE); + + if ((status = database.insertObject(useName, srcMdd, domain)) != 0) + { + prefs->lastColl = useName; + prefs->markModified(); + } + return status; +} + + + +collection_desc *rmanClientApp::executeQuerySync(char *query, r_Ref<r_GMarray> *updateMdd, bool showProgress) +{ + collection_desc *desc; + + if ((desc = new collection_desc) == NULL) + { + cerr << lman->lookup("errorMemory") << endl; + return NULL; + } + + desc->number = 0; + if (database.executeQuery(desc, query, updateMdd, showProgress) == 0) + { + delete desc; + return NULL; + } + + // no result? + if (desc->number > 0) + { + char qcollName[] = "<query>"; + + desc->collName = new char[strlen(qcollName)+1]; + strcpy(desc->collName, qcollName); + } + else + { + delete desc; + return NULL; + } + + return desc; +} + + +int rmanClientApp::executeQuery(char *query, r_Ref<r_GMarray> *updateMdd) +{ + collection_desc *desc; + + if ((desc = executeQuerySync(query, updateMdd)) != NULL) + { + if (desc->mddObjs != NULL) + { + rviewResult *qresult = new rviewResult(desc); + } + else + { + rviewStringSet *qresult = new rviewStringSet(desc); + // rviewStringSet doesn't need the descriptor data after the + // constructor, so we can just delete it right away + rviewDeleteCollection(desc); + } + return 1; + } + return 0; +} + + +int rmanClientApp::getMinterval(r_Minterval &dom, const char *collname, const double *loid) +{ + return database.getMinterval(dom, collname, loid); +} diff --git a/applications/rview/rviewApp.hh b/applications/rview/rviewApp.hh new file mode 100644 index 0000000..c427cfa --- /dev/null +++ b/applications/rview/rviewApp.hh @@ -0,0 +1,112 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * RasDaMan + wxWindows application class used as base of rView etc. + * The main application includes the database object which has to be accessed + * through rmanClientApp-member functions. + * + * COMMENTS: + * none + */ + + + +#ifndef _RVIEW_APP_H_ +#define _RVIEW_APP_H_ + + +#ifdef __GNUG__ +#pragma interface +#endif + + + + + +// RasDaMan includes +#include "rasodmg/ref.hh" +#include "rasodmg/marray.hh" + + + +#include "rviewUtils.hh" +#include "rviewDb.hh" +#include "rviewDModes.hh" + + + + +/* + * A generic RasDaMan client app using wxWindows. + */ +class rmanClientApp : public wxApp +{ + public: + + rmanClientApp(const char *homevar, const char *prefsname, const char *labelname); + virtual ~rmanClientApp(void); + + int OpenServer(const char *srvname, int srvport, const char *dbname, + const char *usrname, const char *usrpassword); + int CloseServer(void); + bool ReadDBState(void); + int LookupCollection(void); + int LookupScaledCollection(void); + int LookupScaledCollection(const char *name, double scale); + int LookupOrthosection(void); + int LookupOrthosection(const char *name, const double *loid); + int CreateCollection(void); + int DeleteCollection(void); + rviewFrame *OpenFile(unsigned int flags=0, r_Ref<r_GMarray> *newMddObj=NULL, bool resultwin=FALSE); + virtual void Shutdown(void); + int insertMDD(r_Ref<r_GMarray> srcMdd, char *collName=NULL, r_Minterval *domain=NULL); + int executeQuery(char *query, r_Ref<r_GMarray> *updateMdd=NULL); + collection_desc *executeQuerySync(char *query, r_Ref<r_GMarray> *updateMdd=NULL, bool showProgress=TRUE); + int getMinterval(r_Minterval &dom, const char *collname, const double *loid=NULL); + + int SavePreferences(void) const; + bool findPreferencesOnPath(char *path); + + // use this call to contact the application instance + static rmanClientApp *theApp(void); + + + protected: + + rviewDatabase database; + char prefsFile[STRINGSIZE]; + char prefsSaveFile[STRINGSIZE]; + char prefsFileLeafname[STRINGSIZE]; + char homeDir[STRINGSIZE]; + + + private: + + static rmanClientApp *theclientapp; + static const char *vffFileName; +}; + +#endif diff --git a/applications/rview/rviewChart.cpp b/applications/rview/rviewChart.cpp new file mode 100644 index 0000000..3fddcef --- /dev/null +++ b/applications/rview/rviewChart.cpp @@ -0,0 +1,1024 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * SOURCE: rviewChart.cpp + * + * MODULE: applications/rview + * + * PURPOSE: + * rView chart viewer. Can display MDD objects of any dimension in a time + * series fashion. Possible modes are bar charts, line and spline function + * plots. + * + * COMMENTS: + * No comments + */ + + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + + +#include <string.h> +#include <iostream.h> +#include <math.h> +#include <limits.h> + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + +#include "raslib/rmdebug.hh" + +#include "rviewTypes.hh" + +#include "labelManager.hh" + +#include "rviewUtils.hh" +#include "rviewDModes.hh" +#include "rviewPrefs.hh" +#include "rviewMDD.hh" + + + +const int chartCanvas::chcanv_cospace = 16; +const int chartCanvas::chcanv_colength = 6; +const int chartCanvas::chcanv_exponents = 4; + +const int rviewChart::chart_twidth = 64; +const int rviewChart::chart_theight = 50; +const int rviewChart::chart_cwidth = 100; +const int rviewChart::chart_cheight = 50; +const int rviewChart::chart_minwidth = 100; +const int rviewChart::chart_minheight = 100; +const int rviewChart::chart_ctrly = 64; +const int rviewChart::chart_totaly = rviewDisplay::display_cheight + rviewChart::chart_ctrly; + + + +chartCanvas::chartCanvas(wxWindow *parent, int x, int y, int w, int h, long style) : wxCanvas(parent, x, y, w, h, style) +{ + brush.SetStyle(wxSOLID); + brush.SetColour((char)0x80, (char)0x80, (char)0x80); + back.SetStyle(wxSOLID); + back.SetColour((char)0xe0, (char)0xe0, (char)0xe0); + pen.SetStyle(wxSOLID); + pen.SetColour(0,0,0); + SetBackground(&back); + font = new wxFont(12, wxROMAN, wxNORMAL, wxNORMAL); + scroll = 0; +} + + +chartCanvas::~chartCanvas(void) +{ + SetBackground(NULL); + delete font; +} + + + +void chartCanvas::setData(mdd_frame *mf, rviewBaseType bt) +{ + mddObj = mf->mdd; + dimMDD = (int)(mddObj->spatial_domain().dimension()); + baseType = bt; + if (baseType == rbt_rgb) + { + brush_r.SetStyle(wxSOLID); + brush_r.SetColour((char)0xff, 0, 0); + brush_g.SetStyle(wxSOLID); + brush_g.SetColour(0, (char)0xff, 0); + brush_b.SetStyle(wxSOLID); + brush_b.SetColour(0, 0, (char)0xff); + pen_r.SetColour((char)0xff, 0, 0); + pen_g.SetColour(0, (char)0xff, 0); + pen_b.SetColour(0, 0, (char)0xff); + } +} + + + +int chartCanvas::setProjection(r_Point &p1, r_Point &p2) +{ + int i, j; + char buffer[STRINGSIZE]; + float twidth, theight, taux; + r_Minterval useInterv; + + pt1 = p1; pt2 = p2; min = 0.0; max = 1.0; + + useInterv= r_Minterval(dimMDD); + for (i=0; i<dimMDD; i++) + { + useInterv << r_Sinterval(pt1[i], pt2[i]); + } + if (mdd_objectRange(mddObj, useInterv, min, max) == 0) + { + cerr << lman->lookup("errorBaseType") << endl; + return 0; + } + + RMDBGONCE(3, RMDebug::module_applications, "chartCanvas", "setProjection(...) min = " << min << ", max = " << max ); + + // Determine format to use for numbering the vertical axis + taux = fabs(min); + if (taux <= pow(10.,(double)-chcanv_exponents)) i = chcanv_exponents; + else i = (int)(log(taux)/log(10.)); + taux = fabs(max); + if (taux <= pow(10.,(double)-chcanv_exponents)) j = chcanv_exponents; + else j = (int)(log(taux)/log(10.)); + if (i < j) i = j; + if (abs(i) >= chcanv_exponents) + { + strcpy(format, "%g"); + } + else if (i >= 0) + { + strcpy(format, "%.0f"); + } + else + { + sprintf(format, "%%.%df", -i); + } + + if (cosys) + { + SetFont(font); + sprintf(buffer, format, min); + GetTextExtent(buffer, &twidth, &theight); + sprintf(buffer, format, max); + GetTextExtent(buffer, &taux, &theight); + if (twidth < taux) twidth = taux; + coleft = (int)(twidth + chcanv_colength/2); + } + else + coleft = chcanv_cospace; + + return coleft; +} + + + +void chartCanvas::setVars(int s, double cs, int ds, bool cy, rviewChartMode cm) +{ + step = s; + costep = cs; + datastep = ds; + cosys = cy; + cmode = cm; +} + + + +// Recurring redraw body +#define _REDCHARTBAR(wd, pm) \ + value = (long)(scale * ((*mddPtr)[prun])pm); \ + if (value < 0) \ + { \ + top = orgy; bot = top - (float)value; \ + } \ + else \ + { \ + top = orgy - (float)value; bot = orgy; \ + } \ + if (top < 0) top = 0; if (bot > height) bot = height; \ + cdc->DrawRect(posx, chcanv_cospace + top, wd, bot - top); + +#define _REDCHARTBARLOOP(wd, pm) \ + for (prun[dim]=pt1[dim]+startOff; prun[dim] <= pt1[dim] + endOff; prun[dim]++, posx+=stepx) \ + { \ + _REDCHARTBAR(wd, pm); \ + } + +#define REDCHARTBAR(type) \ + r_Ref <r_Marray <type> > mddPtr = (r_Ref < r_Marray <type> >)(mddObj); \ + _REDCHARTBARLOOP(stepx, +0); + +void chartCanvas::redrawBar(wxDC *cdc, int height, int dim, int startOff, int endOff, float scale, float posx, float stepx, float orgy) +{ + float top, bot; + r_Point prun = pt1; + long value; + + switch (baseType) + { + case rbt_bool: + { + r_Ref <r_Marray <r_Boolean> > mddPtr = (r_Ref < r_Marray <r_Boolean> >)(mddObj); + for (prun[dim]=pt1[dim]+startOff; prun[dim] <= pt1[dim]+endOff; prun[dim]++, posx+= stepx) + { + // This can only be 0 or 1, so don't bother with the sign + value = (long)(scale * (*mddPtr)[prun]); + cdc->DrawRectangle(posx, chcanv_cospace + orgy - (float)value, stepx, (float)value); + } + } + break; + case rbt_char: + { + REDCHARTBAR(r_Char); + } + break; + case rbt_uchar: + { + REDCHARTBAR(r_Octet); + } + break; + case rbt_short: + { + REDCHARTBAR(r_Short); + } + break; + case rbt_ushort: + { + REDCHARTBAR(r_UShort); + } + break; + case rbt_long: + { + REDCHARTBAR(r_Long); + } + break; + case rbt_ulong: + { + REDCHARTBAR(r_ULong); + } + break; + case rbt_rgb: + { + r_Ref <r_Marray <RGBPixel> > mddPtr = (r_Ref < r_Marray <RGBPixel> >)(mddObj); + double oldPosx = posx; + + cdc->SetBrush(&brush_r); + _REDCHARTBARLOOP(stepx/3, .red); + cdc->SetBrush(&brush_g); posx = oldPosx + stepx/3; + _REDCHARTBARLOOP(stepx/3, .green); + cdc->SetBrush(&brush_b); posx = oldPosx + 2*stepx/3; + _REDCHARTBARLOOP(stepx/3, .blue); + cdc->SetBrush(&brush); + } + break; + case rbt_float: + { + REDCHARTBAR(r_Float); + } + break; + case rbt_double: + { + REDCHARTBAR(r_Double); + } + break; + default: + break; + } +} + + + +#define _REDCHARTLINELOOP(pm) \ + prun[dim] = pt1[dim] + startOff; \ + lastVal = chcanv_cospace + orgy - scale * (((*mddPtr)[prun])pm); \ + for (prun[dim]++ ; prun[dim]<=pt1[dim]+endOff; prun[dim]++, posx += stepx) \ + { \ + newVal = chcanv_cospace + orgy - scale * (((*mddPtr)[prun])pm); \ + cdc->DrawLine(posx, lastVal, posx+stepx, newVal); \ + lastVal = newVal; \ + } + +#define REDCHARTLINE(type) \ + r_Ref<r_Marray<type> > mddPtr = (r_Ref<r_Marray<type> >)(mddObj); \ + _REDCHARTLINELOOP(+0); + +void chartCanvas::redrawLine(wxDC *cdc, int dim, int startOff, int endOff, float scale, float posx, float stepx, float orgy) +{ + r_Point prun = pt1; + float newVal, lastVal; + + if (pt1[dim] + endOff < pt2[dim]) endOff++; + + switch (baseType) + { + case rbt_bool: + { + REDCHARTLINE(r_Boolean); + } + break; + case rbt_char: + { + REDCHARTLINE(r_Char); + } + break; + case rbt_uchar: + { + REDCHARTLINE(r_Octet); + } + break; + case rbt_short: + { + REDCHARTLINE(r_Short); + } + break; + case rbt_ushort: + { + REDCHARTLINE(r_UShort); + } + break; + case rbt_long: + { + REDCHARTLINE(r_Long); + } + break; + case rbt_ulong: + { + REDCHARTLINE(r_ULong); + } + break; + case rbt_rgb: + { + r_Ref<r_Marray<RGBPixel> > mddPtr = (r_Ref<r_Marray<RGBPixel> >)(mddObj); + double oldPosx = posx; + + cdc->SetPen(&pen_r); + _REDCHARTLINELOOP(.red); + cdc->SetPen(&pen_g); posx = oldPosx + stepx/3; + _REDCHARTLINELOOP(.green); + cdc->SetPen(&pen_b); posx = oldPosx + 2*stepx/3; + _REDCHARTLINELOOP(.blue); + cdc->SetPen(&pen); + } + break; + case rbt_float: + { + REDCHARTLINE(r_Float); + } + break; + case rbt_double: + { + REDCHARTLINE(r_Double); + } + break; + default: break; + } +} + + + +#define _REDCHARTSPLINELOOP(pm) \ + for (prun[dim]=pt1[dim]+startOff; prun[dim]<=pt1[dim]+endOff; prun[dim]++, posx+=stepx, i++) \ + { \ + vertices[i].x = posx; \ + vertices[i].y = chcanv_cospace + orgy - scale * (((*mddPtr)[prun])pm); \ + RMDBGONCE(4, RMDebug::module_applications, "chartCanvas", "_REDCHARTSPLINELOOP(pm) V: " << vertices[i].x << ',' << vertices[i].y ); \ + vlist.Append(vertices + i); \ + } + +#define REDCHARTSPLINE(type) \ + r_Ref<r_Marray<type> > mddPtr = (r_Ref<r_Marray<type> >)(mddObj); \ + _REDCHARTSPLINELOOP(+0); + +void chartCanvas::redrawSpline(wxDC *cdc, int dim, int startOff, int endOff, float scale, float posx, float stepx, float orgy) +{ + wxPoint *vertices; + wxList vlist; + wxPoint point; + r_Point prun = pt1; + float clipx, clipwidth; + int i; + + clipx = posx; clipwidth = (endOff - startOff) * stepx; + + // Need additional vertices left (1) and right (2) for splines + if (startOff > 0) {startOff--; posx -= stepx;} + endOff += 2; if (pt1[dim] + endOff > pt2[dim]) endOff = pt2[dim] - pt1[dim]; + + if ((vertices = new wxPoint[endOff - startOff + 1]) == NULL) + { + cerr << lman->lookup("errorMemory") << endl; + return; + } + + // Do not delete the contents of the nodes when the list is killed! + vlist.DeleteContents(FALSE); + + // It is _vitally_ important to set the clipping region here, because the boundary vertices + // are only there to get curvature right. If you don't set the clipping region you get + // redraw errors. + cdc->SetClippingRegion(clipx, 0, clipwidth, 10000); + + i = 0; + switch (baseType) + { + case rbt_bool: + { + REDCHARTSPLINE(r_Boolean); + } + break; + case rbt_char: + { + REDCHARTSPLINE(r_Char); + } + break; + case rbt_uchar: + { + REDCHARTSPLINE(r_Octet); + } + break; + case rbt_short: + { + REDCHARTSPLINE(r_Short); + } + break; + case rbt_ushort: + { + REDCHARTSPLINE(r_UShort); + } + break; + case rbt_long: + { + REDCHARTSPLINE(r_Long); + } + break; + case rbt_ulong: + { + REDCHARTSPLINE(r_ULong); + } + break; + case rbt_rgb: + { + r_Ref<r_Marray<RGBPixel> > mddPtr = (r_Ref<r_Marray<RGBPixel> >) (mddObj); + double oldPosx = posx; + cdc->SetPen(&pen_r); + _REDCHARTSPLINELOOP(.red); + cdc->DrawSpline(&vlist); vlist.Clear(); + cdc->SetPen(&pen_g); posx = oldPosx + stepx/3; i = 0; + _REDCHARTSPLINELOOP(.green); + cdc->DrawSpline(&vlist); vlist.Clear(); + cdc->SetPen(&pen_b); posx = oldPosx + 2*stepx/3; i = 0; + _REDCHARTSPLINELOOP(.blue); + } + break; + case rbt_float: + { + REDCHARTSPLINE(r_Float); + } + break; + case rbt_double: + { + REDCHARTSPLINE(r_Double); + } + break; + default: break; + } + RMDBGONCE(3, RMDebug::module_applications, "chartCanvas", "redrawSpline() vertices " << (void*)vertices ); + + cdc->DrawSpline(&vlist); + + if (baseType == rbt_rgb) + cdc->SetPen(&pen); + + cdc->DestroyClippingRegion(); + + // ... we delete the data ourselves. + delete [] vertices; +} + + + +void chartCanvas::OnPaint(void) +{ + wxUpdateIterator upd(this); + wxRect rect; + int w, h, dim, i, x; + float scale, orgy, posx, stepx, cm, y; + float twidth, theight; + wxCanvasDC *cdc; + r_Range startOff, endOff; + char buffer[STRINGSIZE]; + bool redrawAll = FALSE; + + //cout << "chartCanvas::OnPaint()" << endl; + for (dim=0; dim<dimMDD; dim++) if (pt1[dim] != pt2[dim]) break; + if (dim >= dimMDD) return; + + GetClientSize(&w, &h); + // Reserve space for co system + h -= 2*chcanv_cospace; + + if (fabs(max-min) < 10*DBL_MIN) + { + scale = (float)h; orgy = h; + } + else + { + scale = ((float)h) / (max - min); orgy = max*scale; + } + if (scale <= 0) return; + + cdc = GetDC(); + cdc->BeginDrawing(); + cdc->SetMapMode(MM_TEXT); // 1 unit = 1 pixel + cdc->SetBrush(&brush); + cdc->SetPen(&pen); + cdc->SetFont(font); + + w = GetScrollPos(wxHORIZONTAL); + x = rviewDisplay::display_scrstep * w; + + // On the necessity to redraw everything in case of scroll events see rviewTable.cpp + if (w != scroll) + { + redrawAll = TRUE; + GetClientSize(&rect.width, &rect.height); + rect.x = 0; rect.y = 0; + scroll = w; + } + + stepx = (float)step; + + // It's important to use for, not while here, due to continue in the loop body + for (; upd ; upd++) + { + if (!redrawAll) upd.GetRect(&rect); + + // Leave space to the left for co-system + startOff = (x + rect.x - coleft) / step; + if (startOff < 0) startOff = 0; + if (pt1[dim] + startOff > pt2[dim]) + continue; + endOff = (x + rect.x - coleft + rect.width + step - 1) / step; + if (pt1[dim] + endOff > pt2[dim]) endOff = (r_Range)(pt2[dim] - pt1[dim]); + posx = startOff * stepx + coleft; + + switch (cmode) + { + case rcm_bar: redrawBar(cdc, h, dim, startOff, endOff, scale, posx, stepx, orgy); break; + case rcm_line: redrawLine(cdc, dim, startOff, endOff, scale, posx, stepx, orgy); break; + case rcm_spline: redrawSpline(cdc, dim, startOff, endOff, scale, posx, stepx, orgy); break; + default: break; + } + + // Draw coordinate system? + if (cosys) + { + // co-system + y = (min*max < 0) ? orgy : h; + // Only draw visible portion of horizontal line to avoid overflows + cdc->DrawLine(coleft + startOff * stepx, chcanv_cospace + y, coleft + (endOff+1)*stepx, chcanv_cospace + y); + + // vertical axis + if (coleft + chcanv_colength/2 >= rect.x) + { + cdc->DrawLine(coleft, chcanv_cospace, coleft, chcanv_cospace + h); + i = (int)(min / costep); + for (cm=i*costep; cm <= (float)max; cm += costep) + { + posx = orgy - cm*scale; + if (posx > h) continue; + posx += chcanv_cospace; + cdc->DrawLine(coleft - chcanv_colength/2, posx, coleft + chcanv_colength/2, posx); + sprintf(buffer, format, cm); + cdc->GetTextExtent(buffer, &twidth, &theight); + cdc->DrawText(buffer, coleft - chcanv_colength/2 - twidth, posx - theight/2); + } + } + + // horizontal axis + i = startOff/datastep; i *= datastep; + for (; i <= endOff; i += datastep) + { + posx = i*stepx + coleft; + cdc->DrawLine(posx, chcanv_cospace + y - chcanv_colength, posx, chcanv_cospace + y + chcanv_colength/2); + sprintf(buffer, "%ld", i + pt1[dim]); + cdc->GetTextExtent(buffer, &twidth, &theight); + cdc->DrawText(buffer, posx - twidth/2, y + chcanv_cospace + chcanv_colength/2); + } + } + } + cdc->SetBrush(NULL); + cdc->SetPen(NULL); + cdc->SetFont(NULL); + cdc->EndDrawing(); + + //cout << "Leaving OnPaint..." << endl; +} + + + + + +const char *rviewChart::view_StepSize = "stepSize"; +const char *rviewChart::view_Markers = "markers"; +const char *rviewChart::view_ScrollPos = "scrollPos"; +const char *rviewChart::view_CoSys = "coordSys"; +const char *rviewChart::view_ChartMode = "chartMode"; + +rviewChart::rviewChart(mdd_frame *mf, unsigned int flags) : rviewDisplay(mf, chart_ctrly, flags) +{ + int w, h, i; + char *b; + + RMDBGONCE(3, RMDebug::module_applications, "rviewChart", "rviewChart()"); + + // Chart mode defaults, put into prefs later + cmode = prefs->chartMode; + if ((cmode != rcm_bar) && (cmode != rcm_line) && (cmode != rcm_spline)) cmode = rcm_bar; + step = prefs->chartStep; cosys = prefs->chartCosys; + datastep = prefs->chartMarkx; costep = prefs->chartMarky; + + GetClientSize(&w, &h); + w -= 2*display_cnvborder; h -= 2*display_cnvborder + chart_totaly; + canvas = new chartCanvas((wxWindow*)this, display_cnvborder, display_cnvborder + chart_totaly, w, h); + + stText = new rviewText(ctrlPanel, step); + coText = new rviewText(ctrlPanel, costep); + dataText = new rviewText(ctrlPanel, datastep); + csBox = new rviewCheckBox(ctrlPanel); + csBox->SetValue(cosys); + + canvas->setData(mf, baseType); + + canvas->setVars(step, costep, datastep, cosys, cmode); + + b = projString; + b += sprintf(b, "*:*"); + for (i=1; i<dimMDD; i++) + { + b += sprintf(b, ", %ld", interv[i].low()); + } + project->SetValue(projString); + + scroll = -1; + newProjection(); + + setModeDimension(1); + + setMinimumViewerSize(chart_minwidth, chart_minheight); +} + + +int rviewChart::openViewer(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewChart", "openViewer()"); + + if (baseType == rbt_none) + { + rviewErrorbox::reportError(lman->lookup("errorModeBase"), rviewChart::getFrameName(), "openViewer"); + objectInitializedOK = FALSE; + return -1; + } + + if (rviewDisplay::openViewer() == 0) + { + int w, h; + wxMenu *modes; + char buffer[STRINGSIZE]; + + modes = new wxMenu; + modes->Append(MENU_CHART_MODE_BAR, "", NULL, TRUE); + modes->Append(MENU_CHART_MODE_LINE, "", NULL, TRUE); + modes->Append(MENU_CHART_MODE_SPLINE, "", NULL, TRUE); + + sprintf(buffer, "&%s", lman->lookup("menChartMode")); + mBar->Append(modes, buffer); + + checkModeMenu(); + + GetClientSize(&w, &h); + label(); + + frameWidth=-1; + frameHeight=-1; + + OnSize(w, h); + + Show(TRUE); + + return 0; + } + return -1; +} + + +const char *rviewChart::getFrameName(void) const +{ + return "rviewChart"; +} + +rviewFrameType rviewChart::getFrameType(void) const +{ + return rviewFrameTypeChart; +} + +int rviewChart::getViewerType(void) const +{ + return RVIEW_RESDISP_CHART; +} + + + +// We don't own the mdd object. rviewResult does, so don't delete it!!! +rviewChart::~rviewChart(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewChart", "~rviewChart()"); + closeViewer(); +} + + + +void rviewChart::checkModeMenu(void) +{ + // tick the right mode in the display window. + switch (cmode) + { + case rcm_bar: lastMode = MENU_CHART_MODE_BAR; break; + case rcm_line: lastMode = MENU_CHART_MODE_LINE; break; + case rcm_spline: lastMode = MENU_CHART_MODE_SPLINE; break; + default: lastMode = MENU_CHART_MODE_BAR; break; + } + mBar->Check(lastMode, TRUE); +} + + +void rviewChart::label(void) +{ + setDisplayTitle(lman->lookup("titleChart")); + + stText->SetLabel(lman->lookup("textStepC")); + csBox->SetLabel(lman->lookup("textCosys")); + coText->SetLabel(lman->lookup("textCoStep")); + dataText->SetLabel(lman->lookup("textDataStep")); + + mBar->SetLabel(MENU_CHART_MODE_BAR, lman->lookup("menChartModeBar")); + mBar->SetLabel(MENU_CHART_MODE_LINE, lman->lookup("menChartModeLine")); + mBar->SetLabel(MENU_CHART_MODE_SPLINE, lman->lookup("menChartModeSpline")); + mBar->SetLabelTop(fixedNumberOfMenus, lman->lookup("menChartMode")); + + rviewDisplay::label(); +} + + + +void rviewChart::OnSize(int w, int h) +{ + int x, y, i, j; + + RMDBGONCE(3, RMDebug::module_applications, "rviewChart", "OnSize(" << w << ", " << h << " )"); + + GetClientSize(&x, &y); + i = 2*display_border + chart_totaly + 2*chartCanvas::chcanv_cospace; + if (y < i) + { + y = i; + SetClientSize(x, i); + } + x -= 2*display_border; + i = x - 3*chart_twidth - chart_cwidth; + j = display_border + display_cheight; + stText->SetSize(display_border + i/8, j, chart_twidth - display_border, chart_theight); + coText->SetSize(display_border + (3*i)/8 + chart_twidth, j, chart_twidth - display_border, chart_theight); + dataText->SetSize(display_border + (5*i)/8 + 2*chart_twidth, j, chart_twidth - display_border, chart_theight); + csBox->SetSize(display_border + (7*i)/8 + 3*chart_twidth, j, chart_cwidth, chart_cheight); + + y -= 2*display_border + chart_totaly; + canvas->SetSize(display_border, display_border + chart_totaly, x, y); + rviewDisplay::OnSize(w, h); +} + + + +void rviewChart::OnMenuCommand(int id) +{ + rviewChartMode cm; + + RMDBGONCE(3, RMDebug::module_applications, "rviewChart", "OnMenuCommand()"); + + switch (id) + { + case MENU_CHART_MODE_BAR: cm = rcm_bar; break; + case MENU_CHART_MODE_LINE: cm = rcm_line; break; + case MENU_CHART_MODE_SPLINE: cm = rcm_spline; break; + default: cm = rcm_none; break; + } + + if (cm != rcm_none) + { + mBar->Check(lastMode, FALSE); + mBar->Check(id, TRUE); + lastMode = id; cmode = cm; + newProjection(); + } + else + rviewDisplay::OnMenuCommand(id); +} + + + +int rviewChart::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + int h; + double hd; + + RMDBGONCE(3, RMDebug::module_applications, "rviewChart", "process()"); + + if (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND) + { + if (&obj == (wxObject*)stText) + { + if ((h = atoi(stText->GetValue())) > 0) + { + step = h; + newProjection(); + return 1; + } + } + else if (&obj == (wxObject*)coText) + { + if ((hd = atof(coText->GetValue())) > 0.0) + { + costep = hd; + newProjection(); + return 1; + } + } + else if (&obj == (wxObject*)dataText) + { + if ((h = atoi(dataText->GetValue())) > 0) + { + datastep = h; + newProjection(); + return 1; + } + } + } + + if ((&obj == (wxObject*)csBox) && (type == wxEVENT_TYPE_CHECKBOX_COMMAND)) + { + cosys = csBox->GetValue(); + // to force a redraw. + newProjection(); + } + + if (rviewDisplay::process(obj, evt) != 0) + { + return 1; + } + + return 0; +} + + + +int rviewChart::newProjection(void) +{ + int dim, i; + + RMDBGONCE(3, RMDebug::module_applications, "rviewChart", "newProjection()"); + + if (rviewParseProjection(getVirtualDomain(), pt1, pt2, projString) != dimMDD) + { + rviewErrorbox::reportError(lman->lookup("errorProjection"), rviewChart::getFrameName(), "newProjection"); + return -1; + } + + dim = -1; + for (i=0; i<dimMDD; i++) + { + if (pt1[i] != pt2[i]) + { + if (dim < 0) dim = i; + else dim = dimMDD; + } + } + if ((dim < 0) || (dim >= dimMDD)) + { + rviewErrorbox::reportError(lman->lookup("errorProjectFree"), rviewChart::getFrameName(), "newProjection"); + return -1; + } + canvas->setVars(step, costep, datastep, cosys, cmode); + + i = canvas->setProjection(pt1, pt2); // returns coleft + + if (scroll >= 0) + scroll = canvas->GetScrollPos(wxHORIZONTAL); + else + scroll = 0; + + canvas->SetScrollbars(display_scrstep, 0, (int)(((pt2[dim] - pt1[dim] + 1)*step + i + display_scrstep - 1) / display_scrstep), 0, display_pgstep, 0, scroll); + //canvas->GetVirtualSize(&dim, &i); + //cout << "step = " << step << "virtual size = " << dim << " x " << i << endl; + + return 0; +} + + +int rviewChart::saveView(FILE *fp) +{ + int status = rviewDisplay::saveView(fp); + + writeViewParam(fp, view_StepSize, (long)step); + long mvals[2]; + mvals[0] = (long)costep; mvals[1] = (long)datastep; + writeViewParam(fp, view_Markers, 2, mvals); + writeViewParam(fp, view_ScrollPos, (long)(canvas->GetScrollPos(wxHORIZONTAL))); + writeViewParam(fp, view_CoSys, (long)cosys); + writeViewParam(fp, view_ChartMode, (long)cmode); + + return status; +} + + +int rviewChart::readView(const char *key, const char *value) +{ + int status = rviewDisplay::readView(key, value); + + if (status == 0) + { + if (strcmp(key, view_StepSize) == 0) + { + step = atoi(value); + return 1; + } + else if (strcmp(key, view_Markers) == 0) + { + long mvals[2]; + if (readVector(value, 2, mvals) == 0) + { + costep = (int)mvals[0]; datastep = (int)mvals[1]; + } + return 1; + } + else if (strcmp(key, view_ScrollPos) == 0) + { + scroll = atoi(value); + return 1; + } + else if (strcmp(key, view_CoSys) == 0) + { + cosys = (bool)atoi(value); + return 1; + } + else if (strcmp(key, view_ChartMode) == 0) + { + cmode = (rviewChartMode)atoi(value); + return 1; + } + return 0; + } + return status; +} + + +void rviewChart::loadViewFinished(void) +{ + stText->SetValue(step); + coText->SetValue(costep); + dataText->SetValue(datastep); + + csBox->SetValue(cosys); + + mBar->Check(lastMode, FALSE); + checkModeMenu(); + + canvas->SetScrollPos(wxHORIZONTAL, scroll); +} diff --git a/applications/rview/rviewColMap.cpp b/applications/rview/rviewColMap.cpp new file mode 100644 index 0000000..93d3654 --- /dev/null +++ b/applications/rview/rviewColMap.cpp @@ -0,0 +1,1183 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * Colourspace mapper class and support classes. + * COMMENTS: + * No comments + */ + + + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + + +#include <string.h> +#include <stdlib.h> +#include <iostream.h> +#include <math.h> +#include <limits.h> +#include <float.h> + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + + +#include "raslib/rmdebug.hh" + +#include "wx_pixmap_translate.h" + +#include "rviewUtils.hh" +#include "labelManager.hh" +#include "rviewColMap.hh" +#include "rviewPrefs.hh" +#include "rviewMDD.hh" + + + + + +/* + * Conversion functions shared by colourspaceCanvas and colourspaceMapper: + * convert a value normalized to [0,1] to a value normalized to [0,1] + */ + +static inline double valueToGauss(double value, double peak, double invSig) +{ + double h; + + h = (value - peak) * invSig; return exp(-h*h); +} + +static inline double valueToLinear(double value, double peak, double invSig) +{ + double h; + + if (value < peak) + h = 1.0 - invSig*(peak - value); + else + h = 1.0 - invSig*(value - peak); + + if (h < 0) h = 0; + + return h; +} + +static inline double valueToRectangle(double value, double peak, double invSig) +{ + double h; + + if (value < peak) + h = 1.0 - invSig*(peak - value); + else + h = 1.0 - invSig*(value - peak); + + if (h > 0) h = 1.0; else h = 0.0; + + return h; +} + +static inline double valueToAsymptotic(double value, double peak, double invSig) +{ + double h; + + if (value < peak) h = 0; + else + h = 1.0 - exp((peak - value) * invSig); + + return h; +} + + + +/* + * Colourspace mapper editor canvas class. + */ + +const int colourspaceCanvas::colcanv_cborder = 8; +const int colourspaceCanvas::colcanv_mheight = 8; + +colourspaceCanvas::colourspaceCanvas(colourspaceFrame *parent, colourspace_params *p, int x, int y, int w, int h, long style) : wxCanvas((wxWindow*)parent, x, y, w, h, style) +{ + params = p; + canvX = w; canvY = h; + parentObj = parent; + canvX=100; canvY=100; + values = NULL; + + setDrawingFunction(); + + brush.SetStyle(wxSOLID); brush.SetColour((char)0xe0, (char)0xe0, (char)0xe0); + redPen.SetStyle(wxSOLID); redPen.SetColour((char)0xff, 0, 0); + greenPen.SetStyle(wxSOLID); greenPen.SetColour(0, (char)0xff, 0); + bluePen.SetStyle(wxSOLID); bluePen.SetColour(0, 0, (char)0xff); + blackPen.SetStyle(wxSOLID); blackPen.SetColour(0, 0, 0); + + font = new wxFont(12, wxROMAN, wxNORMAL, wxNORMAL); + + SetBackground(&brush); + SetFont(font); +} + + +colourspaceCanvas::~colourspaceCanvas(void) +{ + SetFont(NULL); + SetBackground(NULL); + delete font; + if (values != NULL) delete [] values; +} + + +void colourspaceCanvas::setDrawingFunction(void) +{ + switch (params->type) + { + case cst_gauss: + conversionFunction = valueToGauss; break; + case cst_linear: + conversionFunction = valueToLinear; break; + case cst_rectangle: + conversionFunction = valueToRectangle; break; + case cst_asympt: + conversionFunction = valueToAsymptotic; break; + default: + cerr << "Unknown drawing function (" << (int)(params->type) << ")" << endl; + conversionFunction = NULL; + break; + } + //cout << "cspace type " << (int)(params->type) << endl; +} + + +void colourspaceCanvas::enableOutlineSum(bool enable) +{ + if (enable) + { + if (values == NULL) + { + values = new float[canvX]; Redraw(); + } + } + else + { + if (values != NULL) + { + delete [] values; values = NULL; Redraw(); + } + } +} + + +void colourspaceCanvas::OnSize(int w, int h) +{ + canvX = w; canvY = h; + height = (float)(canvY - 2*colcanv_cborder); + base = (float)(canvY - colcanv_cborder); + step = 1.0 / (canvX - 2*colcanv_cborder); + cmin = colcanv_cborder; cmax = canvX - colcanv_cborder; + if (values != NULL) + { + delete [] values; values = new float[canvX]; + } +} + + +void colourspaceCanvas::Redraw(void) +{ + Refresh(TRUE); +} + + +void colourspaceCanvas::OnPaint(void) +{ + wxRect rect; + int y = canvY - colcanv_cborder; + float x, xhigh, markstep; + + markstep = (canvX - 2*colcanv_cborder) / 10.0; + xhigh = (float)(canvX - colcanv_cborder) + 0.9; + + BeginDrawing(); + + wxUpdateIterator upd(this); + while (upd) + { + upd.GetRect(&rect); + + if (values != NULL) + memset(values, 0, canvX*sizeof(float)); + + if (conversionFunction != NULL) + { + drawOutline(params->peak_red, params->sigm_red, &redPen, &rect); + drawOutline(params->peak_green, params->sigm_green, &greenPen, &rect); + drawOutline(params->peak_blue, params->sigm_blue, &bluePen, &rect); + } + if (values != NULL) + drawOutlineSum(&blackPen, &rect); + + SetPen(&blackPen); + IntDrawLine(colcanv_cborder, y, canvX - colcanv_cborder, y); + + for (x=(float)colcanv_cborder; x<=xhigh; x += markstep) + { + IntDrawLine((int)x, y + colcanv_mheight/2, (int)x, y - colcanv_mheight/2); + } + upd++; + } + SetPen(NULL); + + EndDrawing(); +} + + +void colourspaceCanvas::OnEvent(wxMouseEvent &mevt) +{ + parentObj->processMouseEvent(mevt); +} + + +int colourspaceCanvas::setupRectangle(int &from, int &to, float &x, wxRect *rect) +{ + if (rect == NULL) + { + from = cmin; from = cmax; x = 0.0; + } + else + { + from = rect->x; to = from + rect->width + 1; + if (((from < cmin) && (to < cmin)) || ((from > cmax) && (to > cmax))) + return -1; + if (from < cmin) from = cmin; + if (to > cmax) to = cmax; + x = (from - cmin) * step; + } + return 0; +} + + +void colourspaceCanvas::drawOutline(double peak, double sigma, wxPen *pen, wxRect *rect) +{ + float mid = (canvX - 2*colcanv_cborder) * peak + colcanv_cborder; + float x, y, lastY; + float invSigma; + int i, j; + + SetPen(pen); + + DrawLine(mid, base, mid, 0.0); + + invSigma = 1.0 / sigma; + + if (setupRectangle(i, j, x, rect) != 0) return; + + lastY = height * conversionFunction(x, peak, invSigma); + if (values != NULL) + values[i] += lastY; + + for (i++, x += step; i < j; i++, x+=step) + { + y = height * conversionFunction(x, peak, invSigma); + DrawLine((float)i-1, base - lastY, (float)i, base - y); + lastY = y; + if (values != NULL) + values[i] += y; + } +} + + +void colourspaceCanvas::drawOutlineSum(wxPen *pen, wxRect *rect) +{ + int i, j; + float x, y, lastY; + + pen->SetStyle(wxSHORT_DASH); SetPen(pen); + DrawLine((float)colcanv_cborder, base - height/3, (float)(canvX - colcanv_cborder), base - height/3); + pen->SetStyle(wxSOLID); SetPen(pen); + + if (setupRectangle(i, j, x, rect) != 0) return; + + lastY = values[i] / 3; + for (i++, x += step; i < j; i++, x+= step) + { + y = values[i] / 3; + DrawLine((float)i-1, base - lastY, (float)i, base - y); + lastY = y; + } +} + + + + + + +/* + * Colourspace mapper editor frame class. + */ + +const int colourspaceFrame::colspc_border = 8; +const int colourspaceFrame::colspc_width = 450; +const int colourspaceFrame::colspc_height = 400; +const int colourspaceFrame::colspc_bwidth = 80; +const int colourspaceFrame::colspc_bheight = 30; +const int colourspaceFrame::colspc_twidth = 100; +const int colourspaceFrame::colspc_theight = 30; +const int colourspaceFrame::colspc_chkheight = 20; +const int colourspaceFrame::colspc_chwidth = colourspaceFrame::colspc_twidth - rview_choice_sub_width; +const int colourspaceFrame::colspc_chheight = 20; +const int colourspaceFrame::colspc_cheight = colourspaceFrame::colspc_bheight + 4*colourspaceFrame::colspc_theight + 5*colourspaceFrame::colspc_border; + + +colourspaceFrame::colourspaceFrame(colourspaceMapper *parent, const colourspace_params *p) : rviewFrame(NULL, lman->lookup("titleColourspace"), 0, 0, colspc_width, colspc_height) +{ + // defaults + doImmediateUpdate = TRUE; doDrawSum = TRUE; + + parentObj = parent; + memcpy(&newParams, p, sizeof(colourspace_params)); + memcpy(&origParams, p, sizeof(colourspace_params)); + canvX = 0; canvY = 0; + mousebut = 0; didUpdate = 0; + + canvas = new colourspaceCanvas(this, &newParams, 0, 0, 100, 100, wxBORDER); + panel = new wxPanel((wxWindow*)this, 0, 100, 100, 100); + + okBut = new rviewButton(panel); + cancelBut = new rviewButton(panel); + defaultBut = new rviewButton(panel); + + immediateUpdate = new rviewCheckBox(panel); + immediateUpdate->SetValue(doImmediateUpdate); + drawSum = new rviewCheckBox(panel); + drawSum->SetValue(doDrawSum); + + // the text widgets displaying the current setup + posR = new rviewText(panel); + posG = new rviewText(panel); + posB = new rviewText(panel); + sigR = new rviewText(panel); + sigG = new rviewText(panel); + sigB = new rviewText(panel); + minVal = new rviewText(panel); + maxVal = new rviewText(panel); + + const char *cstypes[4]; + cstypes[0] = lman->lookup("cspaceTypeGauss"); + cstypes[1] = lman->lookup("cspaceTypeLin"); + cstypes[2] = lman->lookup("cspaceTypeRect"); + cstypes[3] = lman->lookup("cspaceTypeAsympt"); + csType = new rviewChoice(panel, 4, cstypes); + csType->SetLabel(""); + + updateDisplay(); + + Show(TRUE); + + label(); + + frameWidth = -1; frameHeight = -1; + OnSize(colspc_width, colspc_height); + + canvas->enableOutlineSum(doDrawSum); +} + + +colourspaceFrame::~colourspaceFrame(void) +{ + if (parentObj != NULL) parentObj->closeEditor(FALSE); +} + + +const char *colourspaceFrame::getFrameName(void) const +{ + return "colourspaceFrame"; +} + +rviewFrameType colourspaceFrame::getFrameType(void) const +{ + return rviewFrameTypeCspace; +} + + +void colourspaceFrame::unlinkParent(void) +{ + parentObj = NULL; +} + + +void colourspaceFrame::setRange(double newMinVal, double newMaxVal) +{ + char buffer[STRINGSIZE]; + + newParams.minVal = newMinVal; newParams.maxVal = newMaxVal; + // Just update the text widgets. Rebuilding the pixmap is done automatically. + sprintf(buffer, "%f", newMinVal); + minVal->SetValue(buffer); + sprintf(buffer, "%f", newMaxVal); + maxVal->SetValue(buffer); +} + + +void colourspaceFrame::OnSize(int w, int h) +{ + int x, y, px, py; + + GetClientSize(&x, &y); + if ((x == frameWidth) && (y == frameHeight)) + return; + + frameWidth = x; + frameHeight = y; + y -= colspc_cheight; + canvX = x - 2*colspc_border; + canvY = y - 2*colspc_border; + canvas->SetSize(colspc_border, colspc_border, canvX, canvY); + panel->SetSize(0, y, x, colspc_cheight); + px = (x - 3*colspc_twidth) / 5; + if(px<0) px=0; + py = colspc_border; + posR->SetSize(px, py, colspc_twidth, colspc_theight); + posG->SetSize(2*px + colspc_twidth, py, colspc_twidth, colspc_theight); + posB->SetSize(3*px + 2*colspc_twidth, py, colspc_twidth, colspc_theight); + py += colspc_border + colspc_theight; + sigR->SetSize(px, py, colspc_twidth, colspc_theight); + sigG->SetSize(2*px + colspc_twidth, py, colspc_twidth, colspc_theight); + sigB->SetSize(3*px+ 2*colspc_twidth, py, colspc_twidth, colspc_theight); + py += colspc_border + colspc_theight; + px = (x - 3*colspc_twidth)/4; + immediateUpdate->SetSize(px, py, colspc_twidth, colspc_chkheight); + drawSum->SetSize(px, py + colspc_chkheight, colspc_twidth, colspc_chkheight); + minVal->SetSize(2*px + colspc_twidth, py, colspc_twidth, colspc_theight); + maxVal->SetSize(2*px + colspc_twidth, py + colspc_theight, colspc_twidth, colspc_theight); + csType->SetSize(3*px + 2*colspc_twidth, py, colspc_chwidth, colspc_chheight); + py += colspc_border + 2*colspc_theight; + px = (x - 3*colspc_bwidth)/3; + okBut->SetSize(px/2, py, colspc_bwidth, colspc_bheight); + cancelBut->SetSize((3*px)/2 + colspc_bwidth, py, colspc_bwidth, colspc_bheight); + defaultBut->SetSize((5*px)/2 + 2*colspc_bwidth, py, colspc_bwidth, colspc_bheight); +} + + +void colourspaceFrame::label(void) +{ + okBut->SetLabel(lman->lookup("textOK")); + cancelBut->SetLabel(lman->lookup("textCancel")); + defaultBut->SetLabel(lman->lookup("textDefaults")); + + immediateUpdate->SetLabel(lman->lookup("cspaceImmUpdt")); + drawSum->SetLabel(lman->lookup("cspaceDrawSum")); + + posR->SetLabel(lman->lookup("cspacePeakRed")); + posG->SetLabel(lman->lookup("cspacePeakGreen")); + posB->SetLabel(lman->lookup("cspacePeakBlue")); + sigR->SetLabel(lman->lookup("cspaceSigmaRed")); + sigG->SetLabel(lman->lookup("cspaceSigmaGreen")); + sigB->SetLabel(lman->lookup("cspaceSigmaBlue")); + minVal->SetLabel(lman->lookup("cspaceMinVal")); + maxVal->SetLabel(lman->lookup("cspaceMaxVal")); +} + + +void colourspaceFrame::makeUpdate(void) +{ + canvas->Redraw(); + if (doImmediateUpdate) + { + if (parentObj != NULL) parentObj->colourspaceChanged(&newParams); + didUpdate++; + } +} + + +int colourspaceFrame::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)okBut) + { + updateSettings(); + if (parentObj != NULL) parentObj->colourspaceChanged(&newParams); + Close(TRUE); + return 1; + } + else if (&obj == (wxObject*)cancelBut) + { + // return to old values, but we did updates? + if (didUpdate != 0) + { + if (parentObj != NULL) parentObj->colourspaceChanged(&origParams); + Close(TRUE); + return 1; + } + if (parentObj != NULL) parentObj->closeEditor(); + return 1; + } + else if (&obj == (wxObject*)defaultBut) + { + const colourspace_params *cp = &(prefs->csp); + newParams.peak_red = cp->peak_red; + newParams.peak_green = cp->peak_green; + newParams.peak_blue = cp->peak_blue; + newParams.sigm_red = cp->sigm_red; + newParams.sigm_green = cp->sigm_green; + newParams.sigm_blue = cp->sigm_blue; + newParams.type = cp->type; + updateDisplay(); + makeUpdate(); + return 1; + } + } + if (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND) + { + if ((&obj == (wxObject*)posR) || (&obj == (wxObject*)posG) || (&obj == (wxObject*)posB) || + (&obj == (wxObject*)sigR) || (&obj == (wxObject*)sigG) || (&obj == (wxObject*)sigB) || + (&obj == (wxObject*)minVal) || (&obj == (wxObject*)maxVal)) + { + updateSettings(); + makeUpdate(); + } + } + if (type == wxEVENT_TYPE_CHECKBOX_COMMAND) + { + if (&obj == (wxObject*)immediateUpdate) + { + doImmediateUpdate = immediateUpdate->GetValue(); + return 1; + } + if (&obj == (wxObject*)drawSum) + { + doDrawSum = drawSum->GetValue(); + canvas->enableOutlineSum(doDrawSum); + return 1; + } + } + if (type == wxEVENT_TYPE_CHOICE_COMMAND) + { + if (&obj == (wxObject*)csType) + { + updateSettings(); + canvas->setDrawingFunction(); + makeUpdate(); + return 1; + } + } + + return 0; +} + + +void colourspaceFrame::updateSettings(void) +{ + newParams.peak_red = atof(posR->GetValue()); + newParams.peak_green = atof(posG->GetValue()); + newParams.peak_blue = atof(posB->GetValue()); + newParams.sigm_red = atof(sigR->GetValue()); + newParams.sigm_green = atof(sigG->GetValue()); + newParams.sigm_blue = atof(sigB->GetValue()); + newParams.minVal = atof(minVal->GetValue()); + newParams.maxVal = atof(maxVal->GetValue()); + switch (csType->GetSelection()) + { + case 0: newParams.type = cst_gauss; break; + case 1: newParams.type = cst_linear; break; + case 2: newParams.type = cst_rectangle; break; + case 3: newParams.type = cst_asympt; break; + default: + cerr << "Unknown colourspace type" << endl; + break; + } +} + + +void colourspaceFrame::updateDisplay(const colourspace_params *cp) +{ + if (cp != NULL) + { + memcpy(&newParams, cp, sizeof(colourspace_params)); + memcpy(&origParams, cp, sizeof(colourspace_params)); + } + posR->SetValue(newParams.peak_red); + posG->SetValue(newParams.peak_green); + posB->SetValue(newParams.peak_blue); + sigR->SetValue(newParams.sigm_red); + sigG->SetValue(newParams.sigm_green); + sigB->SetValue(newParams.sigm_blue); + minVal->SetValue(newParams.minVal); + maxVal->SetValue(newParams.maxVal); + + switch (newParams.type) + { + case cst_linear: + csType->SetSelection(1); break; + case cst_rectangle: + csType->SetSelection(2); break; + case cst_asympt: + csType->SetSelection(3); break; + default: + csType->SetSelection(0); break; + } + canvas->setDrawingFunction(); + canvas->Redraw(); +} + + +void colourspaceFrame::processMouseEvent(wxMouseEvent &mevt) +{ + int type = mevt.GetEventType(); + + if ((type == wxEVENT_TYPE_LEFT_DOWN) || (type == wxEVENT_TYPE_RIGHT_DOWN)) + { + float delta; + + mevt.Position(&mousex, &mousey); dragColour = -1; + delta = mousex - newParams.peak_red * (canvX -2*colourspaceCanvas::colcanv_cborder) - colourspaceCanvas::colcanv_cborder; + if ((delta*delta) <= MOUSE_HOTZONE * MOUSE_HOTZONE) dragColour = 0; + else + { + delta = mousex - newParams.peak_green * (canvX - 2*colourspaceCanvas::colcanv_cborder) - colourspaceCanvas::colcanv_cborder; + if ((delta*delta) <= MOUSE_HOTZONE * MOUSE_HOTZONE) dragColour = 1; + else + { + delta = mousex - newParams.peak_blue * (canvX - 2*colourspaceCanvas::colcanv_cborder) - colourspaceCanvas::colcanv_cborder; + if ((delta*delta) <= MOUSE_HOTZONE * MOUSE_HOTZONE) dragColour = 2; + } + } + if (dragColour >= 0) + { + if (type == wxEVENT_TYPE_LEFT_DOWN) + mousebut = MOUSE_LEFT; + else + mousebut = MOUSE_RIGHT; + } + } + else if ((type == wxEVENT_TYPE_MOTION) && (mousebut != 0)) + { + float newx, newy; + double *newVal=NULL; + rviewText *newText; + + mevt.Position(&newx, &newy); + + // left button & left/right movement: move peak position + if ((mousebut & MOUSE_LEFT) != 0) + { + switch (dragColour) + { + case 0: newVal = &newParams.peak_red; newText = posR; break; + case 1: newVal = &newParams.peak_green; newText = posG; break; + case 2: newVal = &newParams.peak_blue; newText = posB; break; + default: return; + } + *newVal = (newx - colourspaceCanvas::colcanv_cborder) / (canvX - 2*colourspaceCanvas::colcanv_cborder); + if (*newVal < 0.0) *newVal = 0.0; + if (*newVal > 1.0) *newVal = 1.0; + } + // right button & up/down movement: change variance + else if ((mousebut & MOUSE_RIGHT) != 0) + { + switch (dragColour) + { + case 0: newVal = &newParams.sigm_red; newText = sigR; break; + case 1: newVal = &newParams.sigm_green; newText = sigG; break; + case 2: newVal = &newParams.sigm_blue; newText = sigB; break; + default: return; + } + *newVal -= 0.5 * (newy - mousey) / (canvX - 2*colourspaceCanvas::colcanv_cborder); + // Must not let sigma become too small (floating exceptions) + if (*newVal < 1e-6) *newVal = 1e-6; + } + + mousex = newx; mousey = newy; + + if (newVal != NULL) + { + newText->SetValue(*newVal); + canvas->Redraw(); + if (doImmediateUpdate) + { + if (parentObj != NULL) parentObj->colourspaceChanged(&newParams); + didUpdate++; + } + } + } + else + { + mousebut = 0; + } +} + + + + + + +// Threshold for lookup table size. +#define COLOURSPACE_TABLE_THRESHOLD 0x10000 + +/* + * Colourspace mapper class. For mapping large range integer values to RGB space + * baseType == rbt_none implies just the colourspace object without an associated MDD + */ +colourspaceMapper::colourspaceMapper(r_Ref<r_GMarray> &mdd, rviewBaseType bt, const colourspace_params *cp, bool fullrange, const r_Minterval *domain, unsigned long frange) +{ + didRange = FALSE; + mddObj = mdd; baseType = bt; + IntToRGBTab15 = NULL; IntToRGBTab24 = NULL; + csFrame = NULL; + if (baseType != rbt_none) + { + objInterv = mddObj->spatial_domain(); dimMDD = objInterv.dimension(); + lastInterv = objInterv; + } + // Defaults + if (cp == NULL) + { + memcpy(&par, &(prefs->csp), sizeof(colourspace_params)); + } + else + { + memcpy(&par, cp, sizeof(colourspace_params)); + } + par.minVal = 0.0; par.maxVal = 0.0; par.floatRange = frange; + rangeModeFull = fullrange; useInterv = domain; + scalingFactor = 1.0; // needed for FP types only + tableKind = getTableForType(baseType); + + tableType = (cspaceType)-1; // initialize to some illegal value + setMappingFunctions(); + + processRange((rangeModeFull) ? CSPACE_RANGE_FULL : CSPACE_RANGE_ACTUAL); + + //cout << "mustep " << mustep << ", sigma " << sigma << ", min " << par.minVal << ", max " << par.maxVal << endl; +} + + +int colourspaceMapper::getTableForType(rviewBaseType bt) +{ + int tkind; + + switch (bt) + { + case rbt_long: + case rbt_ulong: + case rbt_float: + case rbt_double: tkind = 1; break; + default: tkind = 0; break; + } + return tkind; +} + + +int colourspaceMapper::bindMapper(r_Ref<r_GMarray> &mdd, rviewBaseType bt, bool fullrange, const r_Minterval *domain, const colourspace_params *cp) +{ + bool cparChanged; + + if (cp == NULL) + cparChanged = 0; + else + cparChanged = + ((cp->peak_red != par.peak_red) || (cp->sigm_red != par.sigm_red) || + (cp->peak_green != par.peak_green) || (cp->sigm_green != par.sigm_green) || + (cp->peak_blue != par.peak_blue) || (cp->sigm_blue != par.sigm_blue) || + (cp->type != par.type)); + + if ((mddObj.ptr() == mdd.ptr()) && (bt == baseType) && (fullrange == rangeModeFull) && (domain == useInterv) && (cparChanged == 0)) + return 0; // nothing to do + + if (cparChanged) + { + colourspace_params cpar; + memcpy(&cpar, cp, sizeof(colourspace_params)); + cpar.minVal = par.minVal; cpar.maxVal = par.maxVal; cpar.floatRange = par.floatRange; + colourspaceChanged(&cpar); + if (csFrame != NULL) + { + csFrame->updateDisplay(&cpar); + } + } + + if ((mddObj.ptr() != mdd.ptr()) || (bt != baseType)) + { + mddObj = mdd; baseType = bt; useInterv = domain; scalingFactor = 1.0; + tableKind = getTableForType(baseType); + rangeModeFull = fullrange; didRange = FALSE; + processRange((rangeModeFull) ? CSPACE_RANGE_FULL : CSPACE_RANGE_ACTUAL); + return 1; + } + if (fullrange != rangeModeFull) + { + rangeModeFull = fullrange; + processRange((rangeModeFull) ? CSPACE_RANGE_FULL : CSPACE_RANGE_ACTUAL); + return 1; + } + if ((domain != useInterv) || ((domain != NULL) && (*domain != *useInterv)) || (cparChanged != 0)) + { + updateProjection(domain); + } + return 1; +} + + +colourspaceMapper::~colourspaceMapper(void) +{ + if (IntToRGBTab15 != NULL) delete [] IntToRGBTab15; + if (IntToRGBTab24 != NULL) delete [] IntToRGBTab24; + + if (csFrame != NULL) + { + csFrame->unlinkParent(); + csFrame->Close(TRUE); + } +} + + +void colourspaceMapper::setMappingFunctions(void) +{ + switch (par.type) + { + default: + cerr << "Unknown mapping function, default to gauss" << endl; + case cst_gauss: + convert15 = &colourspaceMapper::ValToGauss15; convert24 = &colourspaceMapper::ValToGauss24; + break; + case cst_linear: + convert15 = &colourspaceMapper::ValToLinear15; convert24 = &colourspaceMapper::ValToLinear24; + break; + case cst_rectangle: + convert15 = &colourspaceMapper::ValToRectangle15; convert24 = &colourspaceMapper::ValToRectangle24; + break; + case cst_asympt: + convert15 = &colourspaceMapper::ValToAsymptotic15; convert24 = &colourspaceMapper::ValToAsymptotic24; + break; + } +} + + +// Visual C compiler bug: with optimizations sigma is undefined when !fullrange +#ifdef __VISUALC__ +#pragma optimize ("", off) +#endif + +void colourspaceMapper::processRange(int rangeMode) +{ + double h; + + if (baseType == rbt_none) return; + + if (rangeMode != CSPACE_RANGE_OLD) + { + int i; + + lastInterv = (useInterv == NULL) ? objInterv : (*useInterv); + rangeModeFull = (rangeMode == CSPACE_RANGE_FULL) ? TRUE : FALSE; + + // The number of projected pixels is a measure whether a lookup table pays off. + projPixels = lastInterv[0].high() - lastInterv[0].low() + 1; + for (i=1; i<dimMDD; i++) + { + projPixels *= lastInterv[i].high() - lastInterv[i].low() + 1; + } + + // Use the full range of the basetype or the actual range of the object? + if (rangeModeFull) + { + switch (baseType) + { + case rbt_char: par.minVal = 0.0; par.maxVal = (double)UCHAR_MAX; break; + case rbt_uchar: par.minVal = (double)SCHAR_MIN; par.maxVal = (double)SCHAR_MAX; break; + case rbt_short: par.minVal = (double)SHRT_MIN; par.maxVal = (double)SHRT_MAX; break; + case rbt_ushort: par.minVal = 0.0; par.maxVal = (double)USHRT_MAX; break; + case rbt_long: par.minVal = (double)LONG_MIN; par.maxVal = (double)LONG_MAX; break; + case rbt_ulong: par.minVal = 0.0; par.maxVal = (double)ULONG_MAX; break; + // MIN/MAX or DBL and FLT are unsigned, i.e. MIN is the smallest number > 0! + case rbt_float: par.minVal = (double)(-FLT_MAX); par.maxVal = (double)FLT_MAX; break; + case rbt_double: par.minVal = (double)(-DBL_MAX); par.maxVal = (double)DBL_MAX; break; + default: break; + } + } + else + { + if (!didRange) + { + int state; + + ::wxBeginBusyCursor(); + state = mdd_objectRange(mddObj, lastInterv, realMinVal, realMaxVal); + ::wxEndBusyCursor(); + if (state == 0) + { + realMinVal = 0.0; realMaxVal = 1.0; + } + //cout << lastInterv << ": " << realMinVal << ", " << realMaxVal << endl; + didRange = TRUE; + } + par.minVal = realMinVal; par.maxVal = realMaxVal; + } + if (csFrame != NULL) + { + csFrame->setRange(par.minVal, par.maxVal); + } + } + + if ((baseType == rbt_float) || (baseType == rbt_double)) + { + // Avoid overflows; min/max could be the lowest/highest values, so the difference is NaN! + h = ((par.maxVal) / 256.0) - ((par.minVal) / 256.0); + if (h == 0.0) h = 1.0; + scalingFactor = ((par.floatRange - 1) / 256.0 ) / h; + } + + h = (par.maxVal - par.minVal); + peakR = h * par.peak_red; peakG = h * par.peak_green; peakB = h * par.peak_blue; + invSigR = 1.0 / (h * par.sigm_red); invSigG = 1.0 / (h * par.sigm_green); invSigB = 1.0 / (h * par.sigm_blue); + + buildCSTab15(TRUE); buildCSTab24(TRUE); +} + +#ifdef __VISUALC__ +#pragma optimize ("", on) +#endif + + +void colourspaceMapper::updateProjection(const r_Minterval *domain) +{ + if (!rangeModeFull) + { + if ((domain == NULL) && (useInterv == NULL)) return; + if (domain != NULL) + { + int i; + + for (i=0; i<dimMDD; i++) + { + if ((lastInterv[i].low() != (*domain)[i].low()) || + (lastInterv[i].high() != (*domain)[i].high())) + break; + } + // All equal ==> nothing to do + if (i >= dimMDD) return; + } + + didRange = FALSE; + useInterv = domain; + processRange(CSPACE_RANGE_ACTUAL); + } +} + + + +/* + * Shortcuts for defining convertor functions + */ +#define CSPACE_CONVERT(returntype, funcname, convname, rgbname) \ + returntype colourspaceMapper::funcname(double value) \ + { \ + unsigned int red, green, blue; \ + blue = (unsigned int)(255.99*convname(value, peakB, invSigB)); \ + green = (unsigned int)(255.99*convname(value, peakG, invSigG)); \ + red = (unsigned int)(255.99*convname(value, peakR, invSigR)); \ + return rgbname(red, green, blue); \ + } + +#define CSPACE_CONVERT15(funcname, convname) \ + CSPACE_CONVERT(unsigned short, funcname, convname, RGBL_TO_PALETTE_SHORT) +#define CSPACE_CONVERT24(funcname, convname) \ + CSPACE_CONVERT(unsigned long, funcname, convname, RGB_TO_PALETTE_LONG) + +CSPACE_CONVERT15(ValToGauss15, valueToGauss) +CSPACE_CONVERT24(ValToGauss24, valueToGauss) +CSPACE_CONVERT15(ValToLinear15, valueToLinear) +CSPACE_CONVERT24(ValToLinear24, valueToLinear) +CSPACE_CONVERT15(ValToRectangle15, valueToRectangle) +CSPACE_CONVERT24(ValToRectangle24, valueToRectangle) +CSPACE_CONVERT15(ValToAsymptotic15, valueToAsymptotic) +CSPACE_CONVERT24(ValToAsymptotic24, valueToAsymptotic) + + + + +unsigned short *colourspaceMapper::buildCSTab15(bool forceRebuild) +{ + int i, j; + double val; + + // Don't build a 15bpp table if it's not needed + if (tableKind != 0) return NULL; + + if (IntToRGBTab15 != NULL) + { + if ((!forceRebuild) && (tableType == par.type)) return IntToRGBTab15; + delete [] IntToRGBTab15; IntToRGBTab15 = NULL; + } + + // The code always assumes a table for this depth! + //if ((par.maxVal - par.minVal + 1) > (double)COLOURSPACE_TABLE_THRESHOLD) return NULL; + j = (int)((par.maxVal - par.minVal) * scalingFactor) + 1; + + IntToRGBTab15 = new unsigned short[j]; + for (i=0, val=0.0; i<j; i++, val += scalingFactor) + { + IntToRGBTab15[i] = ValToCS15(val); + } + tableType = par.type; + return IntToRGBTab15; +} + + + +unsigned long *colourspaceMapper::buildCSTab24(bool forceRebuild) +{ + int i, j; + double val; + double invScale; + + // Don't build a 32bpp table if it's not needed + if (tableKind == 0) return NULL; + + if (IntToRGBTab24 != NULL) + { + if ((!forceRebuild) && (tableType == par.type)) return IntToRGBTab24; + delete [] IntToRGBTab24; IntToRGBTab24 = NULL; + } + + j = (int)((par.maxVal - par.minVal) * scalingFactor) + 1; + if (j > COLOURSPACE_TABLE_THRESHOLD) return NULL; + + // If we use the range of the current projection and there are fewer visible pixels than + // table entries then don't build a table (or we'd sacrifice a lot of speed) + if ((!rangeModeFull) && (useInterv != NULL) && (j > projPixels)) return NULL; + + invScale = (scalingFactor == 0.0) ? 1.0 : 1/scalingFactor; + IntToRGBTab24 = new unsigned long[j]; + for (i=0, val=0.0; i<j; i++, val += invScale) + { + IntToRGBTab24[i] = ValToCS24(val); + } + tableType = par.type; + return IntToRGBTab24; +} + + + +double colourspaceMapper::getMinVal(void) +{ + return par.minVal; +} + + +double colourspaceMapper::getMaxVal(void) +{ + return par.maxVal; +} + + +double colourspaceMapper::getScalingFactor(void) +{ + return scalingFactor; +} + + +unsigned short *colourspaceMapper::getCSTab15(void) +{ + return IntToRGBTab15; +} + + +unsigned long *colourspaceMapper::getCSTab24(void) +{ + return IntToRGBTab24; +} + + +void colourspaceMapper::colourspaceChanged(const colourspace_params *newParams, bool autoUpdate) +{ + user_event ue; + + memcpy(&par, newParams, sizeof(colourspace_params)); + setMappingFunctions(); + processRange(CSPACE_RANGE_OLD); + + if (csFrame != NULL) + csFrame->updateDisplay(newParams); + + if (autoUpdate) + { + // Notify whoever owns this object that its settings have changed (can't have one + // specific type of parent because it can be owned by images, thumbnails, prefs, ...) + ue.type = usr_cspace_changed; + ue.data = (void*)this; + //parentFrame->userEvent(ue); + if (frameManager != NULL) + frameManager->broadcastUserEvent(ue); + } +} + + +void colourspaceMapper::openEditor(void) +{ + if (csFrame != NULL) return; + + csFrame = new colourspaceFrame(this, &par); +} + + +void colourspaceMapper::closeEditor(bool activeClose) +{ + if (activeClose) csFrame->Close(TRUE); + csFrame = NULL; +} + + +void colourspaceMapper::getParameters(colourspace_params *dest) +{ + if (csFrame != NULL) + { + csFrame->updateSettings(); + } + memcpy(dest, &par, sizeof(colourspace_params)); +} diff --git a/applications/rview/rviewColMap.hh b/applications/rview/rviewColMap.hh new file mode 100644 index 0000000..a217e23 --- /dev/null +++ b/applications/rview/rviewColMap.hh @@ -0,0 +1,254 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * Class definitions for the colourspace mapping. + * COMMENTS: + * No comments + */ + + +#ifndef _RVIEW_COLMAP_H_ +#define _RVIEW_COLMAP_H_ + + +#include "rviewUtils.hh" + + + +class colourspaceMapper; +class colourspaceFrame; + + +enum cspaceType { + cst_gauss, + cst_linear, + cst_rectangle, + cst_asympt +}; + +typedef struct colourspace_params_s { + double peak_red, peak_green, peak_blue; + double sigm_red, sigm_green, sigm_blue; + double minVal, maxVal; + unsigned long floatRange; + cspaceType type; +} colourspace_params; + + +/* + * Colourspace configuration canvas + */ +class colourspaceCanvas: public wxCanvas +{ + public: + + colourspaceCanvas(colourspaceFrame *parent, colourspace_params *p, int x, int y, int w, int h, long style=0); + ~colourspaceCanvas(void); + + void enableOutlineSum(bool enable); + void setDrawingFunction(void); + + void Redraw(void); + + void OnSize(int w, int h); + void OnPaint(void); + void OnEvent(wxMouseEvent &mevt); + + // constants + // Colourspace canvas border + static const int colcanv_cborder; + // Height of markers on colourspace canvas + static const int colcanv_mheight; + + + protected: + + int setupRectangle(int &from, int &to, float &x, wxRect *rect); + void drawOutline(double peak, double sigma, wxPen *pen, wxRect *rect); + void drawOutlineSum(wxPen *pen, wxRect *rect); + // Function pointer + double (*conversionFunction)(double, double, double); + + colourspaceFrame *parentObj; + colourspace_params *params; + int canvX, canvY; + wxBrush brush; + wxPen redPen, greenPen, bluePen, blackPen; + wxFont *font; + float *values; + float height, base, step; + int cmin, cmax; +}; + + +/* + * Colourspace configuration window + */ +class colourspaceFrame: public rviewFrame +{ + public: + + colourspaceFrame(colourspaceMapper *parent, const colourspace_params *p); + ~colourspaceFrame(void); + + void setRange(double newMinVal, double newMaxVal); + void unlinkParent(void); + + void OnSize(int w, int h); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + + void updateSettings(void); + void updateDisplay(const colourspace_params *cp=NULL); + void processMouseEvent(wxMouseEvent &mevt); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + // constants + // Colourspace editor borders + static const int colspc_border; + // Colourspace editor window + static const int colspc_width; + static const int colspc_height; + // Colourspace buttons + static const int colspc_bwidth; + static const int colspc_bheight; + // Colourspace text widgets + static const int colspc_twidth; + static const int colspc_theight; + // Colourspace checkbox widgets + static const int colspc_chkheight; + // Colourspace choice widgets + static const int colspc_chwidth; + static const int colspc_chheight; + // Colourspace control panel height + static const int colspc_cheight; + + + private: + + void makeUpdate(void); + + colourspaceMapper *parentObj; + colourspace_params newParams; + colourspace_params origParams; // in case of immediate update + + colourspaceCanvas *canvas; + wxPanel *panel; + rviewText *posR, *posG, *posB; + rviewText *sigR, *sigG, *sigB; + rviewButton *okBut, *cancelBut, *defaultBut; + rviewCheckBox *immediateUpdate; + rviewCheckBox *drawSum; + rviewText *minVal, *maxVal; + rviewChoice *csType; + bool doImmediateUpdate; + bool doDrawSum; + cspaceType cstype; + float mousex, mousey; + int mousebut; + int dragColour; + int canvX, canvY; + int didUpdate; +}; + + +// modes for calling processRange +#define CSPACE_RANGE_ACTUAL 0 +#define CSPACE_RANGE_FULL 1 +#define CSPACE_RANGE_OLD 2 + +/* + * Class for mapping large range values to RGB colourspace, + * implemented in rviewImage.cpp + */ +class colourspaceMapper +{ + public: + + colourspaceMapper(r_Ref<r_GMarray> &mdd, rviewBaseType bt, const colourspace_params *cp, bool fullrange=FALSE, const r_Minterval *domain=NULL, unsigned long frange=0x10000); + ~colourspaceMapper(void); + + void getObject(r_Ref<r_GMarray> &mdd, rviewBaseType &bt, bool *fullrange=NULL, r_Minterval **domain=NULL) const; + int bindMapper(r_Ref<r_GMarray> &mdd, rviewBaseType bt, bool fullrange=FALSE, const r_Minterval *domain=NULL, const colourspace_params *cp=NULL); + + inline unsigned short ValToCS15(double value) {return (this->*convert15)(value);} + inline unsigned long ValToCS24(double value) {return (this->*convert24)(value);} + unsigned short *buildCSTab15(bool forceRebuild=FALSE); + unsigned long *buildCSTab24(bool forceRebuild=FALSE); + double getMinVal(void); + double getMaxVal(void); + double getScalingFactor(void); + unsigned short *getCSTab15(void); + unsigned long *getCSTab24(void); + void processRange(int rangeMode); + void updateProjection(const r_Minterval *domain); + void colourspaceChanged(const colourspace_params *newParams, bool autoUpdate=TRUE); + void openEditor(void); + void closeEditor(bool activeClose=TRUE); + void getParameters(colourspace_params *dest); + void setMappingFunctions(void); + + + protected: + + static int getTableForType(rviewBaseType bt); + + unsigned short ValToGauss15(double value); + unsigned long ValToGauss24(double value); + unsigned short ValToLinear15(double value); + unsigned long ValToLinear24(double value); + unsigned short ValToRectangle15(double value); + unsigned long ValToRectangle24(double value); + unsigned short ValToAsymptotic15(double value); + unsigned long ValToAsymptotic24(double value); + // function pointers + unsigned short (colourspaceMapper::*convert15)(double value); + unsigned long (colourspaceMapper::*convert24)(double value); + + colourspace_params par; // all the important parameters + double realMinVal, realMaxVal; // actual range of object + bool didRange; + bool rangeModeFull; + double peakR, peakG, peakB; + double invSigR, invSigG, invSigB; + double scalingFactor; + int dimMDD; + int tableKind; // 15 or 24bpp tables? + unsigned short *IntToRGBTab15; + unsigned long *IntToRGBTab24; + cspaceType tableType; + colourspaceFrame *csFrame; + r_Ref<r_GMarray> mddObj; + rviewBaseType baseType; + long projPixels; + r_Minterval objInterv; + r_Minterval lastInterv; + const r_Minterval *useInterv; +}; + +#endif diff --git a/applications/rview/rviewDModes.hh b/applications/rview/rviewDModes.hh new file mode 100644 index 0000000..67c8807 --- /dev/null +++ b/applications/rview/rviewDModes.hh @@ -0,0 +1,1094 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * Class definitions for the regular object viewers and their support + * classes: + * - rviewImage hierarchy, including pixmapCanvas, rendererControl + * and rviewImageSetup. Source in rviewImage.cpp. + * - rviewChart (chartCanvas). Source in rviewChart.cpp. + * - rviewTable (tableCanvas). Source in rviewTable.cpp. + * COMMENTS: + * None + */ + + +#ifndef _RVIEW_DMODES_H_ +#define _RVIEW_DMODES_H_ + + + + +#include "rasodmg/ref.hh" +#include "rasodmg/marray.hh" +#include "rasodmg/gmarray.hh" + +#include "labelManager.hh" +#include "rviewUtils.hh" +#include "rviewDisplay.hh" +#include "rviewColMap.hh" + + + +#ifdef __VISUALC__ +struct vertex; +struct vertex_fp; +struct graph_env; +struct tex_desc; +struct voxel_desc; +struct mesh_desc; +struct mdd_desc; +#else +typedef struct vertex; +typedef struct vertex_fp; +typedef struct graph_env; +typedef struct tex_desc; +typedef struct voxel_desc; +typedef struct mesh_desc; +typedef struct mdd_desc; +#endif + + + + + +class rviewImage; +class rviewRenderImage; + + + +// Possible display formats +enum rviewImageMode { + rim_none, + rim_surf, + rim_voxel +}; + + +// Flags for light direction +#define RVIEW_LIGHTDIR_LEFT 1 +#define RVIEW_LIGHTDIR_RIGHT 2 +#define RVIEW_LIGHTDIR_DOWN 4 +#define RVIEW_LIGHTDIR_UP 8 +#define RVIEW_LIGHTDIR_FRONT 16 +#define RVIEW_LIGHTDIR_BACK 32 + +typedef struct rview_image_setup { + unsigned long zpro, clipz; + double pixelThresholdLow, pixelThresholdHigh, weightThreshold; + int weightQuantisation; + bool useRgbBrightness; + bool useLights; + double lightsAmbient; + double lightsGain; + double lightsAngle; + double lightsScintAngle; + int lightsDir; + int lightsDist; + int kernelSize, kernelType; + bool useVoxCol; + double voxColour; + int gridSize; + double scaleHeight; +} rview_image_setup; + + +/* + * Image setup window for customizing the renderer. + */ +class rviewImageSetup: public rviewFrame +{ + public: + + rviewImageSetup(rview_image_setup *ris, rviewRenderImage *parentWin); + ~rviewImageSetup(void); + + void unlinkParent(void); + + void OnSize(int w, int h); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + + void updateSettings(const rview_image_setup &ris); + void readNewSetup(void); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + static int parseLightDirection(const char *dir); + + // constants + // Borders used in setup window + static const int imgset_border; + // Height of text widgets in setup window + static const int imgset_theight; + // Height of checkboxes in setup window + static const int imgset_chkheight; + // Height of group boxes in setup window + static const int imgset_renheight; + static const int imgset_voxheight; + static const int imgset_hgtheight; + // Dimensions of buttons in setup window + static const int imgset_bwidth; + static const int imgset_bheight; + // Dimensions of checkboxes in setup window + static const int imgset_chowidth; + static const int imgset_choheight; + // Setup window dimensions + static const int imgset_width; + static const int imgset_height; + + + protected: + + rviewRenderImage *parent; + wxPanel *panel; + wxGroupBox *renderGroup, *voxelGroup, *heightGroup; + rviewText *zproWidget, *clipzWidget; + rviewText *pixThreshLowWidget, *pixThreshHighWidget, *wgtThreshWidget, *wgtQuantWidget; + rviewCheckBox *useRgbBrightness; + rviewCheckBox *useLights; + rviewChoice *kernelSize, *kernelType; + rviewText *lightsAmbient, *lightsGain, *lightsAngle, *lightsScintAngle; + rviewText *lightsDir, *lightsDist; + rviewCheckBox *useVoxCol; + rviewText *voxColour; + rviewText *gridSize, *scaleHeight; + rviewButton *okBut, *cancelBut; + rview_image_setup oldSetup, *imgSetup; + + static const char *normalKernelSizes[]; + static const keyword_to_ident_c normalKernelTypes[]; +}; + + + +/* + * Renderer playback control window + */ +class rendererControl: public rviewFrame +{ + public: + + rendererControl(float drx, float dry, float drz, int mode, rviewRenderImage *parentWin); + ~rendererControl(void); + + void unlinkParent(void); + void setActiveMode(int mode); + + void OnSize(int w, int h); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + // constants + // Borders used in renderer control window + static const int rctrl_border; + // Button dimensions + static const int rctrl_bwidth; + static const int rctrl_bheight; + // Reset button dimensions + static const int rctrl_rwidth; + static const int rctrl_rheight; + // Slider height + static const int rctrl_sheight; + // Window dimensions + static const int rctrl_width; + static const int rctrl_height; + + + protected: + + void updateParameters(void); + + wxPanel *panel; + rviewSlider *rotx, *roty, *rotz; + rviewButton *resetX, *resetY, *resetZ; + rviewButton *actionBut, *closeBut; + rviewRenderImage *parent; + int active; +}; + + +/* + * Renderer current view window (rotation, offset, scale) + */ +class rendererCurrentView: public rviewFrame +{ + public: + + rendererCurrentView(const vertex_fp &angles, long off, double scale, rviewRenderImage *parentWin); + ~rendererCurrentView(void); + + void unlinkParent(void); + + void OnSize(int w, int h); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + void updateView(const vertex_fp &angles, long off, double scale); + + // constants + // Borders used in renderer view window + static const int rcview_border; + // Button dimensions + static const int rcview_bwidth; + static const int rcview_bheight; + // Text widget height + static const int rcview_theight; + // Window dimensions + static const int rcview_width; + static const int rcview_height; + + + protected: + + void updateParameters(void); + + wxPanel *panel; + rviewButton *applyButton, *closeButton; + rviewText *rotx, *roty, *rotz; + rviewText *zoff; + rviewText *cubeScale; + rviewRenderImage *parent; +}; + + + +class wxPixmap; + +/* + * Container class for wxPixmap. + */ +class pixmapCanvas: public wxCanvas +{ + public: + + pixmapCanvas(rviewImage *parent, int x, int y, int w, int h, long style=0); + ~pixmapCanvas(void); + + void setPixmap(wxPixmap *pmap); + void updateDisplay(bool borders=FALSE); + + void OnPaint(void); + void OnEvent(wxMouseEvent &mevt); + + void SetAspectRatio(double ratio); // 0 for none + void ToggleDragBox(bool clearMode); + void SetDragBox(int x0, int y0, int x1, int y1); + bool HasDragBox(void) const; + bool GetDragBox(int &x0, int &y0, int &x1, int &y1) const; + void UpdateDragBox(int x1, int y1); + void AdjustDragBox(int x1, int y1); + + + protected: + + void paintCore(int x, int y); + void adjustBoxToRatio(void); + + wxPixmap *pixmap; + wxDC *myDC; + wxBrush brush; + wxBrush border; + wxBrush textBack; + wxPen bpen; + wxPen fpen; + wxFont *font; + rviewImage *parentWin; + int pixWidth, pixHeight; + int offX, offY; + int rect_x0, rect_y0, rect_x1, rect_y1; + double aspectRatio; + wxRect textBBox; +}; + + +// Flags used when settings are updated +#define RVIEW_IFLAG_VOXEL 1 +#define RVIEW_IFLAG_LIGHT 2 +#define RVIEW_IFLAG_HEIGHT 4 + +/* + * Abstract base class for all windows containing an image + controls + */ +class rviewImage: public rviewDisplay +{ + public: + + rviewImage(mdd_frame *mf, int es, unsigned int flags=0); + virtual ~rviewImage(void); + + virtual int openViewer(void); + + virtual void label(void); + virtual int process(wxObject &obj, wxEvent &evt); + virtual void processMouseEvent(wxMouseEvent &mevt); + virtual int userEvent(const user_event &ue); + virtual void prepareToDie(void); + virtual int newProjection(void)=0; + virtual int requestQuit(int level); + + virtual void OnSize(int w, int h); + virtual void OnMenuCommand(int id); + virtual bool OnClose(void); // overload from rviewFrame + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + // Default flags for wxPixmap class (dithering etc) + static int getPixmapFlags(void); + + // constants + // Slider dimensions + static const int image_swidth; + static const int image_sheight; + // Playback buttons dimensions + static const int image_pbwidth; + static const int image_pbheight; + // Minimum size of image + static const int image_minwidth; + static const int image_minheight; + // Checkbox dimensions + static const int image_chkwidth; + static const int image_chkheight; + // Bounding box checkbox dimensions + static const int image_bbwidth; + static const int image_bbheight; + // Text field dimensions + static const int image_twidth; + static const int image_theight; + // Button dimensions + static const int image_bwidth; + static const int image_bheight; + // Offsets of drag box in canvas + static const int image_dragtoff; + static const int image_dragtspace; + // Distance from the border at which to autoscroll + static const int image_draghotzone; + // Extra space reserved on control panel + static const int image_ctrly; + static const int image_totaly; + + + protected: + + void updatePixmap(char *oldData, char *newData); + void configureCspace(bool state); + void setCspaceProjMode(bool pmode); + void resizeImage(void); + void openViewerEpilogue(rviewFrameType ft); + int freeDimsFromProjection(int &dim1, int &dim2, r_Point *map); + void ensureViewCspace(void); + void deleteViewCspace(void); + virtual void projectObjectHook(void); + virtual void configureMode(void); + virtual char *initMode(void)=0; + virtual bool cspaceRangeHook(bool suggest); + virtual char *movieNewFrame(void); + virtual void rotateObject(wxMouseEvent &mevt); + virtual int fileMenuInitHook(wxMenu *menu); + virtual int configMenuInitHook(wxMenu *menu); + // Query viewer capabilities + virtual bool modeNeedsCspace(rviewBaseType bt) const; + virtual bool canRotateObject(void) const; + virtual bool moviePossible(void) const; + virtual bool showScaleSlider(void) const; + // view management + virtual int saveView(FILE *fp); + virtual int readView(const char *key, const char *value); + virtual void loadViewFinished(void); + + wxPixmap *pixmap; + pixmapCanvas *pcanv; + rviewSlider *scaleSlider; + int pixWidth, pixHeight, pixPitch, pixPad, pixDepth; + int virtualPitch; + char *imgData; + double scaleValue; + int scrollx, scrolly; + float mousex, mousey; + int mousebut; + unsigned int freeDims; + // For intensity to RGB translations (ushort) + colourspaceMapper *csmap; + bool doValToCspace; + bool doFullRangeCspace; + bool doProjRangeCspace; + // base type allows cspace mapping? + bool cspaceForType; + // image fully initialized? + bool initPhaseFinished; + r_Minterval *csInterv; + // shared by flat and height field which don't have a shared derivation path + rviewButton *playFwd, *playBack, *playStop; + int playDirection; + int lastMovieMode; + // colourspace parameters for view loading + colourspace_params *cspar; + + // view parameters + static const char *view_ScrollPos; + static const char *view_UseCspace; + static const char *view_CspaceFull; + static const char *view_CspaceProj; + static const char *view_CspaceMeans; + static const char *view_CspaceSigmas; + static const char *view_CspaceType; + static const char *view_CspaceRange; + static const char *view_ScaleValue; +}; + + +/* + * Base class for flat images, i.e. 2D orthogonal projections + */ +class rviewFlatBaseImage: public rviewImage +{ + public: + + rviewFlatBaseImage(mdd_frame *mf, int es, unsigned int flags=0); + virtual ~rviewFlatBaseImage(void); + + virtual int newProjection(void); + virtual int openViewer(void); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + + protected: + + virtual char *initMode(void); + char *projectImage(void); +}; + + +/* + * Standard flat images + */ +class rviewFlatImage: public rviewFlatBaseImage +{ + public: + + rviewFlatImage(mdd_frame *mf, unsigned int flags=0); + ~rviewFlatImage(void); + + void OnSize(int w, int h); + + virtual int openViewer(void); + virtual void label(void); + virtual int process(wxObject &obj, wxEvent &evt); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + virtual int getViewerType(void) const; + + + protected: + + virtual char *initMode(void); + virtual bool moviePossible(void) const; + virtual char *movieNewFrame(void); +}; + + +/* + * Abstract base class for all rendered images (voxel, height field, whatever...) + */ +class rviewRenderImage: public rviewImage +{ + public: + + rviewRenderImage(mdd_frame *mf, int es, unsigned int flags=0); + virtual ~rviewRenderImage(void); + + virtual void label(void); + virtual int process(wxObject &obj, wxEvent &evt); + virtual int newProjection(void); + virtual void prepareToDie(void); + virtual int requestQuit(int level); + virtual int userEvent(const user_event &ue); + virtual void OnSize(int w, int h); + virtual void OnMenuCommand(int id); + virtual bool OnClose(void); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + void closeEditor(bool newSetup); + void updateSettings(int setFlags); + + void closeRendererControls(void); + void setAutoRotation(float rx, float ry, float rz); + void setCurrentView(const vertex_fp &angles, long off, double scale); + + + protected: + + virtual char *initMode(void); + virtual char *setupEnvironment(int w, int h)=0; + virtual void rotateObject(wxMouseEvent &mevt); + virtual bool doUpdate(int updateFlags)=0; + virtual void redrawSettingsChanged(void); + virtual void fillBuffer(void)=0; + virtual void fillBackgroundCore(rviewBaseType bt, double minVal); + virtual int configMenuInitHook(wxMenu *menu); + virtual int viewMenuInitHook(wxMenu *menu); + virtual bool canRotateObject(void) const; + virtual void updateCurrentView(void); + + int setupEnvBase(int w, int h, r_Ref<r_GMarray> &mdd, colourspaceMapper **csm, r_Minterval *csdom); + char *setupGraphEnv(void); + void fillBufferBackground(bool doCspace, bool &cspaceOK, r_Ref<r_GMarray>& obj, colourspaceMapper **csm, r_Minterval *csdom, rviewBaseType bt, bool fullRange, double *useMinVal=NULL); + void translateBufferToCspace(rviewBaseType bt, double *useMinVal=NULL, double *useMaxVal=NULL); + static void rotateCube(int axis, float angle, vertex_fp *matrix); + void rotateCube(int axis, float angle); + void getLightPos(vertex_fp *lpos); + + virtual int saveView(FILE *fp); + virtual int readView(const char *key, const char *value); + virtual void loadViewFinished(void); + + // convert rotation matrix to angles and back + void matrixToAngles(vertex_fp &angles) const; + void anglesToMatrix(const vertex_fp &angles); + + int rendererPlayback; + vertex_fp *geomData, *geomUse; + vertex_fp *rot; + graph_env *graphEnv; + union {unsigned long l; float f; double d;} voxColour; + long zoff; + double cubeScale; + // Rotation angle increments for renderer playback + float drx, dry, drz; + // Setup options + rview_image_setup setup; + rviewImageSetup *setupWindow; + rendererControl *rcontrol; + rendererCurrentView *rcurview; + + // view keywords + static const char *view_ZProject; + static const char *view_ZClip; + static const char *view_PixThreshLow; + static const char *view_PixThreshHigh; + static const char *view_WeightThresh; + static const char *view_WeightQuant; + static const char *view_UseRGBBright; + static const char *view_UseLighting; + static const char *view_LightAmbient; + static const char *view_LightGain; + static const char *view_LightAngle; + static const char *view_LightScint; + static const char *view_LightDir; + static const char *view_LightDist; + static const char *view_KernelSize; + static const char *view_KernelType; + static const char *view_UseVoxColour; + static const char *view_VoxColour; + static const char *view_GridSize; + static const char *view_ScaleHeight; + static const char *view_Rotation; + static const char *view_ZOffset; +}; + + +/* + * Class for visualizations of volumetric images (3D texture mapper & + * voxel renderer). + */ +class rviewVolumeImage: public rviewRenderImage +{ + public: + + rviewVolumeImage(mdd_frame *mf, unsigned int flags=0); + virtual ~rviewVolumeImage(void); + + virtual void label(void); + virtual int process(wxObject &obj, wxEvent &evt); + virtual void OnSize(int w, int h); + virtual void OnMenuCommand(int id); + virtual int openViewer(void); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + virtual int getViewerType(void) const; + + + protected: + + virtual char *initMode(void); + virtual char *setupEnvironment(int w, int h); + virtual bool doUpdate(int updateFlags); + virtual void fillBuffer(void); + + virtual int saveView(FILE *fp); + virtual int readView(const char *key, const char *value); + virtual void loadViewFinished(void); + + rviewCheckBox *boundingBox; + rviewImageMode imode; + int lastMode; + tex_desc *texDesc; + voxel_desc *voxDesc; + bool initVoxParams; + bool doBoundingBox; + + static const char *view_VolumeMode; + static const char *view_UseBBox; +}; + + +/* + * Class for visualizing data as heightfields. + */ +class rviewHeightImage: public rviewRenderImage +{ + public: + + rviewHeightImage(mdd_frame *mf, unsigned int flags=0); + virtual ~rviewHeightImage(void); + + virtual void label(void); + virtual int process(wxObject &obj, wxEvent &evt); + virtual int newProjection(void); + virtual void prepareToDie(void); + virtual int requestQuit(int level); + virtual int openViewer(void); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + virtual int getViewerType(void) const; + + + protected: + + int depthForHeightfield(void) const; + virtual char *initMode(void); + virtual char *setupEnvironment(int w, int h); + virtual char *movieNewFrame(void); + virtual bool doUpdate(int updateFlags); + virtual void redrawSettingsChanged(void); + virtual void fillBackgroundCore(rviewBaseType bt, double minVal); + virtual void fillBuffer(void); + virtual bool cspaceRangeHook(bool suggest); + virtual bool moviePossible(void) const; + virtual bool modeNeedsCspace(rviewBaseType bt) const; + + mdd_desc *mddDesc; + mesh_desc *meshDesc; + // for colourspace mapping of heightfields. + r_Ref<r_GMarray> dummyMDD; +}; + + + +/* + * An image scaled by the DBMS + */ +class r_Fast_Base_Scale; + +class rviewScaledImage: public rviewFlatBaseImage +{ + public: + + rviewScaledImage(collection_desc *cd, r_Fast_Base_Scale *scaler, unsigned int flags=0); + ~rviewScaledImage(void); + + virtual void processMouseEvent(wxMouseEvent &mevt); + virtual void label(void); + virtual int process(wxObject &obj, wxEvent &evt); + virtual int openViewer(void); + virtual void OnSize(int w, int h); + virtual int newProjection(void); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + virtual int getViewerType(void) const; + virtual const r_Minterval &getVirtualDomain(void) const; + + + protected: + + virtual char *initMode(void); + virtual void projectObjectHook(void); + + char *projectImage(void); + bool showScaleSlider(void) const; + void scaleViewBy(double scale); + void newView(bool loadImage=TRUE); + void projectionStringForView(void); + double getLastScale(void) const; + + // view management + virtual int saveView(FILE *fp); + virtual int readView(const char *key, const char *value); + virtual void loadViewFinished(void); + void ensureLoadedView(void); + + rviewButton *zoomInBut; + rviewButton *zoomOutBut; + rviewButton *lastZoomBut; + rviewButton *zoomBoxBut; + rviewText *scaleString; + + bool boxState; + + typedef struct { + double scale; + r_Point low, high; + int dim1, dim2; + } view_desc_t; + + bool compareViews(const view_desc_t &v1, const view_desc_t &v2); + + // The current view + view_desc_t thisView; + r_Minterval fullDomain; + bool dontLoad; + double initialScale; + view_desc_t *loadedView; + + DynamicStack<view_desc_t> viewHistory; + + r_Fast_Base_Scale *scaleObject; + + collection_desc *collDesc; + + static const double scaleStep; + + // view parameters + static const char *view_CurrentBox; + static const char *view_BoxScale; +}; + + +// Structure for static conversion of MDD -> pixmap +typedef struct rviewFlatProjEnv { + // initialized by caller + r_GMarray *mddPtr; + r_Point pt1, pt2; + int dim1, dim2; + rviewBaseType bt; + double scale; + bool doCspace; + colourspaceMapper *csmap; + int cspaceState; + // initialized by prepareFlatProjection() + int width, height; + int pitch, pad, depth; +} rviewFlatProjEnv; + +// Setup variables for projecting images +int rviewPrepareFlatProjection(rviewFlatProjEnv &penv); +// Project an MDD object into an image bitmap +int rviewPerformFlatProjection(rviewFlatProjEnv &env, char *data); + + + + + + +enum rviewChartMode { + rcm_none, + rcm_bar, + rcm_line, + rcm_spline +}; + +/* + * Canvas displaying a chart + */ + +class chartCanvas: public wxCanvas +{ + public: + + chartCanvas(wxWindow *parent, int x, int y, int w, int h, long style=0); + ~chartCanvas(void); + + void setData(mdd_frame *mf, rviewBaseType bt); + void setVars(int s, double cs, int ds, bool cy, rviewChartMode cm); + int setProjection(r_Point &p1, r_Point &p2); + void OnPaint(void); + + // constants + // Space reserved on top / bottom of chart + static const int chcanv_cospace; + // Length of a corrdinate axis marker line + static const int chcanv_colength; + // Start exponential axis numbering when |exponent| >= x + static const int chcanv_exponents; + + + protected: + + // Core-functionality of chart modes + void redrawBar(wxDC *cdc, int height, int dim, int startOff, int endOff, float scale, float posx, float stepx, float orgy); + void redrawLine(wxDC *cdc, int dim, int startOff, int endOff, float scale, float posx, float stepx, float orgy); + void redrawSpline(wxDC *cdc, int dim, int startOff, int endOff, float scale, float posx, float stepx, float orgy); + + r_Ref<r_GMarray> mddObj; + int step, dimMDD; + r_Point pt1, pt2; + double min, max, costep; + rviewBaseType baseType; + rviewChartMode cmode; + wxBrush brush, back; + wxBrush brush_r, brush_g, brush_b; + wxPen pen, pen_r, pen_g, pen_b; + wxFont *font; + bool cosys; + char format[10]; + int coleft, datastep; + int scroll; +}; + + +/* + * A class for drawing charts + */ + +class rviewChart: public rviewDisplay +{ + public: + + rviewChart(mdd_frame *mf, unsigned int flags=0); + ~rviewChart(void); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + int newProjection(void); + virtual int openViewer(void); + + void OnSize(int w, int h); + void OnMenuCommand(int id); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + virtual int getViewerType(void) const; + + // constants + // Text widget dimensions (step) + static const int chart_twidth; + static const int chart_theight; + // checkbox dims + static const int chart_cwidth; + static const int chart_cheight; + // Minimum size of viewer + static const int chart_minwidth; + static const int chart_minheight; + static const int chart_ctrly; + static const int chart_totaly; + + + protected: + + virtual int saveView(FILE *fp); + virtual int readView(const char *key, const char *value); + virtual void loadViewFinished(void); + + void checkModeMenu(void); + + chartCanvas *canvas; + int step, datastep; + int lastMode; + double costep; + int scroll; + bool cosys; + rviewChartMode cmode; + rviewText *stText, *coText, *dataText; + rviewCheckBox *csBox; + + static const char *view_StepSize; + static const char *view_Markers; + static const char *view_ScrollPos; + static const char *view_CoSys; + static const char *view_ChartMode; +}; + + + +/* + * Canvas for plotting text onto + */ + +class textCanvas: public wxCanvas +{ + public: + + textCanvas(wxWindow *parent, int x, int y, int w, int h, long style=0); + ~textCanvas(void); + + void setData(mdd_frame *mf, rviewBaseType bt, unsigned int bs); + void setStep(int sx, int sy); + void setCoSys(bool cs, int &cl, int &ct); + void setProjection(r_Point &pt1, r_Point &pt2, unsigned int fd, r_Point *mapIndex=NULL); + void setNumberBase(int newBase); + void OnPaint(void); + void CalcTextExtent(char *b, float &width, float &height); + void EstimateCellSize(int &width, int &height); + + // constants + // Space around coordinate system + static const int txcanv_cospace; + // Default space between columns + static const int txcanv_colspace; + // Border used in text canvas + static const int txcanv_border; + + + protected: + + r_Ref<r_GMarray> mddObj; + int stepx, stepy, dimMDD; + int scrollX, scrollY; + int coleft, cotop; + int dim1, dim2; + unsigned int freeDims; + r_Point pt1, pt2; + int numberBase; + bool cosys; + rviewBaseType baseType; + unsigned int baseSize; + wxBrush fore, back; + wxPen pen; + wxFont *font; +}; + + + +/* + * Class for displaying tables + */ + +class rviewTable: public rviewDisplay +{ + public: + + rviewTable(mdd_frame *mf, unsigned int flags=0); + ~rviewTable(void); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + int newProjection(void); + void newTableSize(void); + virtual int openViewer(void); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + virtual int getViewerType(void) const; + + void OnSize(int w, int h); + void OnMenuCommand(int id); + + // constants + // Dimensions of additional wxText items (configs) + static const int table_twidth; + static const int table_theight; + // Dimensions of check box + static const int table_cwidth; + static const int table_cheight; + // Minimum dimensions of table + static const int table_minwidth; + static const int table_minheight; + static const int table_ctrly; + static const int table_totaly; + + + protected: + + void EstimateCellSize(int &width, int &height); + + virtual int saveView(FILE *fp); + virtual int readView(const char *key, const char *value); + virtual void loadViewFinished(void); + + void checkModeMenu(void); + + textCanvas *canvas; + int stepx, stepy; + int scrollx, scrolly; + int fieldsx, fieldsy; + int lastMode; + unsigned int freeDims; + bool cosys; + rviewText *sxText, *syText; + rviewCheckBox *csBox; + int numberBase; + + static const char *view_StepSize; + static const char *view_ScrollPos; + static const char *view_CoSys; + static const char *view_NumBase; +}; + + + +/* + * Class for displaying strings + */ + +class rviewStringViewer: public rviewDisplay +{ + public: + + rviewStringViewer(mdd_frame *mf, unsigned int flags=0); + ~rviewStringViewer(void); + + int newProjection(void); + virtual int openViewer(void); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + virtual int getViewerType(void) const; + + void OnSize(int w, int h); + + // constants + static const int strview_msgheight; + static const int strview_minwidth; + static const int strview_minheight; + static const int strview_ctrly; + static const int strview_totaly; + + + protected: + + unsigned int freeDims; + wxMessage *msgString; +}; + +#endif diff --git a/applications/rview/rviewDb.cpp b/applications/rview/rviewDb.cpp new file mode 100644 index 0000000..d9c7888 --- /dev/null +++ b/applications/rview/rviewDb.cpp @@ -0,0 +1,970 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * Purpose: + * + * Database abstraction layer. All database accesses are performed though + * the functions provided here. This includes creating / deleting / looking + * up collections, inserting objects into collections and executing RasQL + * queries. + * + * COMMENTS: + * None + */ + + + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + +#include "wb_timer.h" + + + +#include <string.h> + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + +#include "raslib/scalar.hh" +#include "raslib/rmdebug.hh" +#include "rasodmg/fastscale.hh" + + +#include "rviewTypes.hh" + +#include "labelManager.hh" + +#include "rviewDb.hh" +#include "rviewMDD.hh" +#include "rviewPrefs.hh" + + + +rviewDatabase::rviewDatabase(void) +{ + dbOpen = FALSE; + lastTransferFormat = r_Array; + lastStorageFormat = r_Array; +} + + +rviewDatabase::~rviewDatabase(void) +{ + if (dbOpen) + { + close(); + } +} + + +bool rviewDatabase::isOpen(void) +{ + return dbOpen; +} + + + +int rviewDatabase::open(const char *srvname, int srvport, const char *dbname, + const char *usrname, const char *usrpassword) +{ + int status; + + if (!dbOpen) + { + server = srvname; + port = srvport; + database = dbname; + username = usrname; + userpassword = usrpassword; + + } + else + { + rviewErrorbox::reportError(lman->lookup("errorDatabaseOpen")); + return 0; + } + + rviewProgress *prog = new rviewProgress(lman->lookup("progOpenDb")); + + try + { + ::wxStartTimer(); + + dbase.set_servername(server,port); + dbase.set_useridentification(username, userpassword); + dbase.open(database); +#ifdef RMANDEBUG + cout << "Open Database Time: " << ::wxGetElapsedTime(TRUE) << "ms" << endl; +#endif + dbOpen = TRUE; + status = 1; + } + catch ( r_Error &errObj ) + { +/* + char *errLab; + cerr << errObj.what() << endl; + switch ( errObj.get_kind() ) + { + case r_Error::r_Error_HostInvalid: errLab = "\\errorHostInvalid"; break; + case r_Error::r_Error_ServerInvalid: errLab = "\\errorServerInvalid"; break; + case r_Error::r_Error_ClientUnknown: errLab = "\\errorClientUnknown"; break; + case r_Error::r_Error_DatabaseUnknown: errLab = "\\errorDatabaseUnknown"; break; + case r_Error::r_Error_DatabaseOpen: errLab = "\\errorDatabaseOpen"; break; + case r_Error::r_Error_RpcInterfaceIncompatible: errLab = "\\errorRpcInterface"; break; + default: errLab = "\\errorUnknown"; break; + } +*/ + + rviewErrorbox::reportError((const char*)errObj.what()); + status = 0; + } + + prog->Close(TRUE); + + return status; +} + + + +void rviewDatabase::close(void) +{ +// if (ensureDatabase() == 0) return; + dbase.close(); + dbOpen = FALSE; +} + + + +int rviewDatabase::collectionToDesc(r_Set<r_Ref<r_GMarray> > &mddColl, collection_desc *desc) +{ + int i, collMembers; + rviewBaseType bt = rbt_none; + r_Object *mo = NULL; + + desc->number = 0; desc->mddObjs = NULL; desc->strObjs = NULL; desc->collType = NULL; + + collMembers = mddColl.cardinality(); + + if (collMembers <= 0) + { + return 1; + } + + if ((desc->mddObjs = new mdd_frame[collMembers]) == NULL) + { + cerr << lman->lookup("errorMemory") << endl; + return 0; + } + + r_Iterator < r_Ref <r_GMarray> > iterator = mddColl.create_iterator(); + + for (i=0; i<collMembers; i++, iterator++) + { + RMDBGONCE(3, RMDebug::module_applications, "rviewDb", "collectionToDesc() MDD number " << i << ":\tDomain = " << (*iterator)->spatial_domain() ); + + // Copy each MDD object + desc->mddObjs[i].mdd = new r_GMarray((const r_GMarray &)(*(*iterator))); + // oid isn't copied by copy constructor... + desc->mddObjs[i].mdd->initialize_oid((*iterator)->get_oid()); + if (desc->mddObjs[i].mdd.is_null()) + { + cerr << lman->lookup("errorMemory") << endl; + } + desc->mddObjs[i].flags = 0; + // Set type information? + if (mo == NULL) + { + // we must use the original mdd data to get the type information! + mo = (r_Object*)(&(**iterator)); + bt = rviewGetBasetype(mo); + } + } + + desc->number = collMembers; + + desc->collType = new char[strlen(rviewBaseTypes[bt]) + 1]; + strcpy(desc->collType, rviewBaseTypes[bt]); + + return 1; +} + + + +int rviewDatabase::lookupCollection(collection_desc *desc) +{ + r_Ref < r_Set < r_Ref <r_GMarray > > > mddCollPtr; + r_Transaction transaction; + int status; + char buffer[STRINGSIZE]; + + status = 0; + rviewProgress *prog = new rviewProgress(lman->lookup("progLookup")); + + try + { + ::wxStartTimer(); + + transaction.begin( r_Transaction::read_only ); + + //set storage & transfer params + if (!ensureDatabase()) + { + transaction.abort(); + return 0; + } + + mddCollPtr = dbase.lookup_object(desc->collName); + + status = collectionToDesc(*mddCollPtr, desc); + + transaction.commit(); + + sprintf(buffer, "%s: %d ms", lman->lookup("textTime"), ::wxGetElapsedTime(TRUE)); + if ((desc->collInfo = new char[strlen(buffer) + 1]) != NULL) + { + strcpy(desc->collInfo, buffer); + } +#ifdef RMANDEBUG + cout << "Lookup collection " << buffer << endl; +#endif + } + + catch ( r_Error &errObj ) + { + const char *msg; + + cerr << errObj.what() << endl; + + if (errObj.get_errorno() != 0) + { + msg = errObj.what(); + } + else + { + switch (errObj.get_kind()) + { + case r_Error::r_Error_ClientUnknown: msg = lman->lookup("errorClientUnknown"); break; + case r_Error::r_Error_DatabaseClosed: msg = lman->lookup("errorDatabaseClosed"); break; + case r_Error::r_Error_QueryParameterCountInvalid: msg = lman->lookup("errorQueryParamNum"); break; + case r_Error::r_Error_TransferFailed: msg = lman->lookup("errorTransferFailed"); break; + default: msg = lman->lookup("errorUnknown"); break; + } + } + rviewErrorbox::reportError(msg); + } + + prog->Close(TRUE); + + return status; +} + + +r_Ref<r_GMarray> rviewDatabase::getScaledObject(r_Fast_Base_Scale *scaler, const r_Minterval &trimDomain, double scale) +{ + r_Transaction ta; + r_Ref<r_GMarray> result; + + try + { + ta.begin(r_Transaction::read_only); + + //FIXME +/* + //set storage & transfer params + if (!ensureDatabase()) + { + ta.abort(); + return 0; + } +*/ + // apply trimming before scaling! + result = scaler->get_scaled_object(trimDomain, scale, 1); + ta.commit(); + } + catch(r_Error &err) + { + ta.abort(); + rviewErrorbox::reportError(err.what()); + } + // if the transaction failed, result.is_null() will deliver TRUE. + return result; +} + + +r_Fast_Base_Scale *rviewDatabase::lookupScaledObject(collection_desc *desc, double scale) +{ + r_Transaction ta; + r_Fast_Base_Scale *result; + rviewBaseType baseType; + char buffer[STRINGSIZE]; + + + + desc->strObjs = NULL; + + rviewProgress *prog = new rviewProgress(lman->lookup("progLookup")); + + try + { + ta.begin(r_Transaction::read_only); + + + //set storage & transfer params + if (!ensureDatabase()) + { + ta.abort(); + return 0; + } + + r_Ref<r_GMarray> minArray = r_Fast_Base_Scale::get_minimal_array(desc->collName); + ta.commit(); + baseType = rviewGetBasetype(minArray.ptr()); + minArray.destroy(); + } + catch(r_Error &err) + { + ta.abort(); + rviewErrorbox::reportError(err.what()); + prog->Close(TRUE); + return NULL; + } + + try + { + ta.begin(r_Transaction::read_only); + + + //set storage & transfer params + if (!ensureDatabase()) + { + ta.abort(); + return 0; + } + + switch (baseType) + { + case rbt_bool: + result = new r_Fast_Scale<r_Boolean>(desc->collName); + break; + case rbt_char: + result = new r_Fast_Scale<r_Char>(desc->collName); + break; + case rbt_uchar: + result = new r_Fast_Scale<r_Octet>(desc->collName); + break; + case rbt_short: + result = new r_Fast_Scale<r_Short>(desc->collName); + break; + case rbt_ushort: + result = new r_Fast_Scale<r_UShort>(desc->collName); + break; + case rbt_long: + result = new r_Fast_Scale<r_Long>(desc->collName); + break; + case rbt_ulong: + result = new r_Fast_Scale<r_ULong>(desc->collName); + break; + case rbt_float: + result = new r_Fast_Scale<r_Float>(desc->collName); + break; + case rbt_double: + result = new r_Fast_Scale<r_Double>(desc->collName); + break; + case rbt_rgb: + result = new r_Fast_Scale<RGBPixel>(desc->collName); + break; + default: + rviewErrorbox::reportError(lman->lookup("errorScaledObjType")); + prog->Close(TRUE); + return NULL; + } + ta.commit(); + } + catch(r_Error &err) + { + ta.abort(); + rviewErrorbox::reportError(err.what()); + prog->Close(TRUE); + return NULL; + } + + r_Ref<r_GMarray> mddObj; + + ::wxStartTimer(); + + mddObj = getScaledObject(result, result->get_full_domain(), scale); + if (mddObj.is_null()) + { + delete result; + prog->Close(TRUE); + return NULL; + } + + sprintf(buffer, "%s: %d ms", lman->lookup("textTime"), ::wxGetElapsedTime(TRUE)); + if ((desc->collInfo = new char[strlen(buffer) + 1]) != NULL) + { + strcpy(desc->collInfo, buffer); + } + desc->collType = new char[strlen(rviewBaseTypes[baseType]) + 1]; + strcpy(desc->collType, rviewBaseTypes[baseType]); + desc->mddObjs = new mdd_frame[1]; + desc->number = 1; + desc->mddObjs[0].mdd = mddObj; desc->mddObjs[0].flags = 0; + + prog->Close(TRUE); + + return result; +} + + +int rviewDatabase::createCollection(const char *collName, rviewBaseType bt) +{ + r_Ref <r_Set <r_Ref <r_GMarray> > > mddCollPtr; + r_Transaction ta; + + try + { + mddCollPtr = dbase.lookup_object(collName); + } + catch (...) + { + RMDBGONCE(3, RMDebug::module_applications, "rviewDb", "createCollection( " << collName << " )"); + + ta.begin(); + + //set storage & transfer params + if (!ensureDatabase()) + { + ta.abort(); + return 0; + } + + try + { + switch (bt) + { + case rbt_bool: + mddCollPtr = new( &dbase, rviewSetNames[bt][2] ) r_Set< r_Ref< r_Marray< r_Boolean > > >; + break; + case rbt_char: + mddCollPtr = new( &dbase, rviewSetNames[bt][2] ) r_Set< r_Ref< r_Marray< r_Char > > >; + break; + case rbt_long: + mddCollPtr = new( &dbase, rviewSetNames[bt][2] ) r_Set< r_Ref< r_Marray< RGBPixel > > >; + break; + default: ta.abort(); return 0; + } + + dbase.set_object_name(*mddCollPtr, collName); + + ta.commit(); + } + catch (r_Error &obj) + { + cerr << lman->lookup("errorCollCreate") << ": " << obj.what() << endl; + } + } + return 0; +} + + + +int rviewDatabase::deleteCollection(const char *collName) +{ + r_Ref <r_Set <r_Ref <r_GMarray> > > mddCollPtr; + r_Transaction ta; + + try + { + ta.begin(); + + //set storage & transfer params + if (!ensureDatabase()) + { + ta.abort(); + return 0; + } + + mddCollPtr = dbase.lookup_object(collName); + mddCollPtr.delete_object(); + ta.commit(); + return 1; + } + catch (...) + { + cerr << lman->lookup("errorCollDelete") << endl; + } + return 0; +} + + + +int rviewDatabase::insertObject(const char *collName, r_Ref<r_GMarray> mddObj, r_Minterval *domain) +{ + r_Ref <r_Set <r_Ref <r_GMarray> > > mddCollPtr; + r_Ref <r_GMarray> mddPtr; + r_Transaction ta; + rviewBaseType bt; + r_Object *mo; + int dim, status; + + RMDBGENTER(3, RMDebug::module_applications, "rviewDb", "insertObject( " << collName << ", ... )"); + + dim = (mddObj->spatial_domain()).dimension(); + if ((dim < 1) || (dim > MAXIMUM_DIMENSIONS)) + { + rviewErrorbox::reportError(lman->lookup("errorObjectDims")); + return 0; + } + + // *mddObj ``removes'' r_Ref. Can't leave out the &* combination! + mo = (r_Object*)(&(*mddObj)); + bt = rviewGetBasetype(mo); + + ta.begin(); + + //set storage & transfer params + if (!ensureDatabase()) + { + ta.abort(); + return 0; + } + + rviewProgress *prog = new rviewProgress(lman->lookup("progInsert")); + + ::wxStartTimer(); + + try + { + mddCollPtr = dbase.lookup_object(collName); + RMDBGMIDDLE(3, RMDebug::module_applications, "rviewDb", "insertObject() collection cardinality: " << mddCollPtr->cardinality() ); + } + catch (r_Error &obj) + { + char *setName; + + setName = (char*)obj.what(); + setName = rviewSetNames[bt][dim-1]; + + switch (bt) + { + case rbt_bool: + mddCollPtr = new (&dbase, setName) r_Set <r_Ref <r_Marray <r_Boolean> > >; + break; + case rbt_char: + mddCollPtr = new (&dbase, setName) r_Set <r_Ref <r_Marray <r_Char> > >; + break; + case rbt_uchar: + mddCollPtr = new (&dbase, setName) r_Set <r_Ref <r_Marray <r_Octet> > >; + break; + case rbt_short: + mddCollPtr = new (&dbase, setName) r_Set <r_Ref <r_Marray <r_Short> > >; + break; + case rbt_ushort: + mddCollPtr = new (&dbase, setName) r_Set <r_Ref <r_Marray <r_UShort> > >; + break; + case rbt_long: + mddCollPtr = new (&dbase, setName) r_Set <r_Ref <r_Marray <r_Long> > >; + break; + case rbt_ulong: + mddCollPtr = new (&dbase, setName) r_Set <r_Ref <r_Marray <r_ULong> > >; + break; + case rbt_float: + mddCollPtr = new (&dbase, setName) r_Set <r_Ref <r_Marray <r_Float> > >; + break; + case rbt_double: + mddCollPtr = new (&dbase, setName) r_Set <r_Ref <r_Marray <r_Double> > >; + break; + case rbt_rgb: + mddCollPtr = new (&dbase, setName) r_Set <r_Ref <r_Marray <RGBPixel> > >; + break; + default: + cerr << "Unknown base type " << bt << endl; + ta.abort(); prog->Close(TRUE); return 0; + } + dbase.set_object_name(*mddCollPtr, collName); + } + + status = 0; + try + { + if (mdd_createSubcube(mddObj, mddPtr, domain, &dbase) == 0) + { + cerr << "Failed to create persistent MDD object" << endl; + ta.abort(); prog->Close(TRUE); return 0; + } + RMDBGMIDDLE(3, RMDebug::module_applications, "rviewDb", "insertObject() Object type name " << rviewTypeNames[bt][dim-1] ); + mddPtr->set_type_by_name(rviewTypeNames[bt][dim-1]); + mddCollPtr->insert_element(mddPtr); + + ta.commit(); + +#ifdef RMANDEBUG + cout << "Insert Object Time: " << ::wxGetElapsedTime(TRUE) << "ms" << endl; +#endif + + status = 1; + } + catch (r_Error &obj) + { + cerr << lman->lookup("errorInsertObj") << ": " << obj.what() << endl; + } + + prog->Close(TRUE); + + RMDBGEXIT(3, RMDebug::module_applications, "rviewDb", "insertObject()"); + + return status; +} + + + +int rviewDatabase::executeQuery(collection_desc *desc, const char *qry, r_Ref<r_GMarray> *updateMdd, bool showProgress) +{ + int status; + char buffer[STRINGSIZE]; + + r_Transaction ta; + r_Set<r_Ref_Any> mddColl; + int collMembers; + + rviewProgress *prog = NULL; + + if (showProgress) + prog = new rviewProgress(lman->lookup("progQuery")); + + status = 0; + + desc->number = 0; desc->mddObjs = NULL; desc->strObjs = NULL; desc->collType = NULL; + + try + { + int isUpdateQuery; + char *mddArg; + r_OQL_Query query(qry); + + // Do this check before starting the transaction + if (isUpdateQuery = query.is_update_query()) + { + if ((mddArg = (char*)strstr(qry, "$")) != NULL) + { + if (updateMdd == NULL) + { + rviewErrorbox::reportError(lman->lookup("errorUpdtObject")); + if (prog != NULL) + prog->Close(TRUE); + return 0; + } + } + } + + ::wxStartTimer(); + + if (isUpdateQuery) + { + ta.begin(); + + //set storage & transfer params + if (!ensureDatabase()) + { + ta.abort(); + return 0; + } + + } + else + { + ta.begin(r_Transaction::read_only); + + + //set storage & transfer params + if (!ensureDatabase()) + { + ta.abort(); + return 0; + } + + } + +#ifdef RMANDEBUG + //cout << "Expanded query:" << endl << query.get_query() << endl; +#endif + + if (isUpdateQuery) + { +#ifdef RMANDEBUG + //cout << "Is update query " << endl; +#endif + if (mddArg != NULL) + { + RMDBGONCE(3, RMDebug::module_applications, "rviewDb", "executeQuery() Update MDD domain " << (*updateMdd)->spatial_domain() ); + query << *(*updateMdd); + } + r_oql_execute(query); + } + else + { +#ifdef RMANDEBUG + //cout << "Is normal query" << endl; +#endif + r_oql_execute(query, mddColl); + } + + collMembers = mddColl.cardinality(); + + if (collMembers != 0) + { + int collType; + + collType = mddColl.get_element_type_schema()->type_id(); + if (collType == r_Type::MARRAYTYPE) + { + r_Set<r_Ref<r_GMarray> > mddArrayColl; + r_Iterator<r_Ref_Any> iterator = mddColl.create_iterator(); + int i; + + RMDBGONCE(3, RMDebug::module_applications, "rviewDb", "executeQuery() array-collection, build new set..." ); + + for (i=0; i<collMembers; i++, iterator++) + { + mddArrayColl.insert_element((r_Ref<r_GMarray>)(*iterator)); + } + status = collectionToDesc(mddArrayColl, desc); + } + else + { + r_Iterator<r_Ref_Any > iterator = mddColl.create_iterator(); + ostrstream memstr(buffer, STRINGSIZE); + int i; + + RMDBGONCE(3, RMDebug::module_applications, "rviewDb", "executeQuery() non-marray collection, build table..." ); + + desc->mddObjs = NULL; + desc->strObjs = new char*[collMembers]; + desc->number = collMembers; + + for (i=0; i<collMembers; i++, iterator++) + { + memstr.width(3); + memstr << i << ": "; + switch (collType) + { + case r_Type::POINTTYPE: + ((r_Ref<r_Point>)*iterator)->print_status(memstr); break; + case r_Type::SINTERVALTYPE: + ((r_Ref<r_Sinterval>)*iterator)->print_status(memstr); break; + case r_Type::MINTERVALTYPE: + ((r_Ref<r_Minterval>)*iterator)->print_status(memstr); break; + case r_Type::OIDTYPE: + ((r_Ref<r_OId>)*iterator)->print_status(memstr); break; + default: + ((r_Ref<r_Scalar>)*iterator)->print_status(memstr); break; + } + memstr << '\0'; + desc->strObjs[i] = new char[strlen(buffer)+1]; + strcpy(desc->strObjs[i], buffer); + //cout << "item " << i << ": " << desc->strObjs[i] << endl; + memstr.seekp(0); + } + (mddColl.get_type_schema())->print_status(memstr); + memstr << '\0'; + desc->collType = new char[strlen(buffer)+1]; + strcpy(desc->collType, buffer); + status = 1; + } + } + ta.commit(); + + sprintf(buffer, "%s: %d ms", lman->lookup("textTime"), ::wxGetElapsedTime(TRUE)); + if ((desc->collInfo = new char[strlen(buffer) + 1]) != NULL) + { + strcpy(desc->collInfo, buffer); + } + +#ifdef RMANDEBUG + cout << "Execute Query " << buffer << endl; +#endif + // no query error + line = -1; col = -1; + } + + catch ( r_Equery_execution_failed &errObj ) + { + if (errObj.get_errorno() == 0) + { + cerr << lman->lookup("errorQueryUnknown") << endl; + } + else + { + char msg[1024]; + const char *what; + + what = errObj.what(); + if (what != NULL) // might actually happen... + { + // Memorize the position the error occurred at + line = errObj.get_lineno(); col = errObj.get_columnno(); + sprintf(msg, "%s: %s", lman->lookup("errorQueryFailed"), errObj.what()); + rviewErrorbox::reportError((const char*)msg); + } + } + } + + catch (r_Error &errObj) + { + cerr << errObj.what() << endl; + } + + if (prog != NULL) + prog->Close(TRUE); + + return status; +} + + +int rviewDatabase::getMinterval(r_Minterval &dom, const char *collname, const double *loid) +{ + + r_Transaction ta; + r_Set<r_Ref_Any> collPtr; + char buffer[STRINGSIZE]; + int length, number; + + length = sprintf(buffer, "SELECT SDOM(x) FROM %s AS x", collname); + if (loid != NULL) + sprintf(buffer + length, " WHERE OID(x) = %f", *loid); + + try + { + ta.begin(r_Transaction::read_only); + + + //set storage & transfer params + if (!ensureDatabase()) + { + ta.abort(); + return 0; + } + + r_OQL_Query query(buffer); + r_oql_execute(query, collPtr); + number = collPtr.cardinality(); + if (number > 1) + cout << "rviewDatabase::getMinterval(): more than one object returned!" << endl; + + r_Iterator<r_Ref_Any> iterator = collPtr.create_iterator(); + dom = *((r_Ref<r_Minterval>)(*iterator)); + + ta.commit(); + } + catch (r_Error &err) + { + ta.abort(); + cerr << err.what() << endl; + return 0; + } + + return 1; +} + + + +// Returns the position of the error in the last query +int rviewDatabase::getErrorInfo(int &l, int &c) const +{ + if (line < 0) return 0; + l = line; c = col; + return 1; +} + + + +const r_Database *rviewDatabase::getDatabase(void) const +{ + return &dbase; +} + + + +int rviewDatabase::ensureDatabase(void) +{ + if (dbOpen) + { + r_Data_Format currentFormat; + + currentFormat = prefs->getTransferFormat(); + if ((lastTransferFormat != currentFormat) || (lastTransferParams != prefs->transferParm)) + { + try + { + dbase.set_transfer_format(currentFormat, prefs->transferParm); + lastTransferFormat = currentFormat; + lastTransferParams = prefs->transferParm; + } + catch (r_Error &err) + { + cerr << err.what() << endl; + } + } + currentFormat = prefs->getStorageFormat(); + if ((lastStorageFormat != currentFormat) || (lastStorageParams != prefs->storageParm)) + { + try + { + dbase.set_storage_format(currentFormat, prefs->storageParm); + lastStorageFormat = currentFormat; + lastStorageParams = prefs->storageParm; + } + catch (r_Error &err) + { + cerr << err.what() << endl; + } + } + return 1; + } + rviewErrorbox::reportError(lman->lookup("errorDatabaseClosed")); + return 0; +} diff --git a/applications/rview/rviewDb.hh b/applications/rview/rviewDb.hh new file mode 100644 index 0000000..33764f4 --- /dev/null +++ b/applications/rview/rviewDb.hh @@ -0,0 +1,110 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * Purpose: + * + * Database abstraction layer. All database accesses are performed though + * the functions provided here. This includes creating / deleting / looking + * up collections, inserting objects into collections and executing RasQL + * queries. + * + * COMMENTS: + * none + */ + + + +#ifndef _RVIEW_DB_H_ +#define _RVIEW_DB_H_ + + + +#ifdef __GNUG__ +#pragma interface +#endif + + + + +#include "rasodmg/database.hh" +#include "rasodmg/transaction.hh" +#include "rasodmg/set.hh" +#include "rasodmg/ref.hh" +#include "rasodmg/marray.hh" +#include "rasodmg/gmarray.hh" +#include "rasodmg/iterator.hh" +#include "rasodmg/oqlquery.hh" + + +#include "rviewUtils.hh" + + + +class r_Fast_Base_Scale; + +class rviewDatabase +{ + public: + + rviewDatabase(void); + ~rviewDatabase(void); + int open(const char *srvname, int srvport, const char *dbname, + const char *username, const char *userpassword); + void close(void); + bool isOpen(void); + int createCollection(const char *collName, rviewBaseType bt); + int deleteCollection(const char *collName); + int lookupCollection(collection_desc *desc); + static r_Ref<r_GMarray> getScaledObject(r_Fast_Base_Scale *scaler, const r_Minterval &trimDom, double scale); + r_Fast_Base_Scale *lookupScaledObject(collection_desc *desc, double scale); + int insertObject(const char *collName, r_Ref<r_GMarray> mddObj, r_Minterval *domain=NULL); + int executeQuery(collection_desc *desc, const char *query, r_Ref<r_GMarray> *updateMdd=NULL, bool showProgress=TRUE); + int getMinterval(r_Minterval &dom, const char *collName, const double *loid=NULL); + const r_Database *getDatabase(void) const; + int getErrorInfo(int &line, int &col) const; + + + protected: + + int collectionToDesc(r_Set<r_Ref<r_GMarray> > &mddColl, collection_desc *desc); + + int ensureDatabase(void); + + DynamicString server; + int port; + DynamicString database; + DynamicString username; + DynamicString userpassword; + DynamicString lastTransferParams; + DynamicString lastStorageParams; + r_Data_Format lastTransferFormat; + r_Data_Format lastStorageFormat; + + bool dbOpen; + r_Database dbase; + // For errors in queries + int line, col; +}; + +#endif diff --git a/applications/rview/rviewDisplay.cpp b/applications/rview/rviewDisplay.cpp new file mode 100644 index 0000000..bf6d4f4 --- /dev/null +++ b/applications/rview/rviewDisplay.cpp @@ -0,0 +1,1005 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * Base class for all object viewers (rviewImage, rviewChart, rviewTable + * and rviewSound). Provides a frame with standard control widgets and + * menus, functions for parsing/advancing the projection string and + * initializing protected variables according to the MDD object that will + * be displayed. + * + * COMMENTS: + * None + */ + + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + + +#include <string.h> +#include <iostream.h> +#include <ctype.h> + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + +#include "raslib/rmdebug.hh" + +#include "rviewTypes.hh" + +#include "labelManager.hh" + +#include "rviewUtils.hh" +#include "rviewDisplay.hh" +#include "rviewPrefs.hh" +#include "rviewApp.hh" + + + + +/* + * Need a slightly more sophisticated panel than the default one + */ +class rviewDisplayPanel : public wxPanel +{ + public: + rviewDisplayPanel(rviewDisplay *parent, int posx, int posy, int width, int height); + virtual void OnEvent(wxMouseEvent &mevt); + virtual void OnItemEvent(wxItem *item, wxMouseEvent &mevt); +}; + +rviewDisplayPanel::rviewDisplayPanel(rviewDisplay *parent, int posx, int posy, int width, int height) : + wxPanel((wxWindow*)parent, posx, posy, width, height) +{ +} + +void rviewDisplayPanel::OnEvent(wxMouseEvent &mevt) +{ + rviewDisplay *disp = (rviewDisplay*)(GetParent()); + disp->childMouseEvent(this, mevt); + wxPanel::OnEvent(mevt); +} + +void rviewDisplayPanel::OnItemEvent(wxItem *item, wxMouseEvent &mevt) +{ + rviewDisplay *disp = (rviewDisplay*)(GetParent()); + disp->childMouseEvent(item, mevt); + wxPanel::OnItemEvent(item, mevt); +} + + + + +/* + * Constants + */ + +// window dimensions +const int rviewDisplay::display_width = 300; +const int rviewDisplay::display_height = 400; +const int rviewDisplay::display_cnvborder = 10; +const int rviewDisplay::display_border = 8; +const int rviewDisplay::display_scrstep = 8; +const int rviewDisplay::display_pgstep = 8; +const int rviewDisplay::display_cheight = 60; +const int rviewDisplay::display_pjheight = 50; +const int rviewDisplay::display_pjwidth = 100; +const int rviewDisplay::display_pbwidth = 40; +const int rviewDisplay::display_pbheight = 30; +const int rviewDisplay::display_minwidth = 4*(rviewDisplay::display_pbwidth + rviewDisplay::display_border); + +// flags +const int rviewDisplay::display_flag_standalone = 1; +const int rviewDisplay::display_flag_update = 2; + +// others +const int rviewDisplay::fixedNumberOfMenus = 2; +const unsigned int rviewDisplay::viewBuffSize = 1024; +const char *rviewDisplay::viewFileExtension = "*.rvw"; + +const char *rviewDisplay::view_HeaderLine = "# rView viewer snapshot"; +const char *rviewDisplay::view_ViewerType = "viewerName"; +const char *rviewDisplay::view_ProjString = "projString"; +const char *rviewDisplay::view_WindowSize = "windowSize"; + + + +/* + * rviewDisplay members + */ + +// The global display counter. +int rviewDisplay::displayCounter=0; + +rviewDisplay::rviewDisplay(mdd_frame *mf, int es, unsigned int flags) : rviewFrame(NULL, "", 0, 0, display_width, display_height) +{ + int x, y, pw; + r_Object *mo; + char buffer[STRINGSIZE]; + + if ((flags & display_flag_standalone & display_flag_update) != 0) + RMDBGENTER(3, RMDebug::module_applications, "rviewDisplay", "rviewDisplay() [UA]") + else if ((flags & display_flag_standalone) != 0) + RMDBGENTER(3, RMDebug::module_applications, "rviewDisplay", "rviewDisplay() [A]") + else if ((flags & display_flag_update) != 0) + RMDBGENTER(3, RMDebug::module_applications, "rviewDisplay", "rviewDisplay() [U]") + + // Override by derived classes if an error occurs. + // Can't use virtual functions in constructors! + objectInitializedOK = TRUE; + closeViewerCalled = FALSE; + + displayFlags = flags; + mddObj = mf->mdd; extraSpace = es; + totalCtrlHeight = display_cheight + extraSpace; + rootTitle[0] = '\0'; + displayOperation = FALSE; // used to avoid reentrancy on resize + minViewX = 0; minViewY = 0; dimMode = -1; + + // Copy the current (global) display counter and increment it, thus creating a unique + // ID for each display window. + displayID = displayCounter++; + qwindowID = -1; // no query window + + projString[0] = '\0'; + + baseSize = (int)(mddObj->get_type_length()); + // Determine base type + mo = (r_Object*)(&(*mddObj)); + baseType = rviewGetBasetype(mo); + + interv = mddObj->spatial_domain(); + dimMDD = interv.dimension(); + + mBar = NULL; + ctrlPanel = NULL; project = NULL; + + GetClientSize(&x, &y); + + ctrlPanel = new rviewDisplayPanel(this, 0, 0, x, totalCtrlHeight); + ctrlPanel->SetLabelPosition(wxVERTICAL); + + x -= 2*display_border; pw = x; + if (pw > display_pjwidth) pw = display_pjwidth; + project = new rviewText(ctrlPanel); + projBut = new rviewButton(ctrlPanel); + projMinus = new rviewButton(ctrlPanel, "-"); + advance = new rviewText(ctrlPanel); + projPlus = new rviewButton(ctrlPanel, "+"); + typeMsg = new wxMessage(ctrlPanel, ""); + + // Have to duplicate this code bit. + sprintf(buffer, "%s: %s", lman->lookup("textBaseType"), rviewBaseTypes[baseType]); + typeMsg->SetLabel(buffer); + + frameWidth=-1; + frameWidth=-1; + + RMDBGEXIT(3, RMDebug::module_applications, "rviewDisplay", "rviewDisplay()"); +} + + +rviewDisplay::~rviewDisplay(void) +{ + if((displayFlags & display_flag_standalone & display_flag_update) != 0) + RMDBGEXIT(3, RMDebug::module_applications, "rviewDisplay", "~rviewDisplay() [UA]") + else if ((displayFlags & display_flag_standalone) != 0) + RMDBGEXIT(3, RMDebug::module_applications, "rviewDisplay", "~rviewDisplay() [A]") + else if ((displayFlags & display_flag_update) != 0) + RMDBGEXIT(3, RMDebug::module_applications, "rviewDisplay", "~rviewDisplay() [U]") + + // Update display dying, notify other frames (-> query windows) + if ((displayFlags & display_flag_update) != 0) + { + user_event ue; + ue.type = usr_update_closed; + ue.data = (void*)(&mddObj); + if (frameManager != NULL) + frameManager->broadcastUserEvent(ue); + } + // If standalone free all memory. + if ((displayFlags & display_flag_standalone) != 0) + { + mddObj.destroy(); + } +} + + +// notify the parent window that a viewer has been closed; +// called from viewer destructor. +void rviewDisplay::closeViewer(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewDisplay", "closeViewer()"); + + if (!closeViewerCalled) + { + if (parentFrame != NULL) + { + mdd_frame mf; + user_event ue; + mf.flags = getViewerType(); mf.mdd = mddObj; + ue.type = usr_viewer_closed; ue.data = (void*)(&mf); + parentFrame->userEvent(ue); + } + closeViewerCalled = TRUE; + } +} + + +const char *rviewDisplay::getFrameName(void) const +{ + return "rviewDisplay"; +} + +rviewFrameType rviewDisplay::getFrameType(void) const +{ + return rviewFrameTypeDisplay; +} + + +const r_Minterval &rviewDisplay::getVirtualDomain(void) const +{ + return interv; +} + + +// Must concentrate all functionality relying on virtual functions here rather than +// the constructor +int rviewDisplay::openViewer(void) +{ + int w, h, x, y; + char buffer[STRINGSIZE]; + + RMDBGONCE(3, RMDebug::module_applications, "rviewDisplay", "openViewer()"); + + mBar = new wxMenuBar; + + wxMenu *menu = new wxMenu; + menu->Append(MENU_DISP_DATA_INSERT, ""); + menu->Append(MENU_DISP_DATA_INSERTPRO, ""); + menu->Append(MENU_DISP_DATA_SAVE, ""); + fileMenuInitHook(menu); + menu->Append(MENU_DISP_DATA_CLOSE, ""); + sprintf(buffer, "&%s", lman->lookup("menDispData")); + mBar->Append(menu, buffer); + + menu = new wxMenu; + menu->Append(MENU_DISP_VIEW_SAVE, ""); + menu->Append(MENU_DISP_VIEW_LOAD, ""); + viewMenuInitHook(menu); + sprintf(buffer, "&%s", lman->lookup("menDispView")); + mBar->Append(menu, buffer); + + menuBarInitHook(); + + GetSize(&w, &h); + //lastWidth = w; lastHeight = h; + GetClientSize(&x, &y); + mbarHeight = h - y; + //cout << "mbar height " << mbarHeight << endl; + + SetMenuBar(mBar); + + newDBState(rmanClientApp::theApp()->ReadDBState()); + + return 0; +} + + +void rviewDisplay::setModeDimension(int dim) +{ + bool enable; + char adbuff[4]; + + dimMode = dim; sprintf(adbuff, "%d", dim); + advance->SetValue(adbuff); + enable = (dimMDD > dimMode); + projPlus->Enable(enable); projMinus->Enable(enable); advance->Enable(enable); +} + + +void rviewDisplay::label(void) +{ + char btbuf[STRINGSIZE]; + + mBar->SetLabel(MENU_DISP_DATA_INSERT, lman->lookup("menDispDataIsrt")); + mBar->SetLabel(MENU_DISP_DATA_INSERTPRO, lman->lookup("menDispDataIsrtPro")); + mBar->SetLabel(MENU_DISP_DATA_SAVE, lman->lookup("menDispDataSave")); + mBar->SetLabel(MENU_DISP_DATA_CLOSE, lman->lookup("menDispDataClose")); + mBar->SetLabelTop(0, lman->lookup("menDispData")); + + mBar->SetLabel(MENU_DISP_VIEW_SAVE, lman->lookup("menDispViewSave")); + mBar->SetLabel(MENU_DISP_VIEW_LOAD, lman->lookup("menDispViewLoad")); + mBar->SetLabelTop(1, lman->lookup("menDispView")); + + project->SetLabel(lman->lookup("textProjString")); + projBut->SetLabel(lman->lookup("textOK")); + + sprintf(btbuf, "%s: %s", lman->lookup("textBaseType"), rviewBaseTypes[baseType]); + typeMsg->SetLabel(btbuf); +} + + +void rviewDisplay::newDBState(bool dbstate) +{ + mBar->Enable(MENU_DISP_DATA_INSERT, dbstate); + mBar->Enable(MENU_DISP_DATA_INSERTPRO, dbstate); +} + + +int rviewDisplay::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (((&obj == (wxObject*)project) && (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND)) || ((&obj == (wxObject*)projBut) && (type == wxEVENT_TYPE_BUTTON_COMMAND))) + { + strcpy(projString, project->GetValue()); + newProjection(); + return 1; + } + if (((&obj == (wxObject*)projPlus) || (&obj == (wxObject*)projMinus)) && (type == wxEVENT_TYPE_BUTTON_COMMAND)) + { + if (advanceProjection((&obj == (wxObject*)projPlus) ? 1 : -1) != 0) + { + strcpy(projString, project->GetValue()); + newProjection(); + return 1; + } + } + return 0; +} + + +void rviewDisplay::OnSize(int w, int h) +{ + int x, y, pw, pos, minw, minh; + float tw, th; + + RMDBGONCE(3, RMDebug::module_applications, "rviewDisplay", "OnSize(" << w << ", " << h << " )"); + + if (!displayOperation) + { + project->GetTextExtent((const char*)(lman->lookup("textProjString")), &tw, &th, NULL, NULL, wxSWISS_FONT, FALSE); + minw = display_minwidth + ((int)tw); + if (minw < minViewX) minw = minViewX; + minh = totalCtrlHeight + minViewY + mbarHeight; + if ((w < minw) || (h < minh)) + { + // Avoid infinite loops / reentrancy + displayOperation = TRUE; + if (w < minw) w = minw; + if (h < minh) h = minh; + //cout << "resize display frame to " << w << ", " << h << " / min " << minViewX << ", " << minViewY << endl; + SetSize(-1, -1, w, h); + displayOperation = FALSE; + } + } + + GetClientSize(&x, &y); + + ctrlPanel->SetSize(0, 0, x, totalCtrlHeight); + pos = x - 3*display_pbwidth - display_border; + pw = pos - 3*display_border - display_pbwidth; + x -= 2*display_border; + project->SetSize(display_border, display_border, pw, display_pjheight); + y = display_border + (display_pjheight - display_pbheight); + projBut->SetSize(2*display_border + pw, y, display_pbwidth, display_pbheight); + projMinus->SetSize(pos, y, display_pbwidth, display_pbheight); + advance->SetSize(pos + display_pbwidth, y - display_border, display_pbwidth, 4*display_pbheight/3); + projPlus->SetSize(pos + 2*display_pbwidth, y, display_pbwidth, display_pbheight); + + // Default label font seems to be swiss font. Changing it doesn't appear to + // have any effect. + typeMsg->GetTextExtent((const char*)(typeMsg->GetLabel()), &tw, &th, NULL, NULL, wxSWISS_FONT, FALSE); + typeMsg->SetSize(display_border + x - tw, display_border, tw, th); +} + + + +void rviewDisplay::OnMenuCommand(int id) +{ + switch (id) + { + case MENU_DISP_DATA_INSERT: + rmanClientApp::theApp()->insertMDD(mddObj); + break; + case MENU_DISP_DATA_INSERTPRO: + { + r_Minterval useInterv(dimMDD); + int i; + + for (i=0; i<dimMDD; i++) + useInterv << r_Sinterval((r_Range)pt1[i], (r_Range)pt2[i]); + + rmanClientApp::theApp()->insertMDD(mddObj, NULL, &useInterv); + } + break; + case MENU_DISP_DATA_SAVE: + { + char *prefDir = (char*)(prefs->filePath.ptr()); + char *s = wxFileSelector(lman->lookup("saveData"), (::wxDirExists(prefDir)) ? prefDir : NULL, NULL, NULL, "*", 0, this); + + if (s) + { + FILE *fp; + size_t dataSize = (size_t)baseSize; + int i; + + prefs->filePath = ::wxPathOnly(s); + + for (i=0; i<dimMDD; i++) + { + dataSize *= (interv[i].high() - interv[i].low() + 1); + } + if ((fp = fopen(s, "wb")) == NULL) + { + char buffer[STRINGSIZE]; + sprintf(buffer, "%s %s", lman->lookup("errorFileOpen"), s); + rviewErrorbox::reportError(buffer, rviewDisplay::getFrameName(), "OnMenuCommand"); + } + else + { + if (fwrite(mddObj->get_array(), 1, dataSize, fp) != dataSize) + { + char buffer[STRINGSIZE]; + sprintf(buffer, "%s %s", lman->lookup("errorFileWrite"), s); + rviewErrorbox::reportError(buffer, rviewDisplay::getFrameName(), "OnMenuCommand"); + } + fclose(fp); + } + } + } + break; + case MENU_DISP_DATA_CLOSE: + prepareToDie(); this->Close(TRUE); + break; + case MENU_DISP_VIEW_SAVE: + doSaveView(); + break; + case MENU_DISP_VIEW_LOAD: + doLoadView(); + break; + default: break; + } +} + + + + +int rviewDisplay::userEvent(const user_event &ue) +{ + if (ue.type == usr_mdd_dying) + { + if (*((r_Ref<r_GMarray>*)(ue.data)) == mddObj) + { + prepareToDie(); + this->Close(TRUE); + return 1; + } + } + if ((ue.type == usr_db_opened) || (ue.type == usr_db_closed)) + { + newDBState((ue.type == usr_db_opened)); + return 1; + } + if (ue.type == usr_close_viewers) + { + Close(TRUE); + return 1; + } + return 0; +} + + + +// Virtual function, overload by derived classes if you need to. +void rviewDisplay::prepareToDie(void) +{ +} + +// Virtual function, overload by derived classes +int rviewDisplay::newProjection(void) +{ + return 0; +} + +int rviewDisplay::fileMenuInitHook(wxMenu *menu) +{ + return 0; +} + +int rviewDisplay::viewMenuInitHook(wxMenu *menu) +{ + return 0; +} + +int rviewDisplay::menuBarInitHook(void) +{ + return 0; +} + + + +// Support function for advanceProjection +const char *rviewDisplay::skipIndexMapping(const char *s) +{ + const char *b, *d; + + b = s+1; + while ((*b == ' ') || (*b == '\t')) b++; + d = b; + while ((*b >= '0') && (*b <= '9')) b++; + if (b == d) return NULL; + while ((*b == ' ') || (*b == '\t')) b++; + if (*b != ']') return NULL; + return b+1; +} + +/* + * Change the chosen fixed coordinate according to the advancement mode: + * relative: coord += direction + * absolute: coord = direction + * reset: if (direction <= 0) then to start else to end + */ +int rviewDisplay::advanceProjection(int direction, int advmode) +{ + const char *b, *d; + int cordnt; + r_Range value; + char tailbuff[STRINGSIZE]; + unsigned int freeDims=0; + const char **dimDescs; + const r_Minterval useIv = getVirtualDomain(); + + if ((dimDescs = new const char*[dimMDD]) == NULL) + return 0; + + // Find free dimensions in projection string + strcpy(projString, project->GetValue()); + b = projString; value = 0; + while (*b != '\0') + { + cordnt = 0; // indicates it's a number + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == '\0') break; + if (value < dimMDD) dimDescs[value] = b; + if (*b == '*') // not a number + { + b++; cordnt=1; + } + else + { + if ((*b == '-') || (*b == '+')) b++; + d = b; + while ((*b >= '0') && (*b <= '9')) b++; + if (b == d) + { + delete [] dimDescs; return 0; + } + } + while (*b == ' ') b++; + if (*b == '[') + { + if ((b = skipIndexMapping(b)) == NULL) + { + delete [] dimDescs; return 0; + } + } + else if (*b == ':') + { + b++; + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == '*') b++; + else + { + if ((*b == '-') || (*b == '+')) b++; + d = b; + while ((*b >= '0') && (*b <= '9')) b++; + if (b == d) + { + delete [] dimDescs; return 0; + } + } + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == '[') + { + if ((b = skipIndexMapping(b)) == NULL) + { + delete [] dimDescs; return 0; + } + } + } + else if (cordnt == 0) + { + freeDims |= (1<<value); + } + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == ',') b++; + value++; + } + if ((value != dimMDD) || (freeDims == 0)) + { + delete [] dimDescs; return 0; + } + cordnt = atoi(advance->GetValue()); + // Is the specified coordinate a free one? + if ((cordnt < 0) || (cordnt >= dimMDD) || ((freeDims & (1<<cordnt)) == 0)) + { + for (cordnt=0; cordnt < (int)(8*sizeof(unsigned int)); cordnt++) + { + // freeDims is != 0 here. + if ((freeDims & (1<<cordnt)) != 0) break; + } + sprintf(tailbuff, "%d", cordnt); + advance->SetValue(tailbuff); + } + b = dimDescs[cordnt]; + value = atoi(b); + + // Save tail + if (cordnt < dimMDD-1) + sprintf(tailbuff, ", %s", dimDescs[cordnt+1]); + else + tailbuff[0] = '\0'; + + // update the coordinate according to the advancement mode + if (advmode == display_advmode_relative) + value += direction; + else if (advmode == display_advmode_absolute) + value = direction; + else if (advmode == display_advmode_reset) + { + if (direction <= 0) + value = useIv[cordnt].low(); + else + value = useIv[cordnt].high(); + } + + if ((value >= useIv[cordnt].low()) && (value <= useIv[cordnt].high())) + { + // this cast here is OK, doesn't compromise the function interface. + sprintf((char*)b, "%ld%s", value, tailbuff); + project->SetValue(projString); + delete [] dimDescs; + return 1; + } + delete [] dimDescs; + return 0; +} + + + +void rviewDisplay::setDisplayTitle(const char *title) +{ + char titleString[STRINGSIZE]; + char *b; + + if (title != NULL) + { + strcpy(rootTitle, title); + } + // Create the title postfix string + strcpy(titleString, rootTitle); + b = titleString + strlen(titleString); + if ((displayFlags & (display_flag_standalone | display_flag_update)) != 0) + { + *b++ = ' '; *b++ = '['; + if ((displayFlags & display_flag_standalone) != 0) + b += sprintf(b, "StAln"); + if ((displayFlags & display_flag_update) != 0) + { + b += sprintf(b, "U"); + if (qwindowID != -1) + b += sprintf(b, "d%dq%d", displayID, qwindowID); + *b++ = ' '; + } + *b++ = ']'; + } + *b++ = '\0'; + SetTitle(titleString); +} + + + +void rviewDisplay::noLongerUpdate(void) +{ + displayFlags &= ~display_flag_update; + setDisplayTitle(); +} + + + + +int rviewDisplay::getIdentifier(void) const +{ + return displayID; +} + + +int rviewDisplay::getDisplayCounter(void) const +{ + return displayCounter; +} + + + +void rviewDisplay::setQueryWindow(int qwid) +{ + qwindowID = qwid; + setDisplayTitle(); +} + + + +void rviewDisplay::setMinimumViewerSize(int w, int h) +{ + minViewX = w; minViewY = h; +} + + + +int rviewDisplay::doSaveView(void) +{ + char *name = ::wxFileSelector(lman->lookup("saveView"), (char*)(::wxDirExists(prefs->filePath.ptr()) ? prefs->filePath.ptr() : NULL), NULL, NULL, (char*)viewFileExtension); + + if (name != NULL) + { + FILE *fp = fopen(name, "w"); + if (fp != NULL) + { + fprintf(fp, "%s\n\n", view_HeaderLine); + int status = saveView(fp); + fclose(fp); + return status; + } + return -1; + } + return 0; +} + + +int rviewDisplay::doLoadView(void) +{ + char *name = ::wxFileSelector(lman->lookup("loadView"), (char*)(::wxDirExists(prefs->filePath.ptr()) ? prefs->filePath.ptr() : NULL), NULL, NULL, (char*)viewFileExtension); + + if (name != NULL) + { + FILE *fp = fopen(name, "r"); + if (fp != NULL) + { + int status = parseViewFile(fp); + fclose(fp); + loadViewFinished(); + newProjection(); + return status; + } + return -1; + } + return 0; +} + + +int rviewDisplay::parseViewFile(FILE *fp) +{ + char *buffer = new char[viewBuffSize]; + unsigned int line = 0; + int status = -1; + + if (fgets(buffer, viewBuffSize, fp) != NULL) + { + int len = strlen(buffer); + line++; + if (strncmp(view_HeaderLine, buffer, len-1) == 0) + { + while (!feof(fp)) + { + if (fgets(buffer, viewBuffSize, fp) == NULL) + break; + line++; + char *b = buffer; + while (isspace((unsigned int)(*b))) b++; + if (*b != '\0') + { + char *key = b; + while (isalnum((unsigned int)(*b))) b++; + char *kend = b; + while (isspace((unsigned int)(*b))) b++; + if (*b != '=') + { + cerr << "Missing '=' at line " << line << endl; + } + else + { + *kend = '\0'; b++; + while (isspace((unsigned int)(*b))) b++; + len = strlen(b); + while ((len > 0) && (isspace((unsigned int)(b[len-1])))) b[--len] = '\0'; + //cout << "key " << key << ", value " << b << endl; + if (readView(key, b) < 0) + { + delete [] buffer; + return -1; + } + } + } + } + status = 0; + } + } + delete [] buffer; + return status; +} + + +void rviewDisplay::loadViewFinished(void) +{ + project->SetValue(projString); +} + + +void rviewDisplay::writeViewKey(FILE *fp, const char *key) +{ + fprintf(fp, "%s\t=\t", key); +} + + +void rviewDisplay::writeViewParam(FILE *fp, const char *key, const char *value) +{ + fprintf(fp, "%s\t=\t%s\n", key, value); +} + + +void rviewDisplay::writeViewParam(FILE *fp, const char *key, long value) +{ + fprintf(fp, "%s\t=\t%ld\n", key, value); +} + + +void rviewDisplay::writeViewParam(FILE *fp, const char *key, double value) +{ + fprintf(fp, "%s\t=\t%.15f\n", key, value); +} + + +void rviewDisplay::writeViewParam(FILE *fp, const char *key, unsigned int num, const long *values) +{ + fprintf(fp, "%s\t=\t%ld", key, values[0]); + for (unsigned int i=1; i<num; i++) + fprintf(fp, ", %ld", values[i]); + fprintf(fp, "\n"); +} + + +void rviewDisplay::writeViewParam(FILE *fp, const char *key, unsigned int num, const double *values) +{ + fprintf(fp, "%s\t=\t%.15f", key, values[0]); + for (unsigned int i=1; i<num; i++) + fprintf(fp, ", %.15f", values[i]); + fprintf(fp, "\n"); +} + + +int rviewDisplay::readVector(const char *value, unsigned int num, long *values) +{ + unsigned int i = 0; + const char *b, *rest; + + b = value; + while (i < num) + { + while (isspace((unsigned int)(*b))) b++; + values[i] = strtol(b, (char**)&rest, 10); + if (rest == b) + return -1; + b = rest; i++; + while (isspace((unsigned int)(*b))) b++; + if (*b == ',') b++; + } + return 0; +} + + +int rviewDisplay::readVector(const char *value, unsigned int num, double *values) +{ + unsigned int i = 0; + const char *b, *rest; + + b = value; + while (i < num) + { + while (isspace((unsigned int)(*b))) b++; + values[i] = strtod(b, (char**)&rest); + if (rest == b) + return -1; + b = rest; i++; + while (isspace((unsigned int)(*b))) b++; + if (*b == ',') b++; + } + return 0; +} + + +int rviewDisplay::saveView(FILE *fp) +{ + writeViewParam(fp, view_ViewerType, getFrameName()); + writeViewParam(fp, view_ProjString, projString); + GetSize(&frameWidth, &frameHeight); + long wsize[2]; + wsize[0] = (long)frameWidth; wsize[1] = (long)frameHeight; + writeViewParam(fp, view_WindowSize, 2, wsize); + return 0; +} + + +int rviewDisplay::readView(const char *key, const char *value) +{ + if (strcmp(key, view_ViewerType) == 0) + { + if (strcmp(value, getFrameName()) != 0) + { + char buffer[STRINGSIZE]; + sprintf(buffer, "%s: %s", lman->lookup("errorViewType"), value); + rviewErrorbox::reportError(buffer, getFrameName(), "readView"); + return -1; + } + return 1; + } + else if (strcmp(key, view_ProjString) == 0) + { + strcpy(projString, value); + return 1; + } + else if (strcmp(key, view_WindowSize) == 0) + { + long wsize[2]; + if (readVector(value, 2, wsize) == 0) + { + SetSize(wsize[0], wsize[1]); + return 1; + } + } + return 0; +} diff --git a/applications/rview/rviewDisplay.hh b/applications/rview/rviewDisplay.hh new file mode 100644 index 0000000..7587d06 --- /dev/null +++ b/applications/rview/rviewDisplay.hh @@ -0,0 +1,224 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * Purpose: + * + * Base class for all object viewers (rviewImage, rviewChart, rviewTable + * and rviewSound). Provides a frame with standard control widgets and + * menus, functions for parsing/advancing the projection string and + * initializing protected variables according to the MDD object that will + * be displayed. + * + * COMMENTS: + * None + */ + + +#ifndef _RVIEW_DISPLAY_H_ +#define _RVIEW_DISPLAY_H_ + + + + +#include "rasodmg/ref.hh" +#include "rasodmg/marray.hh" +#include "rasodmg/gmarray.hh" + +#include "labelManager.hh" +#include "rviewUtils.hh" + + + + +/* + * A window containing an display mode + controls + */ +class rviewDisplay: public rviewFrame +{ + public: + + rviewDisplay(mdd_frame *mf, int es, unsigned int flags=0); + virtual ~rviewDisplay(void); + + // must use separate call because no virtual functions beyond level i can + // be called in a constructor of level i. This became necessary when the + // rviewImage class was split up. Returns 0 for OK, -1 for error. + virtual int openViewer(void); + // Same reason for virtual calls (getViewerType()) in the destructor; this + // functions is called from a viewer's destructor to notify its parent of the + // close event. + void closeViewer(void); + + virtual void label(); + virtual int process(wxObject &obj, wxEvent &evt); + + virtual void OnSize(int w, int h); + virtual void OnMenuCommand(int id); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + virtual int getViewerType(void) const = 0; + + // Needs to process user events + virtual int userEvent(const user_event &ue); + // Notify derived classes that they should get ready to die. This is needed + // for stuff like images with movie-playback running. + virtual void prepareToDie(void); + // Notify derived classes that the projection has changed + virtual int newProjection(void); + // get the minterval to use with the projection string + virtual const r_Minterval &getVirtualDomain(void) const; + + // Called when an update object has been replaced with a new one + void noLongerUpdate(void); + // Returns the display ID (used in for update displays) + int getIdentifier(void) const; + int getDisplayCounter(void) const; + // sets the ID of the query window in case it's an update object + void setQueryWindow(int qwindowID); + + // viewer-related constants + // Default width and height of display window + static const int display_width; + static const int display_height; + // Space reserved around the canvas + static const int display_cnvborder; + // Borders used in display window + static const int display_border; + // Scrollbar steps + static const int display_scrstep; + // Page stepping + static const int display_pgstep; + // Height of control area + static const int display_cheight; + // Height of projection widget + static const int display_pjheight; + // Maximum width of projection widget + static const int display_pjwidth; + // Projection OK-button dimensions + static const int display_pbwidth; + static const int display_pbheight; + // Minimum display window size (to avoid X errors) + static const int display_minwidth; + + // Display flags + static const int display_flag_standalone; + static const int display_flag_update; + + // modes for advanceProjection() + enum display_advmode_e { + display_advmode_relative, + display_advmode_absolute, + display_advmode_reset + }; + + + protected: + + // to allow derived functions to add stuff to the file menu before the quit item + virtual int fileMenuInitHook(wxMenu *menu); + // to allow derived functions to add stuff to the view menu + virtual int viewMenuInitHook(wxMenu *menu); + // for appending menus to the menu bar + virtual int menuBarInitHook(void); + + // Called by the derived classes to (un)grey certain widgets + void setModeDimension(int dim); + // Called by derived classes to set minimum size of viewer window + void setMinimumViewerSize(int w, int h); + // Advances projection (+/- buttons) + const char *skipIndexMapping(const char *s); + int advanceProjection(int direction, int advmode=display_advmode_relative); + void newDBState(bool dbstate); + void setDisplayTitle(const char *title=NULL); + // view management + int doSaveView(void); + int doLoadView(void); + int parseViewFile(FILE *fp); + static void writeViewKey(FILE *fp, const char *key); + static void writeViewParam(FILE *fp, const char *key, const char *value); + static void writeViewParam(FILE *fp, const char *key, long value); + static void writeViewParam(FILE *fp, const char *key, double value); + static void writeViewParam(FILE *fp, const char *key, unsigned int num, const long *values); + static void writeViewParam(FILE *fp, const char *key, unsigned int num, const double *values); + static int readVector(const char *value, unsigned int num, long *values); + static int readVector(const char *value, unsigned int num, double *values); + + // save the current view to a file descriptor + virtual int saveView(FILE *fp); + // read a parameter from a view file line; returns 0 if unknown key, 1 otherwise + virtual int readView(const char *key, const char *value); + // loading a new view is finished, allow updating of displays before the redraw + virtual void loadViewFinished(void); + + unsigned int displayFlags; + r_Ref<r_GMarray> mddObj; + r_Minterval interv; + r_Point pt1, pt2, mapIndex; + int dimMDD, dimMode, baseSize; + rviewBaseType baseType; + wxMenuBar *mBar; + wxPanel *ctrlPanel; + rviewText *project, *advance; + wxMessage *typeMsg; + rviewButton *projBut, *projPlus, *projMinus; + char projString[STRINGSIZE]; + char rootTitle[STRINGSIZE]; + int extraSpace; + int totalCtrlHeight; + int displayID; + int qwindowID; + int minViewX, minViewY, mbarHeight; + bool displayOperation; + bool objectInitializedOK; + // number of menus that are always on the menu bar + static const int fixedNumberOfMenus; + + + private: + + // This variable avoids closeViewer() being called more than once + // in case there are viewer classes derived from a common, non- + // abstract base class. + bool closeViewerCalled; + + // This global variable is incremented every time a display window is + // opened. It is used to init the displayID member to identify display + // windows by a number. + static int displayCounter; + + // file extension of view file + static const char *viewFileExtension; + + // size of buffer for view loading + static const unsigned int viewBuffSize; + + // view parameter keywords + static const char *view_HeaderLine; + static const char *view_ViewerType; + static const char *view_ProjString; + static const char *view_WindowSize; +}; + +#endif diff --git a/applications/rview/rviewIO.cpp b/applications/rview/rviewIO.cpp new file mode 100644 index 0000000..538dc9f --- /dev/null +++ b/applications/rview/rviewIO.cpp @@ -0,0 +1,718 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView data exchange interface for storing MDD to the filing system + * or loading from it. Currently supported operations are: + * - Load TIFF file. + * - Save wxPixmap as TIFF to file. + * - Load VFF file (if RVIEW_USE_VFF supported; use conversion module) + * + * COMMENTS: + * None + */ + + + +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + + +#include <stdio.h> +#include <stdlib.h> + +#include <iostream.h> + + +#include "wx_pixmap.h" + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + +#include "raslib/rmdebug.hh" +#include "raslib/basetype.hh" +#include "raslib/parseparams.hh" + +#include "rviewIO.hh" +#include "rviewTypes.hh" + + +#include "tiffio.h" + + +#ifdef RVIEW_USE_VFF +/* + * The use of the conversion module might cause problems, therefore VFF is + * optional; if you have problems with it, build rView without VFF support. + */ +#include "conversion/vff.hh" +#endif + + + + +r_Parse_Params *rviewIO::dfltParams = NULL; +char *rviewIO::tiffCompStr = NULL; +int rviewIO::tiffCompression = COMPRESSION_DEFLATE; + +// Base type structure formats +const char rviewIO::structure_format_mono[] = "marray <bool, [0:%d,0:%d]>"; +const char rviewIO::structure_format_grey[] = "marray <char, [0:%d,0:%d]>"; +const char rviewIO::structure_format_rgb[] = "marray <struct { char red, char green, char blue }, [0:%d,0:%d]>"; + +const char rviewIO::structure_format_cube8[] = "marray <char, [%d:%d,%d:%d,%d:%d]>"; +const char rviewIO::structure_format_cube16[] = "marray <ushort, [%d:%d,%d:%d,%d:%d]>"; +const char rviewIO::structure_format_cube32[] = "marray <uint, [%d:%d,%d:%d,%d:%d]>"; + +// parameters +const char rviewIO::param_KeyTiffComp[] = "comptype"; +const char rviewIO::param_TiffCompNone[] = "none"; +const char rviewIO::param_TiffCompPack[] = "packbits"; +const char rviewIO::param_TiffCompLZW[] = "lzw"; +const char rviewIO::param_TiffCompZLib[] = "deflate"; +const char rviewIO::param_TiffCompJPEG[] = "jpeg"; + + + +rviewIO::rviewIO(void) +{ +} + + +rviewIO::~rviewIO(void) +{ +} + + +void rviewIO::ensureParams(void) +{ + if (dfltParams == NULL) + { + tiffCompStr = NULL; + tiffCompression = COMPRESSION_DEFLATE; + + dfltParams = new r_Parse_Params; + dfltParams->add(param_KeyTiffComp, &tiffCompStr, r_Parse_Params::param_type_string); + } +} + + +void rviewIO::processParams(const char *params) +{ + ensureParams(); + dfltParams->process(params); + tiffCompression = COMPRESSION_DEFLATE; + if (tiffCompStr != NULL) + { + if (strcasecmp(tiffCompStr, param_TiffCompNone) == 0) + tiffCompression = COMPRESSION_NONE; + else if (strcasecmp(tiffCompStr, param_TiffCompPack) == 0) + tiffCompression = COMPRESSION_PACKBITS; + else if (strcasecmp(tiffCompStr, param_TiffCompLZW) == 0) + tiffCompression = COMPRESSION_LZW; + else if (strcasecmp(tiffCompStr, param_TiffCompZLib) == 0) + tiffCompression = COMPRESSION_DEFLATE; + else if (strcasecmp(tiffCompStr, param_TiffCompJPEG) == 0) + tiffCompression = COMPRESSION_JPEG; + } +} + + +void rviewIO::terminate(void) +{ + if (dfltParams != NULL) + { + delete dfltParams; + dfltParams = NULL; + } + + if (tiffCompStr != NULL) + { + delete [] tiffCompStr; + tiffCompStr = NULL; + } +} + + +const char *rviewIO::getExtension(const char *filename) +{ + const char *b, *ext; + + b = filename; ext = NULL; + while (*b != '\0') + { + if (*b == '.') + ext = b+1; + b++; + } + return ext; +} + +int rviewIO::isTIFF(const char *filename) +{ + const char *ext = getExtension(filename); + + // no extension ==> unrecognized + if (ext != NULL) + { + if ((strcasecmp(ext, "tif") == 0) || (strcasecmp(ext, "tiff") == 0)) + return 1; + } + return 0; +} + + +int rviewIO::loadTIFF(const char *filename, r_Ref<r_GMarray> &mddObj, const char *params) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewIO", "loadTIFF()"); + + TIFF* tif; + uint32 width, height, x, y; + uint16 bps, spp, planarc, photom; + int depth, stepx, stepy; + r_Minterval interv(2); + r_Point prun(2); + tdata_t buffer; + unsigned char *b; + char structure[STRINGSIZE]=""; + + if ((tif = TIFFOpen(filename, "r")) == NULL) + return RVIEW_IO_NOTFOUND; + + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); + TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bps); + TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); + TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planarc); + TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photom); + + if ((width < 1) || (height < 1)) return 0; + + RMDBGMIDDLE(3, RMDebug::module_applications, "rviewIO", "loadTIFF() Width " << width << ", height " << height << ", bps " << bps << ", spp " << spp << ", planarconfig " << planarc); + + depth = bps * spp; + + interv << r_Sinterval(r_Range(0), r_Range(width-1)) << r_Sinterval(r_Range(0), r_Range(height-1)); + + if ((buffer = _TIFFmalloc(TIFFScanlineSize(tif))) == NULL) + { + TIFFClose(tif); return RVIEW_IO_MEMORY; + } + + switch (depth) + { + case 1: + { + r_Ref<r_Marray<r_Boolean> > mddPtr = new r_Marray<r_Boolean>(interv); + r_Boolean *imgLine, *imgPtr; + r_Boolean min, max; + char val=0; + int mask; + + // Adjust the photometric interpretation. Later on 0 will be interpreted as black. + if (photom == PHOTOMETRIC_MINISWHITE) + { + min = 1; max = 0; + } + else + { + min = 0; max = 1; + } + stepx = &((*mddPtr)[r_Point(1,0)]) - &((*mddPtr)[r_Point(0,0)]); + stepy = &((*mddPtr)[r_Point(0,1)]) - &((*mddPtr)[r_Point(0,0)]); + imgLine = (r_Boolean*)(mddPtr->get_array()); + for (y=0; y<height; y++, imgLine += stepy) + { + if (planarc == PLANARCONFIG_CONTIG) + { + TIFFReadScanline(tif, buffer, y); + } +#ifdef FILLORDER_MSB2LSB + mask = 0; +#else + mask = 0x100; +#endif + imgPtr = imgLine; b = (unsigned char*)buffer; + for (x=0; x<width; x++, imgPtr += stepx) + { +#ifdef FILLORDER_MSB2LSB + if (mask == 0) {val = *b++; mask = 0x80;} + *imgPtr = ((val & mask) == 0) ? min : max; + mask >>= 1; +#else + if (mask == 0x100) {val = *b++; mask = 1;} + *imgPtr = ((val & mask) == 0) ? min : max; + mask <<= 1; +#endif + } + } + mddPtr->set_type_by_name(rviewTypeNames[rbt_bool][2-1]); + // Init base structure (hack, but better than leaving it undefined) + sprintf(structure, structure_format_mono, width-1, height-1); + mddObj = (r_Ref<r_GMarray>)mddPtr; + } + break; + case 8: + { + int paletteIsGrey = 0; + + uint16 *reds, *greens, *blues; + + TIFFGetField(tif, TIFFTAG_COLORMAP, &reds, &greens, &blues); + if (photom == PHOTOMETRIC_PALETTE) + { + for (x=0; x<256; x++) + { + if ((reds[x] != greens[x]) || (greens[x] != blues[x])) break; + } + if (x >= 256) paletteIsGrey = 1; + } + if ((photom == PHOTOMETRIC_PALETTE) && (paletteIsGrey == 0)) + { + r_Ref<r_Marray<RGBPixel> > mddPtr = new r_Marray<RGBPixel>(interv); + RGBPixel *imgLine, *imgPtr; + + stepx = &((*mddPtr)[r_Point(1,0)]) - &((*mddPtr)[r_Point(0,0)]); + stepy = &((*mddPtr)[r_Point(0,1)]) - &((*mddPtr)[r_Point(0,0)]); + imgLine = (RGBPixel*)(mddPtr->get_array()); + for (y=0; y<height; y++, imgLine += stepy) + { + if (planarc == PLANARCONFIG_CONTIG) + { + TIFFReadScanline(tif, buffer, y); + } + imgPtr = imgLine; b = (unsigned char*)buffer; + for (x=0; x<width; x++, imgPtr += stepx, b++) + { + imgPtr->red = (reds[*b] >> 8); + imgPtr->green = (greens[*b] >> 8); + imgPtr->blue = (blues[*b] >> 8); + } + } + mddPtr->set_type_by_name(rviewTypeNames[rbt_rgb][2-1]); + sprintf(structure, structure_format_rgb, width-1, height-1); + mddObj = (r_Ref<r_GMarray>)mddPtr; + } + else + { + r_Ref<r_Marray<r_Char> > mddPtr = new r_Marray<r_Char>(interv); + r_Char *imgLine, *imgPtr; + r_Char transTab[256]; + + if (paletteIsGrey != 0) + { + for (x=0; x<256; x++) transTab[x] = (reds[x] >> 8); + } + else + { + // Build a translation table depending on the photometric interpretation + if (photom == PHOTOMETRIC_MINISWHITE) + { + for (x=0; x<256; x++) transTab[x] = 255-x; + } + else // default to minisblack + { + for (x=0; x<256; x++) transTab[x] = x; + } + } + + stepx = &((*mddPtr)[r_Point(1,0)]) - &((*mddPtr)[r_Point(0,0)]); + stepy = &((*mddPtr)[r_Point(0,1)]) - &((*mddPtr)[r_Point(0,0)]); + imgLine = (r_Char*)(mddPtr->get_array()); + for (y=0; y<height; y++, imgLine += stepy) + { + if (planarc == PLANARCONFIG_CONTIG) + { + TIFFReadScanline(tif, buffer, y); + } + imgPtr = imgLine; b = (unsigned char*)buffer; + for (x=0; x<width; x++, imgPtr += stepx, b++) + { + *imgPtr = transTab[*b]; + } + } + mddPtr->set_type_by_name(rviewTypeNames[rbt_char][2-1]); + sprintf(structure, structure_format_grey, width-1, height-1); + mddObj = (r_Ref<r_GMarray>)mddPtr; + } + } + break; + case 24: + { + r_Ref<r_Marray<RGBPixel> > mddPtr = new r_Marray<RGBPixel>(interv); + RGBPixel *imgLine, *imgPtr; + + stepx = &((*mddPtr)[r_Point(1,0)]) - &((*mddPtr)[r_Point(0,0)]); + stepy = &((*mddPtr)[r_Point(0,1)]) - &((*mddPtr)[r_Point(0,0)]); + imgLine = (RGBPixel*)(mddPtr->get_array()); + //cout << "stepx=" << stepx << ", stepy=" << stepy << endl; + for (y=0; y<height; y++, imgLine += stepy) + { + if (planarc == PLANARCONFIG_CONTIG) + { + TIFFReadScanline(tif, buffer, y); + } + imgPtr = imgLine; b = (unsigned char*)buffer; + for (x=0; x<width; x++, imgPtr += stepx, b+=3) + { + imgPtr->red = b[0]; + imgPtr->green = b[1]; + imgPtr->blue = b[2]; + } + } + mddPtr->set_type_by_name(rviewTypeNames[rbt_rgb][2-1]); + sprintf(structure, structure_format_rgb, width-1, height-1); + mddObj = (r_Ref<r_GMarray>)mddPtr; + } + break; + default: return RVIEW_IO_FORMAT; + } + + if (structure[0] != 0) + mddObj->set_type_structure(structure); + + _TIFFfree(buffer); + + TIFFClose(tif); + + RMDBGEXIT(3, RMDebug::module_applications, "rviewImage", "~rviewImage() Created MDD object of domain " << mddObj->spatial_domain()); + + return RVIEW_IO_OK; +} + + +int rviewIO::saveTIFF(const char *filename, r_Ref<r_GMarray> &mddPtr, const char *params) +{ + cout << "rviewIO::saveTIFF() not implemented" << endl; + return RVIEW_IO_UNSUPP; +} + + + +/* + * Save a wxPixmap as a TIFF image + */ + +int rviewIO::PixmapToTIFF(wxPixmap *pixmap, const char *filename, const char *params) +{ + TIFF *tif; + int depth, pitch; + uint32 width, height; + uint8 *srcLine; + uint32 i; + + depth = pixmap->getDepth(); pitch = pixmap->getPitch(); + width = (uint32)(pixmap->getWidth()); height = (uint32)(pixmap->getHeight()); + + if ((tif = TIFFOpen(filename, "w")) == NULL) + { + char buffer[STRINGSIZE]; + + sprintf(buffer, "%s %s.\n", lman->lookup("errorFileOpen"), filename); + rviewErrorbox eb(buffer); eb.activate(); + return -1; + } + + processParams(params); + + TIFFSetField(tif, TIFFTAG_ARTIST, "rView"); + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(tif, TIFFTAG_COMPRESSION, tiffCompression); + TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); + TIFFSetField(tif, TIFFTAG_XRESOLUTION, 90.0); + TIFFSetField(tif, TIFFTAG_YRESOLUTION, 90.0); + + srcLine = (uint8*)(pixmap->getData()); + + switch (depth) + { + case 1: + { + wxColour *pal; + + pal = pixmap->getPalette(); + if (pal == NULL) + { + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + } + else + { + if ((int)(pal[0].Red()+pal[0].Green()+pal[0].Blue()) < (int)(pal[1].Red()+pal[1].Green()+pal[1].Blue())) + { + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + } + else + { + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE); + } + } + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 1); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + for (i=0; i<height; i++, srcLine += pitch) + { + if (TIFFWriteScanline(tif, (tdata_t)srcLine, i, 0) < 0) break; + } + } + break; + case 8: + { + wxColour *pal; + + pal = pixmap->getPalette(); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + if (pal == NULL) + { + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + } + else + { + uint16 reds[256], greens[256], blues[256]; + + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE); + for (i=0; i<256; i++) + { + reds[i] = ((pal[i].Red() << 16) - 1) / 255; + greens[i] = ((pal[i].Green() << 16) - 1) / 255; + blues[i] = ((pal[i].Blue() << 16) -1) / 255; + } + TIFFSetField(tif, TIFFTAG_COLORMAP, reds, greens, blues); + } + for (i=0; i<height; i++, srcLine += pitch) + { + if (TIFFWriteScanline(tif, (tdata_t)srcLine, i, 0) < 0) break; + } + } + break; + case 15: + { + uint32 j; + uint8 *destLine, *destPtr; + uint16 *srcPtr; + + destLine = new uint8[3*width]; + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + for (i=0; i<height; i++, srcLine += pitch) + { + destPtr = destLine; srcPtr = (uint16*)srcLine; + for (j=0; j<width; j++, srcPtr++, destPtr+=3) + { + destPtr[0] = (*srcPtr & 0x1f) << 3; + destPtr[1] = (*srcPtr & 0x3e0) >> 2; + destPtr[2] = (*srcPtr & 0x7c00) >> 7; + } + if (TIFFWriteScanline(tif, (tdata_t)destLine, i, 0) < 0) break; + } + delete [] destLine; + } + break; + case 24: + { + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + for (i=0; i<height; i++, srcLine += pitch) + { + if (TIFFWriteScanline(tif, (tdata_t)srcLine, i, 0) < 0) break; + } + } + break; + case 32: + { + uint32 j; + uint8 *destLine, *destPtr; + uint32 *srcPtr; + + destLine = new uint8[3*width]; + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + for (i=0; i<height; i++, srcLine += pitch) + { + destPtr = destLine; srcPtr = (uint32*)srcLine; + for (j=0; j<width; j++, srcPtr++, destPtr+=3) + { + destPtr[0] = (*srcPtr & 0xff); + destPtr[1] = (*srcPtr >> 8) & 0xff; + destPtr[2] = (*srcPtr >> 16) & 0xff; + } + if (TIFFWriteScanline(tif, (tdata_t)destLine, i, 0) < 0) break; + } + delete [] destLine; + } + break; + default: + cerr << "rviewIO::PixmapToTIFF(): Unsupported pixmap depth (" << depth << ')' << endl; + TIFFClose(tif); + break; + } + + TIFFClose(tif); + + if (i < height) + { + char buffer[STRINGSIZE]; + + sprintf(buffer, "%s %s.\n", lman->lookup("errorFileWrite"), filename); + rviewErrorbox eb(buffer); eb.activate(); + return -1; + } + + return 0; +} + + +int rviewIO::isVFF(const char *filename) +{ + const char *ext = getExtension(filename); + + if (ext != NULL) + { + if (strcasecmp(ext, "vff") == 0) + return 1; + } + return 0; +} + + +int rviewIO::loadVFF(const char *filename, r_Ref<r_GMarray> &mddPtr, const char *params) +{ +#ifdef RVIEW_USE_VFF + FILE *fp; + + if ((fp = fopen(filename, "rb")) != NULL) + { + unsigned long vffSize; + char *vffData; + + fseek(fp, 0, SEEK_END); + vffSize = ftell(fp); + fseek(fp, 0, SEEK_SET); + vffData = new char[vffSize]; + fread(vffData, 1, vffSize, fp); + fclose(fp); + + r_Minterval dom(1); + dom << r_Sinterval((r_Range)0, (r_Range)vffSize-1); + + r_Conv_VFF vff(vffData, dom, r_Convertor::ctype_char); + r_Storage_Man_CPP sman; + vff.set_storage_handler(sman); + try + { + r_convDesc desc = vff.convertFrom(params); + delete [] vffData; + if (desc.destInterv.dimension() == 3) + { + const char *fmtString = NULL; + const char *nameString = NULL; + + // Hmm... we seem to lack a set_type_schema() method... + switch (desc.destType->type_id()) + { + case r_Type::BOOL: + case r_Type::CHAR: + case r_Type::OCTET: + fmtString = structure_format_cube8; + nameString = rviewTypeNames[rbt_char][2]; + break; + case r_Type::SHORT: + case r_Type::USHORT: + fmtString = structure_format_cube16; + nameString = rviewTypeNames[rbt_ushort][2]; + break; + case r_Type::LONG: + case r_Type::ULONG: + fmtString = structure_format_cube32; + nameString = rviewTypeNames[rbt_ulong][2]; + break; + default: + break; + } + if (fmtString != NULL) + { + char fmtBuffer[STRINGSIZE]; + + sprintf(fmtBuffer, fmtString, desc.destInterv[0].low(), desc.destInterv[0].high(), desc.destInterv[1].low(), desc.destInterv[1].high(), desc.destInterv[2].low(), desc.destInterv[2].high()); + mddPtr = new r_GMarray; + mddPtr->set_spatial_domain(desc.destInterv); + mddPtr->set_array(desc.dest); + mddPtr->set_type_structure(fmtBuffer); + mddPtr->set_type_by_name(nameString); + mddPtr->set_type_length(((const r_Base_Type*)desc.destType)->size()); + mddPtr->set_current_format(r_Array); + mddPtr->set_array_size(mddPtr->spatial_domain().cell_count() * mddPtr->get_type_length()); + //cout << "LOADED VFF " << mddPtr->spatial_domain() << ", " << mddPtr->get_type_structure() << ", " << mddPtr->get_type_name() << endl; + delete desc.destType; + return RVIEW_IO_OK; + } + } + delete desc.destType; + delete [] desc.dest; + return RVIEW_IO_FORMAT; + } + catch (r_Error &err) + { + cerr << "rviewIO::loadVFF(): " << err.what() << endl; + delete [] vffData; + return RVIEW_IO_FORMAT; + } + } + return RVIEW_IO_NOTFOUND; +#else + return RVIEW_IO_UNSUPP; +#endif +} + + +int rviewIO::saveVFF(const char *filename, r_Ref<r_GMarray> &mddPtr, const char *params) +{ + cout << "rviewIO::saveVFF() not implemented" << endl; + return RVIEW_IO_UNSUPP; +} diff --git a/applications/rview/rviewIO.hh b/applications/rview/rviewIO.hh new file mode 100644 index 0000000..dfede12 --- /dev/null +++ b/applications/rview/rviewIO.hh @@ -0,0 +1,121 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView data exchange interface for storing MDD to the filing system + * or loading from it. Currently supported operations are: + * - Load TIFF file. + * - Save wxPixmap as TIFF to file. + * + * COMMENTS: + * None + */ + + + +#ifndef _RVIEW_IO_H_ +#define _RVIEW_IO_H_ + + +#ifdef __GNUG__ +#pragma interface +#endif + + + +#include "rasodmg/ref.hh" +#include "rasodmg/marray.hh" +#include "rasodmg/gmarray.hh" + +#include "rviewUtils.hh" + + + +// Return status +enum rviewIoStates { + RVIEW_IO_OK, + RVIEW_IO_NOTFOUND, + RVIEW_IO_MEMORY, + RVIEW_IO_UNSUPP, + RVIEW_IO_FORMAT +}; + + +// pixmap conversion functions +class wxPixmap; + +// parameter parser +class r_Parse_Params; + + +class rviewIO +{ + public: + + rviewIO(void); + ~rviewIO(void); + + static void terminate(void); + + static int isTIFF(const char *filename); + static int loadTIFF(const char *filename, r_Ref<r_GMarray> &mddPtr, const char *params=NULL); + static int saveTIFF(const char *filename, r_Ref<r_GMarray> &mddPtr, const char *params=NULL); + static int PixmapToTIFF(wxPixmap *pixmap, const char *filename, const char *params=NULL); + static int isVFF(const char *filename); + static int loadVFF(const char *filename, r_Ref<r_GMarray> &mddPtr, const char *params=NULL); + static int saveVFF(const char *filename, r_Ref<r_GMarray> &mddPtr, const char *params=NULL); + + + private: + + static void ensureParams(void); + static void processParams(const char *params); + + static r_Parse_Params *dfltParams; + static char *tiffCompStr; + static int tiffCompression; + + static const char *getExtension(const char *filename); + + // tiff compression keywords + static const char param_KeyTiffComp[]; + static const char param_TiffCompNone[]; + static const char param_TiffCompPack[]; + static const char param_TiffCompLZW[]; + static const char param_TiffCompZLib[]; + static const char param_TiffCompJPEG[]; + + // TIFF formats + static const char structure_format_mono[]; + static const char structure_format_grey[]; + static const char structure_format_rgb[]; + + // VFF formats + static const char structure_format_cube8[]; + static const char structure_format_cube16[]; + static const char structure_format_cube32[]; +}; + +#endif diff --git a/applications/rview/rviewImage.cpp b/applications/rview/rviewImage.cpp new file mode 100644 index 0000000..bdc6e2f --- /dev/null +++ b/applications/rview/rviewImage.cpp @@ -0,0 +1,5230 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * Purpose: + * + * rView graphical display modes (flat and rendered images). Renderers + * provided by the cube_render sources, pixmap interface provided by + * the new wxWindows class wxPixmap. Also includes the ColourspaceMapper + * object. + * + * COMMENTS: + * None + */ + + + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +#include <wx/wx_prec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + + +#include <string.h> +#include <stdlib.h> +#include <iostream.h> +#include <math.h> +#include <limits.h> +#include <float.h> + + +// I hate Windows... +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + + +#include "raslib/rmdebug.hh" +#include "rasodmg/fastscale.hh" + + +// For information about bit order and so on +#include "wx_pixmap.h" +#include "wx_pixmap_translate.h" + +#include "cube_render.h" + +#include "rviewTypes.hh" + +#include "labelManager.hh" + +#include "rviewUtils.hh" +#include "rviewMDD.hh" +#include "rviewDModes.hh" +#include "rviewColMap.hh" +#include "rviewPrefs.hh" +#include "rviewIO.hh" +#include "rviewDb.hh" + + + + + + + + +// Table mapping base types to visualisztion types +int rviewImageTypes[] = { + RVIEW_IMGTYPE_NONE, // rbt_none + RVIEW_IMGTYPE_MONO, // rbt_bool + RVIEW_IMGTYPE_GREY, // rbt_char + RVIEW_IMGTYPE_GREY, // rbt_uchar + RVIEW_IMGTYPE_GREY12, // rbt_short + RVIEW_IMGTYPE_HIGH, // rbt_ushort + RVIEW_IMGTYPE_TRUE32, // rbt_long + RVIEW_IMGTYPE_TRUE32, // rbt_ulong + RVIEW_IMGTYPE_TRUE24, // rbt_rgb + RVIEW_IMGTYPE_NONE, // rbt_float + RVIEW_IMGTYPE_NONE // rbt_double +}; + + + + + + +/* + * rviewImageSetup members + */ + +const int rviewImageSetup::imgset_border = rviewDisplay::display_border; +const int rviewImageSetup::imgset_theight = 30; +const int rviewImageSetup::imgset_chkheight = 24; +const int rviewImageSetup::imgset_renheight = 206; +const int rviewImageSetup::imgset_voxheight = 256; +const int rviewImageSetup::imgset_hgtheight = 60; +const int rviewImageSetup::imgset_bwidth = 80; +const int rviewImageSetup::imgset_bheight = 40; +const int rviewImageSetup::imgset_chowidth = 130; +const int rviewImageSetup::imgset_choheight = 30; +const int rviewImageSetup::imgset_width = 400; +const int rviewImageSetup::imgset_height = rviewImageSetup::imgset_renheight + rviewImageSetup::imgset_voxheight + rviewImageSetup::imgset_hgtheight + rviewImageSetup::imgset_bheight + 5*rviewImageSetup::imgset_border + rview_window_extra_height; + + +const char *rviewImageSetup::normalKernelSizes[] = { + "0", + "1", + "2", + "3", + NULL +}; + +const keyword_to_ident_c rviewImageSetup::normalKernelTypes[] = { + {RENDER_NORM_KERNEL_HOMO, "kernelTypeAvg"}, + {RENDER_NORM_KERNEL_LINEAR, "kernelTypeLin"}, + {RENDER_NORM_KERNEL_GAUSS, "kernelTypeGauss"}, + {0, NULL} +}; + +rviewImageSetup::rviewImageSetup(rview_image_setup *ris, rviewRenderImage *parentWin) : rviewFrame(NULL, lman->lookup("titleImgSetup"), 0, 0, imgset_width, imgset_height) +{ + char buffer[STRINGSIZE]; + int w, h, x, y, off, posx, posy; + char **ktypes; + char *b; + int i; + + parent = parentWin; imgSetup = ris; + memcpy(&oldSetup, ris, sizeof(rview_image_setup)); + + GetClientSize(&w, &h); + panel = new wxPanel((wxWindow*)this, 0, 0, w, h); + w -= 2*imgset_border; posx = imgset_border; posy = imgset_border; + renderGroup = new wxGroupBox(panel, "", posx, posy, w, imgset_renheight); + posy += imgset_renheight + imgset_border; + voxelGroup = new wxGroupBox(panel, "", posx, posy, w, imgset_voxheight); + posy += imgset_voxheight + imgset_border; + heightGroup = new wxGroupBox(panel, "", posx, posy, w, imgset_hgtheight); + + w -= 2*imgset_border; y = imgset_border; + posx += imgset_border; + renderGroup->GetClientSize(&x, &h); off = y + imgset_renheight - h; + h = (h - 5*imgset_theight) / 5; + + zproWidget = new rviewText(panel); + clipzWidget = new rviewText(panel); + useLights = new rviewCheckBox(panel); + lightsAngle = new rviewText(panel); + lightsScintAngle = new rviewText(panel); + lightsAmbient = new rviewText(panel); + lightsGain = new rviewText(panel); + lightsDir = new rviewText(panel); + lightsDist = new rviewText(panel); + pixThreshLowWidget = new rviewText(panel); + pixThreshHighWidget = new rviewText(panel); + wgtThreshWidget = new rviewText(panel); + wgtQuantWidget = new rviewText(panel); + kernelSize = new rviewChoice(panel, 4, (char**)normalKernelSizes, lman->lookup("imgSetKernSize")); + ktypes = new char *[3]; + b = buffer; + for (i=0; i<3; i++) + { + ktypes[i] = b; b += 1 + sprintf(b, "%s", lman->lookup(normalKernelTypes[i].keyword)); + } + kernelType = new rviewChoice(panel, 3, ktypes, lman->lookup("imgSetKernType")); + delete [] ktypes; + useVoxCol = new rviewCheckBox(panel); + useRgbBrightness = new rviewCheckBox(panel); + voxColour = new rviewText(panel); + gridSize = new rviewText(panel); + scaleHeight = new rviewText(panel); + + okBut = new rviewButton(panel); + cancelBut = new rviewButton(panel); + + label(); + + updateSettings(*ris); + + frameWidth=-1; + frameHeight=-1; + + OnSize(imgset_width, imgset_height); + OnSize(imgset_width, imgset_height); + + Show(TRUE); +} + + + +rviewImageSetup::~rviewImageSetup(void) +{ + if (parent != NULL) + { + user_event ue; + + ue.type = usr_child_closed; ue.data = (void*)this; + parent->userEvent(ue); + } +} + + +const char *rviewImageSetup::getFrameName(void) const +{ + return "rviewImageSetup"; +} + +rviewFrameType rviewImageSetup::getFrameType(void) const +{ + return rviewFrameTypeImgSet; +} + + +void rviewImageSetup::unlinkParent(void) +{ + parent = NULL; +} + + +void rviewImageSetup::updateSettings(const rview_image_setup &ris) +{ + char buffer[STRINGSIZE]; + char *b; + int i; + + zproWidget->SetValue((long)(ris.zpro)); + clipzWidget->SetValue((long)(ris.clipz)); + useLights->SetValue(ris.useLights); + lightsAngle->SetValue(ris.lightsAngle); + lightsScintAngle->SetValue(ris.lightsScintAngle); + lightsAmbient->SetValue(ris.lightsAmbient); + lightsGain->SetValue(ris.lightsGain); + lightsDist->SetValue(ris.lightsDist); + pixThreshLowWidget->SetValue(ris.pixelThresholdLow, TRUE); + pixThreshHighWidget->SetValue(ris.pixelThresholdHigh, TRUE); + wgtThreshWidget->SetValue(ris.weightThreshold, TRUE); + wgtQuantWidget->SetValue(ris.weightQuantisation); + useRgbBrightness->SetValue(ris.useRgbBrightness); + useVoxCol->SetValue(ris.useVoxCol); + voxColour->SetValue(ris.voxColour); + gridSize->SetValue(ris.gridSize); + scaleHeight->SetValue(ris.scaleHeight); + + if ((ris.kernelSize >= 0) && (ris.kernelSize < 4)) + { + kernelSize->SetSelection(ris.kernelSize); + } + for (i=0; normalKernelTypes[i].keyword != NULL; i++) + { + if (normalKernelTypes[i].ident == ris.kernelType) + { + kernelType->SetSelection(i); break; + } + } + b = buffer; + if ((ris.lightsDir & RVIEW_LIGHTDIR_LEFT) != 0) *b++ = 'l'; + else if ((ris.lightsDir & RVIEW_LIGHTDIR_RIGHT) != 0) *b++ = 'r'; + if ((ris.lightsDir & RVIEW_LIGHTDIR_DOWN) != 0) *b++ = 'd'; + else if ((ris.lightsDir & RVIEW_LIGHTDIR_UP) != 0) *b++ = 'u'; + if ((ris.lightsDir & RVIEW_LIGHTDIR_FRONT) != 0) *b++ = 'f'; + else if ((ris.lightsDir & RVIEW_LIGHTDIR_BACK) != 0) *b++ = 'b'; + *b++ = '\0'; + lightsDir->SetValue(buffer); +} + + + +int rviewImageSetup::parseLightDirection(const char *dir) +{ + char *b; + int val; + + b = (char*)dir; + val = 0; + while (*b != '\0') + { + switch (*b) + { + case 'l': val |= RVIEW_LIGHTDIR_LEFT; break; + case 'r': val |= RVIEW_LIGHTDIR_RIGHT; break; + case 'd': val |= RVIEW_LIGHTDIR_DOWN; break; + case 'u': val |= RVIEW_LIGHTDIR_UP; break; + case 'f': val |= RVIEW_LIGHTDIR_FRONT; break; + case 'b': val |= RVIEW_LIGHTDIR_BACK; break; + default: break; + } + b++; + } + return val; +} + + +void rviewImageSetup::readNewSetup(void) +{ + long val; + + val = asctoi(zproWidget->GetValue()); + if (val > 0) imgSetup->zpro = (unsigned long)val; + val = asctoi(clipzWidget->GetValue()); + if (val > 0) imgSetup->clipz = (unsigned long)val; + imgSetup->useLights = useLights->GetValue(); + imgSetup->lightsAngle = atof(lightsAngle->GetValue()); + imgSetup->lightsScintAngle = atof(lightsScintAngle->GetValue()); + imgSetup->lightsAmbient = atof(lightsAmbient->GetValue()); + imgSetup->lightsGain = atof(lightsGain->GetValue()); + imgSetup->lightsDist = asctoi(lightsDist->GetValue()); + imgSetup->pixelThresholdLow = asctof(pixThreshLowWidget->GetValue()); + imgSetup->pixelThresholdHigh = asctof(pixThreshHighWidget->GetValue()); + imgSetup->weightThreshold = asctof(wgtThreshWidget->GetValue()); + imgSetup->useRgbBrightness = useRgbBrightness->GetValue(); + val = asctoi(wgtQuantWidget->GetValue()); + if (val >= 0) imgSetup->weightQuantisation = (int)val; + imgSetup->kernelSize = kernelSize->GetSelection(); + imgSetup->kernelType = normalKernelTypes[kernelType->GetSelection()].ident; + imgSetup->useVoxCol = useVoxCol->GetValue(); + imgSetup->voxColour = asctof(voxColour->GetValue()); + imgSetup->lightsDir = parseLightDirection(lightsDir->GetValue()); + imgSetup->gridSize = asctoi(gridSize->GetValue()); + imgSetup->scaleHeight = atof(scaleHeight->GetValue()); +} + + +void rviewImageSetup::label(void) +{ + SetTitle(lman->lookup("titleImgSetup")); + + renderGroup->SetLabel(lman->lookup("imgSetRender")); + voxelGroup->SetLabel(lman->lookup("imgSetVoxel")); + heightGroup->SetLabel(lman->lookup("imgSetHeight")); + zproWidget->SetLabel(lman->lookup("imgSetRenZpro")); + clipzWidget->SetLabel(lman->lookup("imgSetRenClipz")); + useLights->SetLabel(lman->lookup("imgSetRenUseLight")); + lightsAngle->SetLabel(lman->lookup("imgSetRenLightAn")); + lightsScintAngle->SetLabel(lman->lookup("imgSetRenLightSc")); + lightsAmbient->SetLabel(lman->lookup("imgSetRenLightAm")); + lightsGain->SetLabel(lman->lookup("imgSetRenLightGn")); + lightsDir->SetLabel(lman->lookup("imgSetRenLightDr")); + lightsDist->SetLabel(lman->lookup("imgSetRenLightDs")); + pixThreshLowWidget->SetLabel(lman->lookup("imgSetVoxPixThreshLow")); + pixThreshHighWidget->SetLabel(lman->lookup("imgSetVoxPixThreshHigh")); + wgtThreshWidget->SetLabel(lman->lookup("imgSetVoxWgtThresh")); + wgtQuantWidget->SetLabel(lman->lookup("imgSetVoxWgtQuant")); + useRgbBrightness->SetLabel(lman->lookup("imgSetVoxRgbBright")); + kernelSize->SetLabel(lman->lookup("imgSetKernSize")); + kernelType->SetLabel(lman->lookup("imgSetKernType")); + useVoxCol->SetLabel(lman->lookup("imgSetUseVCol")); + voxColour->SetLabel(lman->lookup("imgSetVoxCol")); + gridSize->SetLabel(lman->lookup("imgSetGridSize")); + scaleHeight->SetLabel(lman->lookup("imgSetHgtScale")); + okBut->SetLabel(lman->lookup("textOK")); + cancelBut->SetLabel(lman->lookup("textCancel")); +} + + + +void rviewImageSetup::OnSize(int w, int h) +{ + int x, y, width, height, off, posx, posy; + + GetClientSize(&width, &height); + + //need to resize? + if (( imgset_width != width) || ( imgset_height != height)) + { + frameWidth = imgset_width; + frameHeight = imgset_height; + width= imgset_width; + height = imgset_height; + SetClientSize(width, height); + return; + } + + panel->SetSize(0, 0, width, height); + width -= 2*imgset_border; height -= 2*imgset_border; + posx = imgset_border; posy = imgset_border; + renderGroup->SetSize(posx, posy, width, imgset_renheight); + posy += imgset_renheight + imgset_border; + voxelGroup->SetSize(posx, posy, width, imgset_voxheight); + posy += imgset_voxheight + imgset_border; + heightGroup->SetSize(posx, posy, width, imgset_hgtheight); + + width -= 2*imgset_border; y = imgset_border; + posx += imgset_border; posy += imgset_border; + renderGroup->GetClientSize(&x, &height); off = y + imgset_renheight - height; + height = (height - 5*imgset_theight) / 5; + posy = off + height/2; + zproWidget->SetSize(posx, posy, width/2 - imgset_border, imgset_theight, wxTE_PROCESS_ENTER); + clipzWidget->SetSize(posx + width/2, posy, width/2, imgset_theight, wxTE_PROCESS_ENTER); + posy += height + imgset_theight; + useLights->SetSize(posx, posy, width/2, imgset_theight); + posy += height + imgset_theight; + lightsAngle->SetSize(posx, posy, width/2 - imgset_border, imgset_theight, wxTE_PROCESS_ENTER); + lightsScintAngle->SetSize(posx + width/2, posy, width/2, imgset_theight, wxTE_PROCESS_ENTER); + posy += height + imgset_theight; + lightsAmbient->SetSize(posx, posy, width/2 - imgset_border, imgset_theight, wxTE_PROCESS_ENTER); + lightsGain->SetSize(posx + width/2, posy, width/2, imgset_theight, wxTE_PROCESS_ENTER); + posy += height + imgset_theight; + lightsDir->SetSize(posx, posy, width/2 - imgset_border, imgset_theight, wxTE_PROCESS_ENTER); + lightsDist->SetSize(posx + width/2, posy, width/2, imgset_theight, wxTE_PROCESS_ENTER); + + y += imgset_renheight + imgset_border; + voxelGroup->GetClientSize(&x, &height); off = y + imgset_voxheight - height; + height = (height - 5*imgset_theight - imgset_chkheight - imgset_choheight) / 6; + posy = off + height/2; + pixThreshLowWidget->SetSize(posx, posy, width, imgset_theight, wxTE_PROCESS_ENTER); + posy += height + imgset_theight; + pixThreshHighWidget->SetSize(posx, posy, width, imgset_theight, wxTE_PROCESS_ENTER); + posy += height + imgset_theight; + wgtThreshWidget->SetSize(posx, posy, width, imgset_theight, wxTE_PROCESS_ENTER); + posy += height + imgset_theight; + wgtQuantWidget->SetSize(posx, posy, width, imgset_theight, wxTE_PROCESS_ENTER); + posy += height + imgset_theight; + kernelSize->SetSize(posx, posy, imgset_chowidth, imgset_choheight); + kernelType->SetSize(width - imgset_chowidth - 3*imgset_border, posy, imgset_chowidth, imgset_choheight); + posy += height + imgset_theight; + useVoxCol->SetSize(posx, posy, width/2, imgset_theight); + useRgbBrightness->SetSize(posx + width/2, posy, width/2, imgset_theight); + posy += height + imgset_chkheight; + voxColour->SetSize(posx, posy, width, imgset_theight); + + y += imgset_voxheight + imgset_border; + heightGroup->GetClientSize(&x, &height); off = y + imgset_hgtheight - height; + height = (height - imgset_theight); + posy = off + height/2; + gridSize->SetSize(posx, posy, width/2 - imgset_border, imgset_theight, wxTE_PROCESS_ENTER); + scaleHeight->SetSize(posx + width/2, posy, width/2, imgset_theight, wxTE_PROCESS_ENTER); + + y += imgset_hgtheight + imgset_border; + off = (width - 2*imgset_bwidth) / 2; + okBut->SetSize(posx + off/2, y, imgset_bwidth, imgset_bheight); + cancelBut->SetSize(posx + (3*off)/2 + imgset_bwidth, y, imgset_bwidth, imgset_bheight); +} + + + +int rviewImageSetup::process(wxObject &obj, wxEvent &evt) +{ + int type; + + type = evt.GetEventType(); + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)okBut) + { + readNewSetup(); + if (parent != NULL) parent->closeEditor(TRUE); + return 1; + } + else if (&obj == (wxObject*)cancelBut) + { + memcpy(imgSetup, &oldSetup, sizeof(rview_image_setup)); + if (parent != NULL) parent->closeEditor(TRUE); + return 1; + } + } + else if ((type == wxEVENT_TYPE_TEXT_ENTER_COMMAND) || (type == wxEVENT_TYPE_CHECKBOX_COMMAND)) + { + int setFlags = -1; + + if ((&obj == (wxObject*)zproWidget) || (&obj == (wxObject*)clipzWidget) || + (&obj == (wxObject*)useLights)) setFlags = 0; + // These only require an update of the view-window when voxel mode is on. + else if ((&obj == (wxObject*)pixThreshLowWidget) || (&obj == (wxObject*)pixThreshHighWidget) || + (&obj == (wxObject*)wgtThreshWidget) || (&obj == (wxObject*)wgtQuantWidget) || + (&obj == (wxObject*)useRgbBrightness)) setFlags = RVIEW_IFLAG_VOXEL; + // These only require an update of the view-window when lights are on. + else if ((&obj == (wxObject*)useVoxCol) || (&obj == (wxObject*)lightsAngle) || + (&obj == (wxObject*)lightsScintAngle) || (&obj == (wxObject*)lightsAmbient) || + (&obj == (wxObject*)lightsGain) || (&obj == (wxObject*)lightsDir) || + (&obj == (wxObject*)lightsDist)) setFlags = RVIEW_IFLAG_LIGHT; + // These only require an update of the view-window when height mode is on + else if ((&obj == (wxObject*)gridSize) || (&obj == (wxObject*)scaleHeight)) setFlags = RVIEW_IFLAG_HEIGHT; + // Mixed modes + else if (&obj == (wxObject*)voxColour) setFlags = RVIEW_IFLAG_LIGHT | RVIEW_IFLAG_HEIGHT; + if (setFlags >= 0) + { + readNewSetup(); + if (parent != NULL) parent->updateSettings(setFlags); + return 1; + } + } + else if (type == wxEVENT_TYPE_CHOICE_COMMAND) + { + if ((&obj == (wxObject*)kernelSize) || (&obj == (wxObject*)kernelType)) + { + readNewSetup(); + if (parent != NULL) parent->updateSettings(TRUE); + return 1; + } + } + + return 0; +} + + + + + +/* + * rendererControl class. For advanced control of the renderers + animations + */ + +const int rendererControl::rctrl_border = rviewDisplay::display_border; +const int rendererControl::rctrl_bwidth = 80; +const int rendererControl::rctrl_bheight = 30; +const int rendererControl::rctrl_rwidth = 30; +const int rendererControl::rctrl_rheight = 30; +const int rendererControl::rctrl_sheight = 50; +const int rendererControl::rctrl_width = 300; +const int rendererControl::rctrl_height = 5*rendererControl::rctrl_border + 3*rendererControl::rctrl_sheight + rendererControl::rctrl_bheight + rview_window_extra_height; + +rendererControl::rendererControl(float drx, float dry, float drz, int mode, rviewRenderImage *parentWin) : rviewFrame(NULL, lman->lookup("titleRendererCtrl"), 0, 0, rctrl_width, rctrl_height) +{ + int w; + + parent = parentWin; active = mode; + panel = new wxPanel((wxWindow*)this, 0, 0, rctrl_width, rctrl_height); + w = rctrl_width - 2*rctrl_border; + rotx = new rviewSlider(panel, 1000*drx, -1000, 1000, w, lman->lookup("textRotX")); + roty = new rviewSlider(panel, 1000*dry, -1000, 1000, w, lman->lookup("textRotY")); + rotz = new rviewSlider(panel, 1000*drz, -1000, 1000, w, lman->lookup("textRotZ")); + + resetX = new rviewButton(panel, "0"); + resetY = new rviewButton(panel, "0"); + resetZ = new rviewButton(panel, "0"); + + actionBut = new rviewButton(panel); + closeBut = new rviewButton(panel); + + label(); + + OnSize(rctrl_width, rctrl_height); + + Show(TRUE); +} + + +rendererControl::~rendererControl(void) +{ + if (parent != NULL) + { + user_event ue; + + ue.type = usr_child_closed; ue.data = (void*)this; + parent->userEvent(ue); + } +} + + +const char *rendererControl::getFrameName(void) const +{ + return "rendererControl"; +} + +rviewFrameType rendererControl::getFrameType(void) const +{ + return rviewFrameTypeRenCtrl; +} + + +void rendererControl::unlinkParent(void) +{ + parent = NULL; +} + + +void rendererControl::OnSize(int w, int h) +{ + int x, y, dx, dy; + + GetClientSize(&x, &y); + panel->SetSize(0, 0, x, y, wxSIZE_ALLOW_MINUS_ONE); + dx = (x - 2*rctrl_bwidth) / 2; dy = (rctrl_sheight - rctrl_rheight) / 2; + x -= 3*rctrl_border + rctrl_rwidth; y = rctrl_border; + rotx->SetSize(rctrl_border, y, x, rctrl_sheight); + resetX->SetSize(2*rctrl_border + x, y+dy, rctrl_rwidth, rctrl_rheight); + y += rctrl_border + rctrl_sheight; + roty->SetSize(rctrl_border, y, x, rctrl_sheight); + resetY->SetSize(2*rctrl_border + x, y+dy, rctrl_rwidth, rctrl_rheight); + y += rctrl_border + rctrl_sheight; + rotz->SetSize(rctrl_border, y, x, rctrl_sheight); + resetZ->SetSize(2*rctrl_border + x, y+dy, rctrl_rwidth, rctrl_rheight); + y += rctrl_border + rctrl_sheight; + + actionBut->SetSize(dx/2, y, rctrl_bwidth, rctrl_bheight); + closeBut->SetSize((3*dx)/2 + rctrl_bwidth, y, rctrl_bwidth, rctrl_bheight); +} + + +void rendererControl::setActiveMode(int mode) +{ + active = mode; + actionBut->SetLabel(lman->lookup((active == 0) ? "textStart" : "textStop")); + actionBut->SetSize(-1, -1, rctrl_bwidth, rctrl_bheight); +} + + +void rendererControl::label(void) +{ + rotx->SetLabel(lman->lookup("textRotX")); + roty->SetLabel(lman->lookup("textRotY")); + rotz->SetLabel(lman->lookup("textRotZ")); + + closeBut->SetLabel(lman->lookup("textClose")); + + setActiveMode(active); +} + + +void rendererControl::updateParameters(void) +{ + if (parent != NULL) + { + if (active == 0) + { + parent->setAutoRotation(1000.0, 1000.0, 1000.0); + } + else + { + parent->setAutoRotation((rotx->GetValue()) / 1000.0, (roty->GetValue()) / 1000.0, (rotz->GetValue()) / 1000.0); + } + } +} + + +int rendererControl::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_SLIDER_COMMAND) + { + if ((&obj == (wxObject*)rotx) || (&obj == (wxObject*)roty) || (&obj == (wxObject*)rotz)) + { + updateParameters(); + return 1; + } + } + else if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)actionBut) + { + setActiveMode(active ^ 1); + updateParameters(); + return 1; + } + else if (&obj == (wxObject*)closeBut) + { + if (parent != NULL) parent->closeRendererControls(); + return 1; + } + else if ((&obj == (wxObject*)resetX) || (&obj == (wxObject*)resetY) || (&obj == (wxObject*)resetZ)) + { + if (&obj == (wxObject*)resetX) + rotx->SetValue(0); + else if (&obj == (wxObject*)resetY) + roty->SetValue(0); + else if (&obj == (wxObject*)resetZ) + rotz->SetValue(0); + + updateParameters(); + return 1; + } + } + return 0; +} + + + + +/* + * Renderer view window + */ + +const int rendererCurrentView::rcview_border = rviewDisplay::display_border; +const int rendererCurrentView::rcview_bwidth = 80; +const int rendererCurrentView::rcview_bheight = 30; +const int rendererCurrentView::rcview_theight = 30; +const int rendererCurrentView::rcview_width = 180; +const int rendererCurrentView::rcview_height = 220; + +rendererCurrentView::rendererCurrentView(const vertex_fp &angles, long off, double scale, rviewRenderImage *parentWin) : + rviewFrame(NULL, lman->lookup("titleRendererView"), 0, 0, rcview_width, rcview_height) +{ + parent = parentWin; + + panel = new wxPanel((wxWindow*)this, 0, 0, rcview_width, rcview_height); + + rotx = new rviewText(panel); + roty = new rviewText(panel); + rotz = new rviewText(panel); + + zoff = new rviewText(panel); + cubeScale = new rviewText(panel); + + applyButton = new rviewButton(panel); + closeButton = new rviewButton(panel); + + label(); + + updateView(angles, off, scale); + + OnSize(rcview_width, rcview_height); + + Show(TRUE); +} + + +rendererCurrentView::~rendererCurrentView(void) +{ + if (parent != NULL) + { + user_event ue; + + ue.type = usr_child_closed; ue.data = (void*)this; + parent->userEvent(ue); + } +} + + +const char *rendererCurrentView::getFrameName(void) const +{ + return "rendererCurrentView"; +} + + +rviewFrameType rendererCurrentView::getFrameType(void) const +{ + return rviewFrameTypeRenView; +} + + +void rendererCurrentView::unlinkParent(void) +{ + parent = NULL; +} + + +void rendererCurrentView::OnSize(int w, int h) +{ + int x, y; + + GetClientSize(&x, &y); + + panel->SetSize(0, 0, w, h, wxSIZE_ALLOW_MINUS_ONE); + + x -= 2*rcview_border; + rotx->SetSize(rcview_border, rcview_border, x, rcview_theight); + roty->SetSize(rcview_border, rcview_border + rcview_theight, x, rcview_theight); + rotz->SetSize(rcview_border, rcview_border + 2*rcview_theight, x, rcview_theight); + zoff->SetSize(rcview_border, rcview_border + 3*rcview_theight, x, rcview_theight); + cubeScale->SetSize(rcview_border, rcview_border + 4*rcview_theight, x, rcview_theight); + + x -= 2*rcview_bwidth; + if (x < 0) x = 0; + applyButton->SetSize(rcview_border + x/4, 3*rcview_border + 5*rcview_theight, rcview_bwidth, rcview_bheight); + closeButton->SetSize(rcview_border + (3*x)/4 + rcview_bwidth, 3*rcview_border + 5*rcview_theight, rcview_bwidth, rcview_bheight); +} + + +void rendererCurrentView::label(void) +{ + rotx->SetLabel(lman->lookup("textRotX")); + roty->SetLabel(lman->lookup("textRotY")); + rotz->SetLabel(lman->lookup("textRotZ")); + zoff->SetLabel(lman->lookup("textZOffset")); + cubeScale->SetLabel(lman->lookup("textScale")); + applyButton->SetLabel(lman->lookup("textApply")); + closeButton->SetLabel(lman->lookup("textClose")); +} + + +int rendererCurrentView::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)applyButton) + { + updateParameters(); + return 1; + } + else if (&obj == (wxObject*)closeButton) + { + Close(TRUE); + return 1; + } + } + else if (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND) + { + updateParameters(); + return 1; + } + return 0; +} + + +void rendererCurrentView::updateView(const vertex_fp &angles, long off, double scale) +{ + rotx->SetValue((double)angles.x); + roty->SetValue((double)angles.y); + rotz->SetValue((double)angles.z); + zoff->SetValue(off); + cubeScale->SetValue(scale); +} + + +void rendererCurrentView::updateParameters(void) +{ + if (parent != NULL) + { + vertex_fp angles; + angles.x = atof(rotx->GetValue()); + angles.y = atof(roty->GetValue()); + angles.z = atof(rotz->GetValue()); + parent->setCurrentView(angles, atoi(zoff->GetValue()), atof(cubeScale->GetValue())); + } +} + + + + + + + +/* + * pixmapCanvas class. For displaying wxPixmaps + */ +pixmapCanvas::pixmapCanvas(rviewImage *parent, int x, int y, int w, int h, long style) : wxCanvas((wxWindow*)parent, x, y, w, h, style) +{ + pixmap = NULL; font = NULL; + parentWin = parent; + myDC = (wxDC*)GetDC(); + brush.SetStyle(wxSOLID); brush.SetColour(0,0,0); + border.SetStyle(wxSOLID); border.SetColour(0,0,0); + bpen.SetStyle(wxSOLID); bpen.SetColour(0,0,0); + // Pen used for frames + fpen.SetStyle(wxSOLID); fpen.SetColour((char)0xff, (char)0xff, (char)0xff); + fpen.SetCap(wxCAP_BUTT); fpen.SetJoin(wxJOIN_BEVEL); + SetBackground(&brush); + rect_x0 = -1; rect_y0 = -1; rect_x1 = -1; rect_y1 = -1; + aspectRatio = 0; +} + + + +// Don't delete the pixmap! This is not our job. +pixmapCanvas::~pixmapCanvas(void) +{ + if (font != NULL) + { + SetFont(NULL); delete font; + } + SetBackground(NULL); +} + + +void pixmapCanvas::setPixmap(wxPixmap *pmap) +{ + pixmap = pmap; + pixWidth = pmap->getWidth(); pixHeight = pmap->getHeight(); + if (rect_x0 >= 0) + { + if (((rect_x0 >= pixWidth) && (rect_x1 >= pixWidth)) || + ((rect_y0 >= pixHeight) && (rect_y1 >= pixHeight))) + { + rect_x0 = -1; rect_y0 = -1; rect_x1 = -1; rect_y1 = -1; + } + else + { + if (rect_x0 >= pixWidth) rect_x0 = pixWidth-1; + if (rect_x1 >= pixWidth) rect_x1 = pixWidth-1; + if (rect_y0 >= pixHeight) rect_y0 = pixHeight-1; + if (rect_y1 >= pixHeight) rect_y1 = pixHeight-1; + } + } +} + + +void pixmapCanvas::SetAspectRatio(double ratio) +{ + aspectRatio = ratio; +} + + +void pixmapCanvas::adjustBoxToRatio(void) +{ + if ((rect_x0 >= 0) && (aspectRatio > 0)) + { + int width, height; + + width = rect_x1 - rect_x0; + if (width < 0) width = -width; + height = (int)(width * aspectRatio); + if (rect_y1 < rect_y0) height = -height; + rect_y1 = rect_y0 + height; + if ((rect_y1 < 0) || (rect_y1 >= pixHeight)) + { + if (rect_y1 < 0) rect_y1 = 0; else rect_y1 = pixHeight-1; + height = rect_y1 - rect_y0; + if (height < 0) height = -height; + width = (int)(height / aspectRatio); + if (rect_x1 < rect_x0) width = -width; + rect_x1 = rect_x0 + width; + } + } +} + + +void pixmapCanvas::ToggleDragBox(bool clearMode) +{ + int minx, miny, maxx, maxy; + + // Drag box defined? + if (rect_x0 < 0) return; + + if (rect_x0 < rect_x1) + { + minx = rect_x0; maxx = rect_x1; + } + else + { + minx = rect_x1; maxx = rect_x0; + } + if (rect_y0 < rect_y1) + { + miny = rect_y0; maxy = rect_y1; + } + else + { + miny = rect_y1; maxy = rect_y0; + } + + // Only redraw the drag box if it's visible... + if ((rect_x0 != rect_x1) && (rect_y0 != rect_y1)) + { + int ominx, ominy, omaxx, omaxy; + + SetLogicalFunction(wxINVERT); SetPen(&fpen); + //cout << "Draw " << minx << ',' << miny << ',' << maxx << ',' << maxy << endl; + ominx = minx + offX; ominy = miny + offY; omaxx = maxx + offX; omaxy = maxy + offY; + SetClippingRegion((float)ominx, (float)ominy, omaxx-ominx+1.0, omaxy-ominy+1.0); + IntDrawLine(ominx, ominy, omaxx - 1, ominy); + IntDrawLine(omaxx, ominy, omaxx, omaxy - 1); + IntDrawLine(omaxx, omaxy, ominx + 1, omaxy); + IntDrawLine(ominx, omaxy, ominx, ominy + 1); + DestroyClippingRegion(); + } + + if ((rect_x0 == rect_x1) && (rect_y0 == rect_y1) && !clearMode) return; + + // Is the old rectangle to be removed or the new one to be plotted? + // This makes a difference with the text! + if (clearMode) + { + Refresh(FALSE, &textBBox); + } + else + { + float posx, posy, twidth, theight; + char buffer[STRINGSIZE]; + + // Create font on demand only + if (font == NULL) + { + // Do not use pure white as background, this isn't handled correctly under X + wxColour tfore(0, 0, 0), tback((char)0xf0, (char)0xf0, (char)0xf0); + + font = new wxFont(12, wxROMAN, wxNORMAL, wxNORMAL); + SetFont(font); SetTextForeground(&tfore); SetTextBackground(&tback); + textBack.SetColour(tback); textBack.SetStyle(wxSOLID); + } + SetBrush(&textBack); SetPen(&bpen); SetLogicalFunction(wxCOPY); + sprintf(buffer, "%d:%d, %d:%d", minx, maxx, miny, maxy); + GetTextExtent(buffer, &twidth, &theight); + posx = minx + offX + rviewImage::image_dragtoff; posy = miny + offY + rviewImage::image_dragtoff; +#ifdef wx_msw + textBBox.x = (int)posx - rviewDisplay::display_scrstep * GetScrollPos(wxHORIZONTAL); + textBBox.y = (int)posy - rviewDisplay::display_scrstep * GetScrollPos(wxVERTICAL); +#else + textBBox.x = (int)posx; textBBox.y = (int)posy; +#endif + twidth += rviewImage::image_dragtspace; theight += rviewImage::image_dragtspace; + textBBox.width = (int)twidth; textBBox.height = (int)theight; + DrawRectangle(posx, posy, twidth, theight); + DrawText(buffer, posx + rviewImage::image_dragtspace/2, posy + rviewImage::image_dragtspace/2); + SetBrush(NULL); + } + SetPen(NULL); +} + + +void pixmapCanvas::SetDragBox(int x0, int y0, int x1, int y1) +{ + int scrollX, scrollY; + + ToggleDragBox(TRUE); + + scrollX = 0;//rviewDisplay::display_scrstep * GetScrollPos(wxHORIZONTAL); + scrollY = 0;//rviewDisplay::display_scrstep * GetScrollPos(wxVERTICAL); + + rect_x0 = x0 - offX + scrollX; rect_y0 = y0 - offY + scrollY; + rect_x1 = x1 - offX + scrollX; rect_y1 = y1 - offY + scrollY; + if (((rect_x0 < 0) && (rect_x1 < 0)) || ((rect_x0 >= pixWidth) && (rect_x1 >= pixWidth)) || + ((rect_y0 < 0) && (rect_y1 < 0)) || ((rect_y0 >= pixHeight) && (rect_y1 >= pixHeight))) + { + rect_x0 = -1; rect_y0 = -1; rect_x1 = -1; rect_y1 = -1; + return; + } + adjustBoxToRatio(); + if (rect_x0 < 0) rect_x0 = 0; if (rect_x0 >= pixWidth) rect_x0 = pixWidth-1; + if (rect_y0 < 0) rect_y0 = 0; if (rect_y0 >= pixHeight) rect_y0 = pixHeight-1; + if (rect_x1 < 0) rect_x1 = 0; if (rect_x1 >= pixWidth) rect_x1 = pixWidth-1; + if (rect_y1 < 0) rect_y1 = 0; if (rect_y1 >= pixHeight) rect_y1 = pixHeight-1; + + ToggleDragBox(FALSE); +} + + +bool pixmapCanvas::HasDragBox(void) const +{ + return ((rect_x0 >= 0) && (rect_x0 != rect_x1)); +} + + +bool pixmapCanvas::GetDragBox(int &x0, int &y0, int &x1, int &y1) const +{ + if ((rect_x0 < 0) || (rect_x0 == rect_x1)) return FALSE; + + // output sorted: x0 < x1, y0 < y1 + if (rect_x0 < rect_x1) + { + x0 = rect_x0; x1 = rect_x1; + } + else + { + x0 = rect_x1; x1 = rect_x0; + } + if (rect_y0 < rect_y1) + { + y0 = rect_y0; y1 = rect_y1; + } + else + { + y0 = rect_y1; y1 = rect_y0; + } + + return TRUE; +} + + +void pixmapCanvas::UpdateDragBox(int x1, int y1) +{ + if (rect_x0 < 0) return; + + ToggleDragBox(TRUE); + + rect_x1 = x1 - offX; + if (rect_x1 < 0) rect_x1 = 0; if (rect_x1 >= pixWidth) rect_x1 = pixWidth-1; + rect_y1 = y1 - offY; + if (rect_y1 < 0) rect_y1 = 0; if (rect_y1 >= pixHeight) rect_y1 = pixHeight-1; + + adjustBoxToRatio(); + + ToggleDragBox(FALSE); + +#if 0 + int width, height; + int scrollX, scrollY; + int mapX, mapY; + + scrollX = GetScrollPos(wxHORIZONTAL); scrollY = GetScrollPos(wxVERTICAL); + mapX = x1 - scrollX * rviewDisplay::display_scrstep; + mapY = y1 - scrollY * rviewDisplay::display_scrstep; + // Autoscroll? + GetClientSize(&width, &height); + if (width >= 2*rviewImage::image_draghotzone) + { + if (mapX < rviewImage::image_draghotzone) + { + scrollX -= (rviewImage::image_draghotzone - mapX + rviewDisplay::display_scrstep-1) / rviewDisplay::display_scrstep; + if (scrollX >= 0) + { + SetScrollPos(wxHORIZONTAL, scrollX); + x1 = rviewImage::image_draghotzone + (x1-mapX); WarpPointer(x1, y1); + } + } + else if (mapX > (width - rviewImage::image_draghotzone)) + { + scrollX += (mapX - width + rviewImage::image_draghotzone + rviewDisplay::display_scrstep-1) / rviewDisplay::display_scrstep; + if (scrollX <= GetScrollRange(wxHORIZONTAL)) + { + SetScrollPos(wxHORIZONTAL, scrollX); + x1 = (width - rviewImage::image_draghotzone) + (x1-mapX); WarpPointer(x1, y1); + } + } + } + if (height >= 2*rviewImage::image_draghotzone) + { + if (mapY < rviewImage::image_draghotzone) + { + scrollY -= (rviewImage::image_draghotzone - mapY + rviewDisplay::display_scrstep-1) / rviewDisplay::display_scrstep; + if (scrollY >= 0) + { + SetScrollPos(wxVERTICAL, scrollY); + WarpPointer(x1, rviewImage::image_draghotzone + scrollY * rviewDisplay::display_scrstep); + } + } + else if (mapY > (height - rviewImage::image_draghotzone)) + { + scrollY += (mapY - height + rviewImage::image_draghotzone + rviewDisplay::display_scrstep-1) / rviewDisplay::display_scrstep; + if (scrollY <= GetScrollRange(wxVERTICAL)) + { + SetScrollPos(wxVERTICAL, scrollY); + WarpPointer(x1, height-rviewImage::image_draghotzone + scrollY * rviewDisplay::display_scrstep); + } + } + } +#endif +} + + +void pixmapCanvas::AdjustDragBox(int x1, int y1) +{ + if (rect_x0 < 0) SetDragBox(x1, y1, x1, y1); + else + { + ToggleDragBox(TRUE); + + x1 = x1 - offX;// + rviewDisplay::display_scrstep * GetScrollPos(wxHORIZONTAL); + if (x1 < 0) x1 = 0; if (x1 >= pixWidth) x1 = pixWidth-1; + y1 = y1 - offY;// + rviewDisplay::display_scrstep * GetScrollPos(wxVERTICAL); + if (y1 < 0) y1 = 0; if (y1 >= pixHeight) y1 = pixHeight-1; + + if (abs(x1 - rect_x0) < abs(x1 - rect_x1)) + rect_x0 = rect_x1; + if (abs(y1 - rect_y0) < abs(y1 - rect_y1)) + rect_y0 = rect_y1; + rect_x1 = x1; rect_y1 = y1; + + adjustBoxToRatio(); + + ToggleDragBox(FALSE); + } +} + + +// Core functionality for plotting picture + border +void pixmapCanvas::paintCore(int x, int y) +{ + int sx, sy; + + GetSize(&sx, &sy); + + if ((offX > 0) || (offY > 0)) + { + float bordx, bordy, height; + + bordx = (float)offX; bordy = (float)offY; height = (float)sy; + + SetLogicalFunction(wxCOPY); + SetPen(&bpen); SetBrush(&border); + + if (offX > 0) + { + DrawRectangle(0.0, 0.0, bordx, height); + DrawRectangle(bordx + pixWidth, 0.0, bordx+1, height); + } + if (offY > 0) + { + DrawRectangle(bordx, 0.0, (float)pixWidth, bordy); + DrawRectangle(bordx, bordy + pixHeight, (float)pixWidth, bordy+1); + } + SetPen(NULL); SetBrush(NULL); + } + + pixmap->plotPixmap(offX - x, offY - y); + + ToggleDragBox(FALSE); +} + + +void pixmapCanvas::OnPaint(void) +{ + wxUpdateIterator upd(this); + wxRect rect; + int w, h, x, y; + + GetClientSize(&w, &h); + x = rviewDisplay::display_scrstep * GetScrollPos(wxHORIZONTAL); + y = rviewDisplay::display_scrstep * GetScrollPos(wxVERTICAL); + + offX = (w - pixWidth) / 2; if (offX < 0) offX = 0; + offY = (h - pixHeight) / 2; if (offY < 0) offY = 0; + + BeginDrawing(); + + while (upd) + { + //upd.GetRect(&rect); + paintCore(x, y); + upd++; + } + + EndDrawing(); +} + + +void pixmapCanvas::updateDisplay(bool borders) +{ + if (parentWin->IsShown()) + { + int cw, ch; + int x, y; + + x = rviewDisplay::display_scrstep * GetScrollPos(wxHORIZONTAL); + y = rviewDisplay::display_scrstep * GetScrollPos(wxVERTICAL); + + GetClientSize(&cw, &ch); + offX = (cw - pixWidth)/2; if (offX < 0) offX = 0; + offY = (ch - pixHeight)/2; if (offY < 0) offY = 0; + pixmap->invalidatePixmap(); + if (borders) + { + int sx, sy; + + GetVirtualSize(&sx, &sy); + sx *= rviewDisplay::display_scrstep; sy *= rviewDisplay::display_scrstep; + SetClippingRegion(0, 0, sx, sy); + paintCore(x, y); + } + else + { + SetClippingRegion(offX, offY, pixWidth, pixHeight); + paintCore(x, y); + } + DestroyClippingRegion(); + } +} + + + +void pixmapCanvas::OnEvent(wxMouseEvent &mevt) +{ + parentWin->processMouseEvent(mevt); +} + + + + + + +/* + * rviewImage members + */ + +const int rviewImage::image_swidth = 100; +const int rviewImage::image_sheight = 40; +const int rviewImage::image_pbwidth = rviewDisplay::display_pbwidth; +const int rviewImage::image_pbheight = 30; +const int rviewImage::image_minwidth = 50; +const int rviewImage::image_minheight = 50; +const int rviewImage::image_chkwidth = 80; +const int rviewImage::image_chkheight = 30; +const int rviewImage::image_bbwidth = 60; +const int rviewImage::image_bbheight = 20; +const int rviewImage::image_twidth = 100; +const int rviewImage::image_theight = 50; +const int rviewImage::image_bwidth = 64; +const int rviewImage::image_bheight = 30; +const int rviewImage::image_dragtoff = 8; +const int rviewImage::image_dragtspace = 8; +const int rviewImage::image_draghotzone = 32; +const int rviewImage::image_ctrly = 60; +const int rviewImage::image_totaly = rviewDisplay::display_cheight + rviewImage::image_ctrly; + +const char *rviewImage::view_ScrollPos = "scrollPos"; +const char *rviewImage::view_UseCspace = "useCspace"; +const char *rviewImage::view_CspaceFull = "cspaceFull"; +const char *rviewImage::view_CspaceProj = "cspaceProj"; +const char *rviewImage::view_CspaceMeans = "cspaceMeans"; +const char *rviewImage::view_CspaceSigmas = "cspaceSigmas"; +const char *rviewImage::view_CspaceType = "cspaceType"; +const char *rviewImage::view_CspaceRange = "cspaceRange"; +const char *rviewImage::view_ScaleValue = "scaleValue"; + +rviewImage::rviewImage(mdd_frame *mf, int es, unsigned int flags) : rviewDisplay(mf, es+image_ctrly, flags) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewImage", "rviewImage()"); + + // make destructor safe first. + csmap = NULL; // signifies no data for value->colourspace mapping initialised yet + csInterv = NULL; + pixmap = NULL; pcanv = NULL; scaleSlider = NULL; + cspar = NULL; + + // This variable is set to true once openViewerEpilogue() is called. Its main use is + // 1) on Windows: trap calls to OnSize() happening before the object is fully initialized + // 2) for the renderers: avoid actually drawing anything until the window size is fixed. + initPhaseFinished = FALSE; + + // Check the base type immediately: + if (baseType == rbt_none) + { + objectInitializedOK = FALSE; + return; + } + +#if 0 + // Test resampling routines... + r_Ref<r_GMarray> mddPtr; + r_Minterval newInterv(dimMDD); + //for (i=0; i<dimMDD; i++) newInterv << r_Sinterval(4*mddObj->spatial_domain()[i].low(), 4*mddObj->spatial_domain()[i].high()); + //mdd_objectScaleInter(mddObj, mddPtr, newInterv, baseType); + for (i=0; i<dimMDD; i++) newInterv << r_Sinterval((r_Range)(0.5*mddObj->spatial_domain()[i].low()), (r_Range)(0.5*mddObj->spatial_domain()[i].high())); + mdd_objectScaleAverage(mddObj, mddPtr, newInterv, baseType); + mddObj.destroy(); + mddObj = mddPtr; + interv = mddObj->spatial_domain(); +#endif +} + + + +rviewImage::~rviewImage(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewImage", "~rviewImage()"); + + if (cspar != NULL) delete cspar; + if (csmap != NULL) delete csmap; + if (csInterv != NULL) delete csInterv; + delete pixmap; +} + + +// concentrate most intelligence in this function rather than the constructor +// because we can't use virtual functions (correctly) in the constructor. +int rviewImage::openViewer(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewImage", "openViewer()"); + + if (!objectInitializedOK) + return -1; + + // init lower level stuff like the menu bar + rviewDisplay::openViewer(); + + wxMenu *config; + wxMenu *submenu; + char buffer[STRINGSIZE]; + + config = new wxMenu; + configMenuInitHook(config); + submenu = new wxMenu; + submenu->Append(MENU_IMAGE_CSPACE_ON, "", NULL, TRUE); + submenu->Append(MENU_IMAGE_CSPACE_FULL, "", NULL, TRUE); + submenu->Append(MENU_IMAGE_CSPACE_PROJ, "", NULL, TRUE); + submenu->Append(MENU_IMAGE_CSPACE_EDIT, ""); + config->Append(MENU_IMAGE_SETUP_CSPACE, lman->lookup("menCspaceTitle"), submenu, NULL); + if (moviePossible()) + { + submenu = new wxMenu; + submenu->Append(MENU_IMAGE_MOVIE_ONCE, "", NULL, TRUE); + submenu->Append(MENU_IMAGE_MOVIE_START, "", NULL, TRUE); + submenu->Append(MENU_IMAGE_MOVIE_SWITCH, "", NULL, TRUE); + config->AppendSeparator(); + config->Append(MENU_IMAGE_SETUP_MOVIE, lman->lookup("menImgSetupMovie"), submenu, NULL); + } + sprintf(buffer, "&%s", lman->lookup("menImgSetup")); + mBar->Append(config, buffer); + + mBar->Enable(MENU_IMAGE_CSPACE_EDIT, FALSE); + + pcanv = NULL; imgData = NULL; + scaleValue = 100.0; + + // Colourspace mapper + doValToCspace = (prefs->rgbSpace != 0); + doFullRangeCspace = (prefs->rgbSpace == 2); + // We're not allowed to call setCspaceProjMode before a projection is set + // in the init... functions + doProjRangeCspace = (prefs->rgbSpace == 3); + if (modeNeedsCspace(baseType)) doValToCspace = TRUE; + + mousex = -1.0; mousey = -1.0; mousebut = 0; + + if (showScaleSlider()) + scaleSlider = new rviewSlider(ctrlPanel, (int)scaleValue, 0, 500, image_swidth, lman->lookup("textScale")); + + if (rviewCheckInitCspace(baseType, NULL, mddObj) != 0) + { + mBar->Check(MENU_IMAGE_CSPACE_ON, doValToCspace); + mBar->Check(MENU_IMAGE_CSPACE_FULL, doFullRangeCspace); + mBar->Check(MENU_IMAGE_CSPACE_PROJ, doProjRangeCspace); + mBar->Enable(MENU_IMAGE_CSPACE_FULL, doValToCspace); + mBar->Enable(MENU_IMAGE_CSPACE_PROJ, doValToCspace); + mBar->Enable(MENU_IMAGE_CSPACE_EDIT, doValToCspace); + cspaceForType = TRUE; + } + else + { + cspaceForType = FALSE; + } + configureCspace(cspaceForType); + + if (moviePossible()) + { + switch (prefs->movieMode) + { + case 1: lastMovieMode = MENU_IMAGE_MOVIE_START; break; + case 2: lastMovieMode = MENU_IMAGE_MOVIE_SWITCH; break; + default: lastMovieMode = MENU_IMAGE_MOVIE_ONCE; break; + } + playDirection = 0; + + playBack = new rviewButton(ctrlPanel, "<"); + playStop = new rviewButton(ctrlPanel, "[]"); + playFwd = new rviewButton(ctrlPanel, ">"); + mBar->Check(lastMovieMode, TRUE); + } + else + { + playBack = NULL; playStop = NULL; playFwd = NULL; + } + + configureMode(); + + wxColour palette[2]; + int w, h; + + GetClientSize(&w, &h); + + + w -= 2*display_cnvborder; h -= 2*display_cnvborder + totalCtrlHeight; + pcanv = new pixmapCanvas(this, display_cnvborder, display_cnvborder + totalCtrlHeight, w, h); + + scrollx = -1; scrolly = -1; + pixWidth = -1; pixHeight = -1; pixPitch = -1; pixPad = 0; pixDepth = 0; virtualPitch = -1; + + if (initMode() == NULL) + { + objectInitializedOK = FALSE; + return -1; + } + + pcanv->Clear(); + + if (rviewImageTypes[baseType] == RVIEW_IMGTYPE_MONO) + { + palette[0] = wxColour(0,0,0); palette[1] = wxColour(255,255,255); + } + pixmap = new wxPixmap((wxWindow*)pcanv, pixWidth, pixHeight, pixDepth, pixPad, imgData, getPixmapFlags(), ((rviewImageTypes[baseType] == RVIEW_IMGTYPE_MONO) ? palette : NULL)); + pcanv->setPixmap(pixmap); + + return 0; +} + + +void rviewImage::openViewerEpilogue(rviewFrameType ft) +{ + // Only do something if this was called from the top level image class, i.e. the + // one that overloads getFrameType(). Otherwise NOP. + if (ft == getFrameType()) + { + int w, h; + + initPhaseFinished = TRUE; + + RMDBGONCE(3, RMDebug::module_applications, "rviewImage", "openViewerEpilogue() from top level " << getFrameName() ); + + label(); + + setMinimumViewerSize(image_minwidth, image_minheight); + + GetSize(&w, &h); + + OnSize(w, h); + OnSize(w, h); + + Show(TRUE); + } +} + + +int rviewImage::freeDimsFromProjection(int &dim1, int &dim2, r_Point *map) +{ + // Apply the projection string + if (rviewParseProjection(getVirtualDomain(), pt1, pt2, projString, &freeDims, map) != dimMDD) + { + rviewErrorbox::reportError(lman->lookup("errorProjection"), rviewImage::getFrameName(), "freeDimsFromProjection"); + return -1; + } + // Check whether there are more or less than 2 free dimensions. + for (dim1=0; dim1<dimMDD; dim1++) if ((freeDims & (1<<dim1)) != 0) break; + for (dim2=dim1+1; dim2<dimMDD; dim2++) if ((freeDims & (1<<dim2)) != 0) break; + if ((dim1 >= dimMDD) || (dim2 >= dimMDD)) + { + rviewErrorbox::reportError(lman->lookup("errorProjectFree"), rviewImage::getFrameName(), "freeDimsFromProjection"); + return -1; + } + if (map != NULL) + { + dim1 = (*map)[dim1]; dim2 = (*map)[dim2]; + } + return 0; +} + + +void rviewImage::projectObjectHook(void) +{ +} + + +const char *rviewImage::getFrameName(void) const +{ + return "rviewImage"; +} + +rviewFrameType rviewImage::getFrameType(void) const +{ + return rviewFrameTypeImage; +} + + +int rviewImage::fileMenuInitHook(wxMenu *menu) +{ + menu->Append(MENU_DISP_DATA_SAVETIFF, ""); + return 1; +} + + +bool rviewImage::modeNeedsCspace(rviewBaseType bt) const +{ + return ((bt == rbt_float) || (bt == rbt_double)); +} + + +void rviewImage::configureCspace(bool state) +{ + mBar->Enable(MENU_IMAGE_SETUP_CSPACE, state); + // Bug (feature?) in WinNT version: menu having submenu can't be disabled. + mBar->Enable(MENU_IMAGE_CSPACE_ON, state); + mBar->Enable(MENU_IMAGE_CSPACE_FULL, state); + mBar->Enable(MENU_IMAGE_CSPACE_PROJ, state); + mBar->Enable(MENU_IMAGE_CSPACE_EDIT, state); +} + + +int rviewImage::configMenuInitHook(wxMenu *menu) +{ + return 0; +} + +bool rviewImage::cspaceRangeHook(bool suggest) +{ + return suggest; +} + +void rviewImage::configureMode(void) +{ + bool csranges; + + if (modeNeedsCspace(baseType)) doValToCspace = TRUE; + + csranges = cspaceRangeHook((cspaceForType && doValToCspace)); + + if (doValToCspace) + { + mBar->Enable(MENU_IMAGE_CSPACE_ON, doValToCspace); + mBar->Enable(MENU_IMAGE_CSPACE_EDIT, doValToCspace); + } + mBar->Enable(MENU_IMAGE_CSPACE_FULL, csranges); + mBar->Enable(MENU_IMAGE_CSPACE_PROJ, csranges); + mBar->Check(MENU_IMAGE_CSPACE_ON, doValToCspace); +} + + +void rviewImage::setCspaceProjMode(bool pm) +{ + if (pm) + { + int i=0; + r_Minterval tempInterv(dimMDD); + + if(!csInterv) + csInterv = new r_Minterval(dimMDD); + + for (i=0; i<dimMDD; i++) + { + tempInterv << r_Sinterval((r_Range)pt1[i], (r_Range)pt2[i]); + } + + *csInterv=tempInterv; + } + + doProjRangeCspace = pm; +} + + +void rviewImage::label(void) +{ + if (scaleSlider != NULL) + scaleSlider->SetLabel(lman->lookup("textScale")); + + mBar->SetLabelTop(fixedNumberOfMenus, lman->lookup("menImgSetup")); + mBar->SetLabel(MENU_IMAGE_SETUP_CSPACE, lman->lookup("menCspaceTitle")); + mBar->SetLabel(MENU_IMAGE_CSPACE_ON, lman->lookup("menCspaceOn")); + mBar->SetLabel(MENU_IMAGE_CSPACE_FULL, lman->lookup("menCspaceFull")); + mBar->SetLabel(MENU_IMAGE_CSPACE_PROJ, lman->lookup("menCspaceProj")); + mBar->SetLabel(MENU_IMAGE_CSPACE_EDIT, lman->lookup("menCspaceEdit")); + if (moviePossible()) + { + mBar->SetLabel(MENU_IMAGE_SETUP_MOVIE, lman->lookup("menImgSetupMovie")); + mBar->SetLabel(MENU_IMAGE_MOVIE_ONCE, lman->lookup("menImgMovieOnce")); + mBar->SetLabel(MENU_IMAGE_MOVIE_START, lman->lookup("menImgMovieStart")); + mBar->SetLabel(MENU_IMAGE_MOVIE_SWITCH, lman->lookup("menImgMovieSwitch")); + } + mBar->SetLabel(MENU_DISP_DATA_SAVETIFF, lman->lookup("menDispDataSaveTIFF")); + + rviewDisplay::label(); +} + + + +char *rviewImage::movieNewFrame(void) +{ + return NULL; +} + +int rviewImage::process(wxObject &obj, wxEvent &evt) +{ + RMDBGENTER(3, RMDebug::module_applications, "rviewImage", "process()"); + + int type = evt.GetEventType(); + + if (rviewDisplay::process(obj, evt) != 0) + { + return 1; + } + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (moviePossible()) + { + int oldDirection = playDirection; + + if (&obj == (wxObject*)playStop) + { + RMDBGMIDDLE(3, RMDebug::module_applications, "rviewImage", "process() Playback stop" ); + playDirection = 0; + return 1; + } + if (&obj == (wxObject*)playBack) + playDirection = -1; + else if (&obj == (wxObject*)playFwd) + playDirection = 1; + + // Only enter the loop if we didn't have playback before to avoid reentrancy. + if ((oldDirection == 0) && (playDirection != 0)) + { + char *data, *lastData; + + RMDBGONCE(3, RMDebug::module_applications, "rviewImage", "process() Playback start " << playDirection ); + do + { + while (advanceProjection(playDirection) != 0) + { + lastData = imgData; + + data = movieNewFrame(); + + if (data == NULL) + { + playDirection = 0; return 0; + } + updatePixmap(lastData, data); + ::wxYield(); + // Order is _vitally _ important here. Check _after_ the call to ::wxYield()! + if (playDirection == 0) break; + } + // Allow for one NULL cycle after playback (ensures no infinite loops if playback + // dimension is only 1 element wide. + ::wxYield(); + if (playDirection != 0) + { + if (mBar->Checked(MENU_IMAGE_MOVIE_ONCE)) + playDirection = 0; + else if (mBar->Checked(MENU_IMAGE_MOVIE_START)) + { + advanceProjection(-playDirection, display_advmode_reset); + } + else + playDirection = -playDirection; + } + } + while (playDirection != 0); + } + return 1; + } + } + + return 0; +} + + +bool rviewImage::moviePossible(void) const +{ + return FALSE; +} + +bool rviewImage::canRotateObject(void) const +{ + return FALSE; +} + +bool rviewImage::showScaleSlider(void) const +{ + return TRUE; +} + +void rviewImage::rotateObject(wxMouseEvent &mevt) +{ +} + +void rviewImage::processMouseEvent(wxMouseEvent &mevt) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewImage", "processMouseEvent()" ); + + int newbut=0; + + if (mevt.ControlDown()) newbut = MOUSE_CONTROL; + else if (!canRotateObject()) {mousebut = 0; return;} + + if (mevt.leftDown) newbut |= MOUSE_LEFT; + if (mevt.middleDown) newbut |= MOUSE_MIDDLE; + if (mevt.rightDown) newbut |= MOUSE_RIGHT; + + if (((newbut & MOUSE_CONTROL) == 0) && canRotateObject()) + { + rotateObject(mevt); + } + else + { + // Drag a box + if ((newbut & MOUSE_LEFT) != 0) + { + if ((mousebut & MOUSE_LEFT) == 0) + { + pcanv->SetDragBox(mevt.x, mevt.y, mevt.x, mevt.y); + } + else + { + pcanv->UpdateDragBox(mevt.x, mevt.y); + } + } + else if ((newbut & MOUSE_RIGHT) != 0) + { + if ((mousebut & MOUSE_RIGHT) == 0) + { + pcanv->AdjustDragBox(mevt.x, mevt.y); + } + else + { + pcanv->UpdateDragBox(mevt.x, mevt.y); + } + } + } + + mousebut = newbut; + mousex = mevt.x; mousey = mevt.y; +} + + +void rviewImage::updatePixmap(char *oldData, char *newData) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewImage", "updatePixmap()" ); + + if (pixmap == NULL) return; + + if (oldData != newData) + { + pixmap->newPixmap((wxWindow*)pcanv, pixWidth, pixHeight, pixDepth, pixPad, imgData, getPixmapFlags() | WX_PIXFLAG_SAMEPALETTE); + pcanv->setPixmap(pixmap); + } + pcanv->updateDisplay((oldData != newData)); +} + + +int rviewImage::getPixmapFlags(void) +{ + int pixflags = WX_PIXFLAG_TRANSLATE; + + if (prefs->imgDither) + { + if (prefs->ditherBest) + pixflags |= WX_PIXFLAG_DITHER; + else + pixflags |= WX_PIXFLAG_FASTDITHER; + } + return pixflags; +} + + + +void rviewImage::OnSize(int w, int h) +{ + int x, y, pos; + bool resize=false; + + RMDBGONCE(3, RMDebug::module_applications, "rviewImage", "OnSize " << w << ", " << h ); + + // fully initialized yet? + if (initPhaseFinished) + { + GetClientSize(&x, &y); + + if ((frameWidth == w) && (frameHeight == h)) + return; + + frameWidth = w; + frameHeight = h; + + x -= 2*display_cnvborder; y -= 2*display_cnvborder + totalCtrlHeight; + pcanv->SetSize(display_cnvborder, display_cnvborder + totalCtrlHeight, x, y); + + // Ctrl panel items + if (scaleSlider != NULL) + scaleSlider->SetSize(display_border, image_totaly - image_sheight, x, image_sheight); + + pos = x - 3*image_pbwidth - display_border + 2*display_cnvborder; + // Buttons might not be present + if (playBack != NULL) + playBack->SetSize(pos, display_cheight, image_pbwidth, image_pbheight); + if (playStop != NULL) + playStop->SetSize(pos + image_pbwidth, display_cheight, image_pbwidth, image_pbheight); + if (playFwd != NULL) + playFwd->SetSize(pos + 2*image_pbwidth, display_cheight, image_pbwidth, image_pbheight); + } + + rviewDisplay::OnSize(w, h); +} + + +void rviewImage::OnMenuCommand(int id) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewImage", "OnMenuCommand()" ); + + switch (id) + { + case MENU_DISP_DATA_SAVETIFF: + { + char *file; + char *prefDir = (char*)(prefs->filePath.ptr()); + file = ::wxFileSelector(lman->lookup("saveTIFF"), (::wxDirExists(prefDir)) ? prefDir : NULL, NULL, NULL, "*", 0, this); + if (file != NULL) + { + ::wxBeginBusyCursor(); + rviewIO::PixmapToTIFF(pixmap, file, prefs->vffParams.ptr()); + prefs->filePath = ::wxPathOnly(file); + ::wxEndBusyCursor(); + } + } + break; + case MENU_IMAGE_CSPACE_EDIT: + if (csmap != NULL) + { + csmap->openEditor(); + } + break; + case MENU_IMAGE_MOVIE_ONCE: + case MENU_IMAGE_MOVIE_START: + case MENU_IMAGE_MOVIE_SWITCH: + if (moviePossible()) + { + mBar->Check(lastMovieMode, FALSE); mBar->Check(id, TRUE); lastMovieMode = id; + } + break; + case MENU_IMAGE_CSPACE_ON: + case MENU_IMAGE_CSPACE_FULL: + case MENU_IMAGE_CSPACE_PROJ: + { + if (id == MENU_IMAGE_CSPACE_ON) + { + // You're not allowed to leave cspace mode for these type/mode combinations + if (modeNeedsCspace(baseType)) + { + mBar->Check(MENU_IMAGE_CSPACE_ON, TRUE); + } + else + { + doValToCspace = mBar->Checked(MENU_IMAGE_CSPACE_ON); + mBar->Enable(MENU_IMAGE_CSPACE_FULL, doValToCspace); + mBar->Enable(MENU_IMAGE_CSPACE_PROJ, doValToCspace); + mBar->Enable(MENU_IMAGE_CSPACE_EDIT, doValToCspace); + } + } + else if (id == MENU_IMAGE_CSPACE_FULL) + { + doFullRangeCspace = mBar->Checked(MENU_IMAGE_CSPACE_FULL); + if (csmap != NULL) + { + csmap->processRange((doFullRangeCspace) ? CSPACE_RANGE_FULL : CSPACE_RANGE_ACTUAL); + } + } + else if (id == MENU_IMAGE_CSPACE_PROJ) + { + setCspaceProjMode(mBar->Checked(MENU_IMAGE_CSPACE_PROJ)); + if (csmap != NULL) + { + csmap->updateProjection(csInterv); + } + } + newProjection(); + } + break; + default: + rviewDisplay::OnMenuCommand(id); + break; + } +} + + +// Refuse to be killed if animation is in progress +bool rviewImage::OnClose(void) +{ + if (playDirection != 0) return FALSE; + return TRUE; +} + + +int rviewImage::userEvent(const user_event &ue) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewImage", "userEvent()" ); + + if ((ue.type == usr_cspace_changed) && (ue.data == (void*)csmap) && (doValToCspace)) + { + // The csmapper has already updated the tables and internal information + newProjection(); + return 1; + } + // Don't intercept unknown messages! + return rviewDisplay::userEvent(ue); +} + + + +void rviewImage::prepareToDie(void) +{ + // Stop movie-playback immediately. + playDirection = 0; + ::wxYield(); +} + + +int rviewImage::requestQuit(int level) +{ + playDirection = 0; + return rviewFrame::requestQuit(level); +} + + +void rviewImage::resizeImage(void) +{ + int x, y, sx, sy; + + if (scrollx >= 0) + scrollx = pcanv->GetScrollPos(wxHORIZONTAL); + else + scrollx = 0; + + if (scrolly >= 0) + scrolly = pcanv->GetScrollPos(wxVERTICAL); + else + scrolly = 0; + + pcanv->GetVirtualSize(&x, &y); + sx = (pixWidth + display_scrstep - 1) / display_scrstep; + sy = (pixHeight + display_scrstep - 1) / display_scrstep; + + // Only update the canvas size if this is absolutely necessary to avoid flicker. + if ((x != display_scrstep*sx) || (y != display_scrstep*sy)) + { + pcanv->SetScrollbars(display_scrstep, display_scrstep, sx, sy, display_pgstep, display_pgstep, scrollx, scrolly); + } +} + + +void rviewImage::ensureViewCspace(void) +{ + if (cspar == NULL) + { + cspar = new colourspace_params; + memset(cspar, 0, sizeof(colourspace_params)); + } +} + + +void rviewImage::deleteViewCspace(void) +{ + if (cspar != NULL) + { + delete cspar; + cspar = NULL; + } +} + + +int rviewImage::saveView(FILE *fp) +{ + int status = rviewDisplay::saveView(fp); + + long spos[2]; + spos[0] = (long)scrollx; spos[1] = (long)scrolly; + + writeViewParam(fp, view_ScrollPos, 2, spos); + writeViewParam(fp, view_UseCspace, (long)doValToCspace); + writeViewParam(fp, view_CspaceFull, (long)doFullRangeCspace); + writeViewParam(fp, view_CspaceProj, (long)doProjRangeCspace); + writeViewParam(fp, view_ScaleValue, scaleValue / 100.0); + if (csmap != NULL) + { + colourspace_params par; + double cvals[3]; + csmap->getParameters(&par); + cvals[0] = par.peak_red; cvals[1] = par.peak_green; cvals[2] = par.peak_blue; + writeViewParam(fp, view_CspaceMeans, 3, cvals); + cvals[0] = par.sigm_red; cvals[1] = par.sigm_green; cvals[2] = par.sigm_blue; + writeViewParam(fp, view_CspaceSigmas, 3, cvals); + cvals[0] = par.minVal; cvals[1] = par.maxVal; + writeViewParam(fp, view_CspaceRange, 2, cvals); + writeViewParam(fp, view_CspaceType, (long)par.type); + } + return status; +} + + +int rviewImage::readView(const char *key, const char *value) +{ + int status = rviewDisplay::readView(key, value); + + if (status == 0) + { + if (strcmp(key, view_ScrollPos) == 0) + { + long spos[2]; + if (readVector(value, 2, spos) == 0) + { + scrollx = (int)spos[0]; scrolly = (int)spos[1]; + } + return 1; + } + else if (strcmp(key, view_UseCspace) == 0) + { + doValToCspace = (bool)atoi(value); + return 1; + } + else if (strcmp(key, view_CspaceFull) == 0) + { + doFullRangeCspace = (bool)atoi(value); + return 1; + } + else if (strcmp(key, view_CspaceProj) == 0) + { + doProjRangeCspace = (bool)atoi(value); + return 1; + } + else if (strcmp(key, view_CspaceMeans) == 0) + { + double mean[3]; + ensureViewCspace(); + if (readVector(value, 3, mean) == 0) + { + cspar->peak_red = mean[0]; cspar->peak_green = mean[1]; cspar->peak_blue = mean[2]; + } + return 1; + } + else if (strcmp(key, view_CspaceSigmas) == 0) + { + double sigm[3]; + ensureViewCspace(); + if (readVector(value, 3, sigm) == 0) + { + cspar->sigm_red = sigm[0]; cspar->sigm_green = sigm[1]; cspar->sigm_blue = sigm[2]; + } + return 1; + } + else if (strcmp(key, view_CspaceRange) == 0) + { + ensureViewCspace(); + double crange[2]; + if (readVector(value, 2, crange) == 0) + { + cspar->minVal = crange[0]; cspar->maxVal = crange[1]; + } + return 1; + } + else if (strcmp(key, view_CspaceType) == 0) + { + ensureViewCspace(); + cspar->type = (cspaceType)atoi(value); + return 1; + } + else if (strcmp(key, view_ScaleValue) == 0) + { + scaleValue = 100*atof(value); + return 1; + } + return 0; + } + return status; +} + + +void rviewImage::loadViewFinished(void) +{ + rviewDisplay::loadViewFinished(); + + pcanv->SetScrollPos(wxHORIZONTAL, scrollx); + pcanv->SetScrollPos(wxVERTICAL, scrolly); + + if (scaleSlider != NULL) + scaleSlider->SetValue((int)scaleValue); + + mBar->Check(MENU_IMAGE_CSPACE_ON, doValToCspace); + mBar->Check(MENU_IMAGE_CSPACE_FULL, doFullRangeCspace); + mBar->Check(MENU_IMAGE_CSPACE_PROJ, doProjRangeCspace); + mBar->Enable(MENU_IMAGE_CSPACE_ON, doValToCspace); + mBar->Enable(MENU_IMAGE_CSPACE_FULL, doValToCspace); + mBar->Enable(MENU_IMAGE_CSPACE_PROJ, doValToCspace); + mBar->Enable(MENU_IMAGE_CSPACE_EDIT, doValToCspace); +} + + + + + +/* + * Flat image base class members + */ +rviewFlatBaseImage::rviewFlatBaseImage(mdd_frame *mf, int es, unsigned int flags) : rviewImage(mf, es, flags) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewFlatBaseImage", "rviewFlatImage()" ); +} + + +rviewFlatBaseImage::~rviewFlatBaseImage(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewFlatBaseImage", "~rviewFlatBaseImage()" ); +} + + +int rviewFlatBaseImage::openViewer(void) +{ + if (dimMDD < 2) + { + rviewErrorbox::reportError(lman->lookup("errorModeDim"), rviewFlatBaseImage::getFrameName(), "openViewer"); + objectInitializedOK = FALSE; + return -1; + } + return rviewImage::openViewer(); +} + + +const char *rviewFlatBaseImage::getFrameName(void) const +{ + return "rviewFlatBaseImage"; +} + +rviewFrameType rviewFlatBaseImage::getFrameType(void) const +{ + return rviewFrameTypeFltBsImage; +} + + +int rviewFlatBaseImage::newProjection(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewFlatBaseImage", "newProjection()" ); + + char *data, *lastData = imgData; + + if ((data = projectImage()) == NULL) return -1; + + updatePixmap(lastData, data); + + return 0; +} + + +char *rviewFlatBaseImage::initMode(void) +{ + int i=0, j=0, w=0, h=0; + char *data=0; + + RMDBGONCE(3, RMDebug::module_applications, "rviewFlatBaseImage", "initMode()" ); + + // Initialise the projection string. Use a static default for now, later on use the + // last value used. + data = projString; + data += sprintf(data, "*:*, *:*"); + for (i=2; i<dimMDD; i++) + { + data += sprintf(data, ", %ld", interv[i].low()); + } + project->SetValue(projString); + + // This returns a pointer to the projected MDD data, ready for display by wxPixmap. + // It will also set the variables pixWidth, pixHeight. + data = projectImage(); + + // Calculate the size of the whole window to display the entire image by + // first examining the difference between the frame's size and the canvas' + // client size (i.e. without the scrollbars) + GetSize(&i, &j); + pcanv->GetClientSize(&w, &h); + w = pixWidth + (i-w); h = pixHeight + (j-h); + + // Limit the size of the window to the values specified in prefs. + if (w > prefs->maxDWidth) w = prefs->maxDWidth; + if (h > prefs->maxDHeight) h = prefs->maxDHeight; + + // ... then set the window size. + SetSize(-1, -1, w, h); + + setModeDimension(2); + + return data; +} + + +char *rviewFlatBaseImage::projectImage(void) +{ + int dim1, dim2; + + RMDBGONCE(3, RMDebug::module_applications, "rviewFlatBaseImage", "projectImage()" ); + + mapIndex = r_Point(dimMDD); + if (freeDimsFromProjection(dim1, dim2, &mapIndex) != 0) return NULL; + + projectObjectHook(); + + rviewFlatProjEnv penv; + + penv.mddPtr = mddObj.ptr(); + penv.pt1 = pt1; penv.pt2 = pt2; + penv.dim1 = dim1; penv.dim2 = dim2; + penv.bt = baseType; + penv.doCspace = doValToCspace; + penv.scale = scaleValue / 100; + if (penv.scale <= 0) penv.scale = 0.01; + + if (rviewPrepareFlatProjection(penv) != 0) return NULL; + + if (doValToCspace) + { + // init if necessary + setCspaceProjMode(doProjRangeCspace); + // No need for the virtual pitch here, as the image buffer won't be filled with data of the base type + penv.cspaceState = rviewCheckInitCspace(baseType, &csmap, mddObj, doFullRangeCspace, csInterv, penv.width, &penv.pitch, &penv.depth, &penv.pad, NULL, cspar); + if (csmap != NULL) + { + mBar->Enable(MENU_IMAGE_CSPACE_EDIT, TRUE); + if (doProjRangeCspace) + { + setCspaceProjMode(doProjRangeCspace); + csmap->updateProjection(csInterv); + } + } + deleteViewCspace(); + } + else + penv.cspaceState = 0; + + penv.csmap = csmap; + + // Only allocate a new array if it's the first time or the size has changed. + if ((penv.width != pixWidth) || (penv.height != pixHeight) || (pixPad != penv.pad) || (pixDepth != penv.depth) || (penv.pitch != virtualPitch)) + { + pixWidth = penv.width; pixHeight = penv.height; + pixPad = penv.pad; pixPitch = penv.pitch; + pixDepth = penv.depth; virtualPitch = penv.pitch; + if ((imgData = (char*)malloc(penv.pitch * pixHeight)) == NULL) + { + rviewErrorbox::reportError(lman->lookup("errorMemory"), rviewFlatBaseImage::getFrameName(), "projectImage"); + return NULL; + } + } + + if (rviewPerformFlatProjection(penv, imgData) != 0) return NULL; + + resizeImage(); + + return imgData; +} + + + + +/* + * Standard flat images + */ + +rviewFlatImage::rviewFlatImage(mdd_frame *mf, unsigned int flags) : rviewFlatBaseImage(mf, 0, flags) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewFlatImage", "rviewFlatImage()" ); +} + +rviewFlatImage::~rviewFlatImage(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewFlatImage", "~rviewFlatImage()" ); + closeViewer(); +} + +void rviewFlatImage::OnSize(int w, int h) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewFlatImage", "OnSize()"); + + if (initPhaseFinished) + { + int x, y, step, posx, posy; + + GetClientSize(&x, &y); + + if(x < 6*image_bwidth) + { + x= 6*image_bwidth; + frameWidth=x; + frameHeight=y; + SetClientSize(x, y); + return; + } + } + rviewFlatBaseImage::OnSize(w, h); +} +int rviewFlatImage::openViewer(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewFlatImage", "openViewer()" ); + + if (rviewFlatBaseImage::openViewer() == 0) + { + openViewerEpilogue(rviewFlatImage::getFrameType()); + return 0; + } + return -1; +} + + +const char *rviewFlatImage::getFrameName(void) const +{ + return "rviewFlatImage"; +} + +rviewFrameType rviewFlatImage::getFrameType(void) const +{ + return rviewFrameTypeFlatImage; +} + +int rviewFlatImage::getViewerType(void) const +{ + return RVIEW_RESDISP_IMGFLAT; +} + + +bool rviewFlatImage::moviePossible(void) const +{ + return (dimMDD >= 3); +} + + +char *rviewFlatImage::movieNewFrame(void) +{ + return projectImage(); +} + +void rviewFlatImage::label(void) +{ + setDisplayTitle(lman->lookup("titleImageFlat")); + rviewFlatBaseImage::label(); +} + + +int rviewFlatImage::process(wxObject &obj, wxEvent &evt) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewFlatImage", "process()" ); + + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_SLIDER_COMMAND) + { + scaleValue = (double)(scaleSlider->GetValue()); + newProjection(); + return 1; + } + + return rviewImage::process(obj, evt); +} + + +char *rviewFlatImage::initMode(void) +{ + if (playBack != NULL) + playBack->Enable(TRUE); + if (playFwd != NULL) + playFwd->Enable(TRUE); + + return rviewFlatBaseImage::initMode(); +} + + + + + + + + +/* + * Rendered image class members + */ + +const char *rviewRenderImage::view_ZProject = "zProject"; +const char *rviewRenderImage::view_ZClip = "zClip"; +const char *rviewRenderImage::view_PixThreshLow = "pixThreshLow"; +const char *rviewRenderImage::view_PixThreshHigh = "pixThreshHigh"; +const char *rviewRenderImage::view_WeightThresh = "weightThresh"; +const char *rviewRenderImage::view_WeightQuant = "weightQuant"; +const char *rviewRenderImage::view_UseRGBBright = "useRgbBright"; +const char *rviewRenderImage::view_UseLighting = "useLighting"; +const char *rviewRenderImage::view_LightAmbient = "lightAmbient"; +const char *rviewRenderImage::view_LightGain = "lightGain"; +const char *rviewRenderImage::view_LightAngle = "lightAngle"; +const char *rviewRenderImage::view_LightScint = "lightScintAngle"; +const char *rviewRenderImage::view_LightDir = "lightDirection"; +const char *rviewRenderImage::view_LightDist = "lightDistance"; +const char *rviewRenderImage::view_KernelSize = "kernelSize"; +const char *rviewRenderImage::view_KernelType = "kernelType"; +const char *rviewRenderImage::view_UseVoxColour = "useVoxColour"; +const char *rviewRenderImage::view_VoxColour = "voxColour"; +const char *rviewRenderImage::view_GridSize = "gridSize"; +const char *rviewRenderImage::view_ScaleHeight = "scaleHeight"; +const char *rviewRenderImage::view_Rotation = "rotation"; +const char *rviewRenderImage::view_ZOffset = "zOffset"; + +rviewRenderImage::rviewRenderImage(mdd_frame *mf, int es, unsigned int flags) : rviewImage(mf, es, flags) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "rviewRenderImage()" ); + + // make destructor-safe first + setupWindow = NULL; rcontrol = NULL; rcurview = NULL; + geomData = NULL; geomUse = NULL; rot = NULL; graphEnv = NULL; + + // Default renderer parameters + setup.zpro = prefs->imgZpro; setup.clipz = prefs->imgClipz; + drx = 0.0; dry = 0.0; drz = 0.0; + + // Default voxel parameters. Try to use halfway sensible defaults. Values of -1 + // saved in the preferences object are substituted by default-values + setup.useRgbBrightness = prefs->imgRgbBrightness; + setup.weightQuantisation = 4; + setup.pixelThresholdLow = prefs->imgPixThreshLow; + setup.pixelThresholdHigh = prefs->imgPixThreshHigh; + setup.weightThreshold = prefs->imgWgtThresh; + // Light defaults + setup.useLights = prefs->imgLight; + setup.lightsAngle = prefs->imgLightAngle; + setup.lightsScintAngle = prefs->imgLightScintAngle; + setup.lightsAmbient = prefs->imgLightAmbient; + setup.lightsGain = prefs->imgLightGain; + setup.lightsDir = rviewImageSetup::parseLightDirection(prefs->imgLightDir); + setup.lightsDist = prefs->imgLightDist; + setup.kernelSize = prefs->imgKernSize; setup.kernelType = prefs->imgKernType + 1; + setup.useVoxCol = prefs->imgUseVCol; setup.voxColour = prefs->imgVoxColour; + // Height field defaults + setup.gridSize = prefs->imgHeightGrid; setup.scaleHeight = prefs->imgHeightScale; + + rendererPlayback = 0; + + geomData = new vertex_fp[4]; geomUse = new vertex_fp[4]; + rot = new vertex_fp[3]; + graphEnv = new graph_env; +} + + +rviewRenderImage::~rviewRenderImage(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "~rviewRenderImage()" ); + + if (setupWindow != NULL) + { + setupWindow->unlinkParent(); + setupWindow->Close(TRUE); + } + if (rcontrol != NULL) + { + rcontrol->unlinkParent(); + rcontrol->Close(TRUE); + } + if (rcurview != NULL) + { + rcurview->unlinkParent(); + rcurview->Close(TRUE); + } + if(geomData) + { + delete [] geomData; + geomData=0; + } + + if(geomUse) + { + delete [] geomUse; + geomUse=0; + } + if(rot) + { + delete [] rot; + rot=0; + } + + if(graphEnv) + { + delete graphEnv; + graphEnv=0; + } +} + + +const char *rviewRenderImage::getFrameName(void) const +{ + return "rviewRenderImage"; +} + +rviewFrameType rviewRenderImage::getFrameType(void) const +{ + return rviewFrameTypeRndImage; +} + + +int rviewRenderImage::configMenuInitHook(wxMenu *menu) +{ + menu->Append(MENU_IMAGE_SETUP_RENDER, ""); + menu->Append(MENU_IMAGE_SETUP_RCONTROL, ""); + menu->AppendSeparator(); + return 3; +} + + +int rviewRenderImage::viewMenuInitHook(wxMenu *menu) +{ + menu->Append(MENU_DISP_VIEW_SHOW, ""); + return 1; +} + + +void rviewRenderImage::label(void) +{ + mBar->SetLabel(MENU_IMAGE_SETUP_RENDER, lman->lookup("menImgSetupRender")); + mBar->SetLabel(MENU_IMAGE_SETUP_RCONTROL, lman->lookup("menImgSetupRctrl")); + mBar->SetLabel(MENU_DISP_VIEW_SHOW, lman->lookup("menDispViewShow")); + + rviewImage::label(); +} + + +int rviewRenderImage::newProjection(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "newProjection()" ); + + char *data, *lastData = imgData; + + if ((data = setupGraphEnv()) == NULL) return -1; + fillBuffer(); + + updatePixmap(lastData, data); + + return 0; +} + + +void rviewRenderImage::updateCurrentView(void) +{ + if (rcurview != NULL) + { + vertex_fp angles; + matrixToAngles(angles); + rcurview->updateView(angles, zoff, cubeScale); + } +} + + +int rviewRenderImage::process(wxObject &obj, wxEvent &evt) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "process()" ); + + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_SLIDER_COMMAND) + { + scaleValue = (double)(scaleSlider->GetValue()); + cubeScale = scaleValue / 100.0; + if (cubeScale < 0.01) cubeScale = 0.01; + fillBuffer(); + pcanv->updateDisplay(TRUE); + updateCurrentView(); + return 1; + } + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)playStop) + { + rendererPlayback = 0; + if (rcontrol != NULL) + rcontrol->setActiveMode(0); + + return 1; + } + } + + return rviewImage::process(obj, evt); +} + + +bool rviewRenderImage::canRotateObject(void) const +{ + return TRUE; +} + + +void rviewRenderImage::rotateObject(wxMouseEvent &mevt) +{ + float pos; + + // Rotate / translate 3D object + if (mevt.leftDown) + { + pos = mevt.x - mousex; + rotateCube(1, pos / 100); + pos = mevt.y - mousey; + rotateCube(0, pos / 100); + fillBuffer(); + pcanv->updateDisplay(); + updateCurrentView(); + } + + if (mevt.rightDown) + { + pos = mevt.y - mousey; + zoff += (long)pos; + fillBuffer(); + pcanv->updateDisplay(); + updateCurrentView(); + } +} + + +void rviewRenderImage::OnSize(int w, int h) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "OnSize()" ); + + int oldwidth = frameWidth; + int oldheight = frameHeight; + + rviewImage::OnSize(w, h); + + // Fully initialized yet? + if ((initPhaseFinished) && ((oldwidth != frameWidth) || (oldheight != frameHeight))) + { + char *data, *lastData = imgData; + + if ((data = setupGraphEnv()) != NULL) + { + fillBuffer(); + updatePixmap(lastData, data); + } + } +} + + +void rviewRenderImage::OnMenuCommand(int id) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "OnMenuCommand()" ); + + switch (id) + { + case MENU_IMAGE_SETUP_RENDER: + if (setupWindow == NULL) + { + setupWindow = new rviewImageSetup(&setup, this); + } + break; + case MENU_IMAGE_SETUP_RCONTROL: + if (rcontrol == NULL) + { + rcontrol = new rendererControl(drx, dry, drz, rendererPlayback, this); + } + break; + case MENU_DISP_VIEW_SHOW: + if (rcurview == NULL) + { + vertex_fp angles; + matrixToAngles(angles); + rcurview = new rendererCurrentView(angles, zoff, cubeScale, this); + } + break; + default: + rviewImage::OnMenuCommand(id); + break; + } +} + + +void rviewRenderImage::closeEditor(bool newSetup) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "closeEditor()" ); + + if (newSetup) + { + updateSettings(FALSE); + } + setupWindow->Close(TRUE); + setupWindow = NULL; +} + + +void rviewRenderImage::redrawSettingsChanged(void) +{ + fillBuffer(); + pcanv->updateDisplay(); +} + + +void rviewRenderImage::updateSettings(int setFlags) +{ + bool doUpt = FALSE; + + if (setFlags == 0) + doUpt = TRUE; + else + doUpt = doUpdate(setFlags); + + if (doUpt) + { + redrawSettingsChanged(); + } +} + + +bool rviewRenderImage::OnClose(void) +{ + if (rendererPlayback != 0) return FALSE; + return rviewImage::OnClose(); +} + + +int rviewRenderImage::userEvent(const user_event &ue) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "userEvent()" ); + + if (ue.type == usr_child_closed) + { + if (ue.data == (void*)setupWindow) + { + setupWindow = NULL; + return 1; + } + else if (ue.data == (void*)rcontrol) + { + rcontrol = NULL; + return 1; + } + else if (ue.data == (void*)rcurview) + { + rcurview = NULL; + return 1; + } + } + return rviewImage::userEvent(ue); +} + + +void rviewRenderImage::closeRendererControls(void) +{ + if (rcontrol != NULL) + { + rcontrol->Close(TRUE); + rcontrol = NULL; + } +} + + +void rviewRenderImage::prepareToDie(void) +{ + rendererPlayback = 0; + rviewImage::prepareToDie(); +} + + +int rviewRenderImage::requestQuit(int level) +{ + rendererPlayback = 0; + return rviewImage::requestQuit(level); +} + + +void rviewRenderImage::setAutoRotation(float rx, float ry, float rz) +{ + //cout << "rotate " << rx << ", " << ry << ", " << rz << endl; + + if (rx >= 100.0) + { + rendererPlayback = 0; return; + } + drx = rx; dry = ry; drz = rz; + + // No re-entrancy + if (rendererPlayback != 0) return; + + rendererPlayback = 1; + + while (rendererPlayback != 0) + { + rotateCube(0, M_PI*drx/10); rotateCube(1, M_PI*dry/10); rotateCube(2, M_PI*drz/10); + updateCurrentView(); + newProjection(); + ::wxYield(); + } +} + + +void rviewRenderImage::setCurrentView(const vertex_fp &angles, long off, double scale) +{ + anglesToMatrix(angles); + zoff = off; + cubeScale = scale; + scaleSlider->SetValue((int)(100*cubeScale)); + + newProjection(); +} + + +char *rviewRenderImage::initMode(void) +{ + int i; + char *data; + + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "initMode()" ); + + // Scaling factor for the data cube. + cubeScale = scaleValue / 100.0; + if (cubeScale < 0.01) cubeScale = 0.01; + + // Init rotation matrix + for (i=0; i<3; i++) + { + rot[i].x = 0.0; rot[i].y = 0.0; rot[i].z = 0.0; + } + rot[0].x = 1.0; rot[1].y = 1.0; rot[2].z = 1.0; + + zoff = 0; + + data = setupGraphEnv(); + + if (initPhaseFinished) fillBuffer(); + + // Do not update the pixmap here! That's done by the calling procedure, if necessary + + resizeImage(); + + return data; +} + + +char *rviewRenderImage::setupGraphEnv(void) +{ + int w, h; + + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "setupGraphEnv()" ); + + if (rviewParseProjection(getVirtualDomain(), pt1, pt2, projString, &freeDims) != dimMDD) + { + rviewErrorbox::reportError(lman->lookup("errorProjection"), rviewRenderImage::getFrameName(), "setupGraphEnv"); + return NULL; + } + + pcanv->GetClientSize(&w, &h); + + setupEnvironment(w, h); + + graphEnv->zpro = setup.zpro; graphEnv->clipz = setup.clipz; + graphEnv->clipl = -pixWidth/2; graphEnv->clipr = graphEnv->clipl + pixWidth - 1; + graphEnv->clipd = -pixHeight/2; graphEnv->clipu = graphEnv->clipd + pixHeight - 1; + graphEnv->midx = -graphEnv->clipl; graphEnv->midy = graphEnv->clipu; + /* lineadd set in one of the modules above */ + graphEnv->dest = (void*)imgData; + + return imgData; +} + + +void rviewRenderImage::rotateCube(int axis, float angle, vertex_fp *matrix) +{ + real_t c, s; + vertex_fp h; + int i, j; + + c = cos(angle); s = sin(angle); + + switch (axis) + { + case 0: i = 1; j = 2; break; + case 1: i = 2; j = 0; break; + case 2: i = 0; j = 1; break; + default: + cerr << "Bad rotation axis " << axis; + return; + } + h.x = c * matrix[i].x + s * matrix[j].x; + h.y = c * matrix[i].y + s * matrix[j].y; + h.z = c * matrix[i].z + s * matrix[j].z; + matrix[j].x = c * matrix[j].x - s * matrix[i].x; + matrix[j].y = c * matrix[j].y - s * matrix[i].y; + matrix[j].z = c * matrix[j].z - s * matrix[i].z; + matrix[i].x = h.x; + matrix[i].y = h.y; + matrix[i].z = h.z; + + /*for (i=0; i<3; i++) + { + cout << '[' << matrix[i].x << ',' << matrix[i].y << ',' << matrix[i].z << ']'; + } + cout << endl;*/ + +} + +void rviewRenderImage::rotateCube(int axis, float angle) +{ + rotateCube(axis, angle, rot); +} + + +void rviewRenderImage::getLightPos(vertex_fp *lpos) +{ + lpos->x = 0; lpos->y = 0; lpos->z = 0; + if ((setup.lightsDir & RVIEW_LIGHTDIR_LEFT) != 0) lpos->x = -1.0; + else if ((setup.lightsDir & RVIEW_LIGHTDIR_RIGHT) != 0) lpos->x = 1.0; + if ((setup.lightsDir & RVIEW_LIGHTDIR_DOWN) != 0) lpos->y = -1.0; + else if ((setup.lightsDir & RVIEW_LIGHTDIR_UP) != 0) lpos->y = 1.0; + if ((setup.lightsDir & RVIEW_LIGHTDIR_FRONT) != 0) lpos->z = -1.0; + else if ((setup.lightsDir & RVIEW_LIGHTDIR_BACK) != 0) lpos->z = 1.0; + if (setup.lightsDir != 0) + { + double h; + + h = ((double) setup.lightsDist) /sqrt((lpos->x * lpos->x) + (lpos->y * lpos->y) + (lpos->z * lpos->z)); + lpos->x *= h; lpos->y *= h; lpos->z *= h; + } + lpos->z += graphEnv->zpro; +} + + +// not correct for x==0, but I don't need that case +#define SIGN(x) ((x > 0) ? 1 : -1) + +#define PRINT_MATRIX(mat) { \ + for (unsigned int i=0; i<3; i++) \ + cout << i << ": " << (mat)[i].x << ", " << (mat)[i].y << ", " << (mat)[i].z << endl; \ + } + +void rviewRenderImage::matrixToAngles(vertex_fp &angles) const +{ + vertex_fp matrix[3]; + + // make copy of rotation matrix for working + memcpy(&matrix, rot, 3*sizeof(vertex_fp)); + + // Successively rotate it back to identity. Getting this right is a major headache; + // one must bear in mind that rot[] are row-vectors and that atan() only returns + // values in -pi/2 to pi/2, so the result may have to be shifted by pi. + if (matrix[1].x == 0.0) + { + angles.z = 0.0; + } + else + { + angles.z = -((matrix[0].x == 0.0) ? SIGN(matrix[1].x) * M_PI/2 : atan(matrix[1].x / matrix[0].x)); + } + if (matrix[0].x < 0) angles.z += M_PI; + if (angles.z != 0.0) + { + rotateCube(2, -angles.z, matrix); + //cout << "ROTz" << endl; PRINT_MATRIX(matrix); + } + + if (matrix[2].x == 0.0) + { + angles.y = 0.0; + } + else + { + angles.y = -((matrix[0].x == 0.0) ? -SIGN(matrix[2].x) * M_PI/2 : -atan(matrix[2].x / matrix[0].x)); + rotateCube(1, -angles.y, matrix); + //cout << "ROTy" << endl; PRINT_MATRIX(matrix); + } + + if (matrix[2].y == 0.0) + { + angles.x = 0.0; + } + else + { + angles.x = -((matrix[1].y == 0.0) ? SIGN(matrix[2].y) * M_PI/2 : atan(matrix[2].y / matrix[1].y)); + } + if (matrix[1].y < 0) angles.x += M_PI; + if (angles.x != 0.0) + { + rotateCube(0, -angles.x, matrix); + //cout << "ROTx" << endl; PRINT_MATRIX(matrix); + } + +#if 0 + rotateCube(0, angles.x, matrix); + rotateCube(1, angles.y, matrix); + rotateCube(2, angles.z, matrix); + unsigned int i; + double res; + for (i=0, res=0.0; i<3; i++) + { + cout << "REC " << i << ": " << matrix[i].x << ", " << matrix[i].y << ", " << matrix[i].z + << " vs. " << rot[i].x << ", " << rot[i].y << ", " << rot[i].z << endl; + res += (matrix[i].x - rot[i].x) * (matrix[i].x - rot[i].x) + (matrix[i].y - rot[i].y) * (matrix[i].y - rot[i].y) + (matrix[i].z - rot[i].z) * (matrix[i].z - rot[i].z); + } + cout << "Residuum " << res << endl; +#endif +} + + +void rviewRenderImage::anglesToMatrix(const vertex_fp &angles) +{ + unsigned int i; + + for (i=0; i<3; i++) + { + rot[i].x = 0.0; rot[i].y = 0.0; rot[i].z = 0.0; + } + rot[0].x = 1.0; rot[1].y = 1.0; rot[2].z = 1.0; + + rotateCube(0, angles.x); + rotateCube(1, angles.y); + rotateCube(2, angles.z); +} + + +int rviewRenderImage::saveView(FILE *fp) +{ + int status = rviewImage::saveView(fp); + + writeViewParam(fp, view_ZProject, (long)setup.zpro); + writeViewParam(fp, view_ZClip, (long)setup.clipz); + writeViewParam(fp, view_PixThreshLow, setup.pixelThresholdLow); + writeViewParam(fp, view_PixThreshHigh, setup.pixelThresholdHigh); + writeViewParam(fp, view_WeightThresh, setup.weightThreshold); + writeViewParam(fp, view_WeightQuant, (long)setup.weightQuantisation); + writeViewParam(fp, view_UseRGBBright, (long)setup.useRgbBrightness); + writeViewParam(fp, view_UseLighting, (long)setup.useLights); + writeViewParam(fp, view_LightAmbient, setup.lightsAmbient); + writeViewParam(fp, view_LightGain, setup.lightsGain); + writeViewParam(fp, view_LightAngle, setup.lightsAngle); + writeViewParam(fp, view_LightScint, setup.lightsScintAngle); + writeViewParam(fp, view_LightDir, (long)setup.lightsDir); + writeViewParam(fp, view_LightDist, (long)setup.lightsDist); + writeViewParam(fp, view_KernelSize, (long)setup.kernelSize); + writeViewParam(fp, view_KernelType, (long)setup.kernelType); + writeViewParam(fp, view_UseVoxColour, (long)setup.useVoxCol); + writeViewParam(fp, view_VoxColour, setup.voxColour); + writeViewParam(fp, view_GridSize, (long)setup.gridSize); + writeViewParam(fp, view_ScaleHeight, setup.scaleHeight); + + vertex_fp angles; + double avals[3]; + matrixToAngles(angles); + avals[0] = angles.x; avals[1] = angles.y; avals[2] = angles.z; + writeViewParam(fp, view_Rotation, 3, avals); + writeViewParam(fp, view_ZOffset, zoff); + + return status; +} + + +int rviewRenderImage::readView(const char *key, const char *value) +{ + int status = rviewImage::readView(key, value); + + if (status == 0) + { + if (strcmp(key, view_ZProject) == 0) + { + setup.zpro = (unsigned long)atol(value); + return 1; + } + else if (strcmp(key, view_ZClip) == 0) + { + setup.clipz = (unsigned long)atol(value); + return 1; + } + else if (strcmp(key, view_PixThreshLow) == 0) + { + setup.pixelThresholdLow = atof(value); + return 1; + } + else if (strcmp(key, view_PixThreshHigh) == 0) + { + setup.pixelThresholdHigh = atof(value); + return 1; + } + else if (strcmp(key, view_WeightThresh) == 0) + { + setup.weightThreshold = atof(value); + return 1; + } + else if (strcmp(key, view_WeightQuant) == 0) + { + setup.weightQuantisation = atoi(value); + return 1; + } + else if (strcmp(key, view_UseRGBBright) == 0) + { + setup.useRgbBrightness = (bool)atoi(value); + return 1; + } + else if (strcmp(key, view_UseLighting) == 0) + { + setup.useLights = (bool)atoi(value); + return 1; + } + else if (strcmp(key, view_LightAmbient) == 0) + { + setup.lightsAmbient = atof(value); + return 1; + } + else if (strcmp(key, view_LightGain) == 0) + { + setup.lightsGain = atof(value); + return 1; + } + else if (strcmp(key, view_LightAngle) == 0) + { + setup.lightsAngle = atof(value); + return 1; + } + else if (strcmp(key, view_LightScint) == 0) + { + setup.lightsScintAngle = atof(value); + return 1; + } + else if (strcmp(key, view_LightDir) == 0) + { + setup.lightsDir = atoi(value); + return 1; + } + else if (strcmp(key, view_LightDist) == 0) + { + setup.lightsDist = atoi(value); + return 1; + } + else if (strcmp(key, view_KernelSize) == 0) + { + setup.kernelSize = atoi(value); + return 1; + } + else if (strcmp(key, view_KernelType) == 0) + { + setup.kernelType = atoi(value); + return 1; + } + else if (strcmp(key, view_UseVoxColour) == 0) + { + setup.useVoxCol = (bool)atoi(value); + return 1; + } + else if (strcmp(key, view_VoxColour) == 0) + { + setup.voxColour = atof(value); + return 1; + } + else if (strcmp(key, view_GridSize) == 0) + { + setup.gridSize = atoi(value); + return 1; + } + else if (strcmp(key, view_ScaleHeight) == 0) + { + setup.scaleHeight = atof(value); + return 1; + } + else if (strcmp(key, view_Rotation) == 0) + { + double avals[3]; + if (readVector(value, 3, avals) == 0) + { + vertex_fp angles; + angles.x = avals[0]; angles.y = avals[1]; angles.z = avals[2]; + anglesToMatrix(angles); + } + return 1; + } + else if (strcmp(key, view_ZOffset) == 0) + { + zoff = atol(value); + return 1; + } + return 0; + } + return status; +} + + +void rviewRenderImage::loadViewFinished(void) +{ + rviewImage::loadViewFinished(); + + if (setupWindow != NULL) + setupWindow->updateSettings(setup); + + if (rcurview != NULL) + { + vertex_fp angles; + matrixToAngles(angles); + rcurview->updateView(angles, zoff, cubeScale); + } + + cubeScale = scaleValue / 100.0; +} + + + +/* + * Handling the renderer buffer + */ + +#define FILL_BACKGROUND_CORE(type) \ + type value, *imgSrcPtr; \ + int fbcx, fbcy; \ + imgSrcPtr = (type*)(graphEnv->dest); value = (type)minVal; \ + for (fbcy=0; fbcy < pixHeight; fbcy++) \ + { \ + for (fbcx=0; fbcx < pixWidth; fbcx++) \ + { \ + imgSrcPtr[fbcx] = value; \ + } \ + imgSrcPtr = (type*)(((char*)imgSrcPtr) + virtualPitch); \ + } + +void rviewRenderImage::fillBackgroundCore(rviewBaseType bt, double minVal) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "fillBackgroundCore()" ); + + switch (bt) + { + case rbt_char: + { + FILL_BACKGROUND_CORE(r_Char); + } + break; + case rbt_uchar: + { + FILL_BACKGROUND_CORE(r_Octet); + } + break; + case rbt_short: + { + FILL_BACKGROUND_CORE(r_Short); + } + break; + case rbt_ushort: + { + FILL_BACKGROUND_CORE(r_UShort); + } + break; + case rbt_long: + { + FILL_BACKGROUND_CORE(r_Long); + } + break; + case rbt_ulong: + { + FILL_BACKGROUND_CORE(r_ULong); + } + break; + case rbt_float: + { + FILL_BACKGROUND_CORE(r_Float); + } + break; + case rbt_double: + { + FILL_BACKGROUND_CORE(r_Double); + } + break; + default: + break; + } +} + +void rviewRenderImage::fillBufferBackground(bool doCspace, bool &cspaceOK, r_Ref<r_GMarray> &obj, colourspaceMapper **csm, r_Minterval *csdom, rviewBaseType bt, bool fullRange, double *useMinVal) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "fillBufferBackground()" ); + + // In case of colourspace mapping and rendering don't fill the background with 0 but + // rather with the currently configured minimum value! + if (doCspace) + { + cspaceOK = FALSE; + if (rviewCheckInitCspace(bt, csm, obj, fullRange, csdom) != 0) + { + double minVal; + + cspaceOK = TRUE; + minVal = (useMinVal == NULL) ? (*csm)->getMinVal() : *useMinVal; + + fillBackgroundCore(bt, minVal); + } + } + else + { + memset(graphEnv->dest, 0, pixPitch * pixHeight); + } +} + + +// Macro for colourspace translation from/to various basetypes +// Fill in inverse order because source type may be smaller than destination type +#define TRANSLATE_TO_COLOURSPACE15(type) \ + type *imgSrcPtr; \ + long value; \ + for (j=0; j<pixHeight; j++, imgLine+=pixPitch) \ + { \ + imgPtrS = ((unsigned short*)imgLine) + pixWidth-1; imgSrcPtr = ((type*)imgLine) + pixWidth-1; \ + for (i=0; i<pixWidth; i++, imgPtrS--, imgSrcPtr--) \ + { \ + value = (long)(*imgSrcPtr); if (value > maxValL) value = maxValL; \ + value -= minValL; if (value < 0) value = 0; \ + *imgPtrS = IntToRGBTab15[value]; \ + } \ + } + +// Fill in ascending order here because the destination type may be smaller than the source type +#define TRANSLATE_TO_COLOURSPACE32(type, minval, maxval, scale) \ + type value, *imgSrcPtr; \ + char *imgSrcBase = (char*)imgLine; \ + if (IntToRGBTab24 == NULL) \ + { \ + for (j=0; j<pixHeight; j++, imgLine+=pixPitch, imgSrcBase+=virtualPitch) \ + { \ + imgPtrL = (unsigned long*)imgLine; imgSrcPtr = (type*)imgSrcBase; \ + for (i=0; i<pixWidth; i++, imgPtrL++, imgSrcPtr++) \ + { \ + *imgPtrL = csmap->ValToCS24((double)(*imgSrcPtr) - minVal); \ + } \ + } \ + } \ + else \ + { \ + for (j=0; j<pixHeight; j++, imgLine+=pixPitch, imgSrcBase+=virtualPitch) \ + { \ + imgPtrL = (unsigned long*)imgLine; imgSrcPtr = (type*)imgSrcBase; \ + for (i=0; i<pixWidth; i++, imgPtrL++, imgSrcPtr++) \ + { \ + value = *imgSrcPtr; if (value > maxval) value = maxval; \ + value -= minval; if (value < 0) value = 0; \ + *imgPtrL = IntToRGBTab24[(unsigned long)(value * scale)]; \ + } \ + } \ + } + +void rviewRenderImage::translateBufferToCspace(rviewBaseType bt, double *useMinVal, double *useMaxVal) +{ + double minVal; + unsigned char *imgLine; + unsigned short *IntToRGBTab15, *imgPtrS; + unsigned long *IntToRGBTab24, *imgPtrL; + double maxVal, scalingFactor; + long minValL, maxValL; + int i, j; + + RMDBGONCE(3, RMDebug::module_applications, "rviewRenderImage", "translateBufferToCspace()" ); + + // If bounding boxes are drawn there might be pixels out of range here, so trap those! + minVal = (useMinVal == NULL) ? csmap->getMinVal() : *useMinVal; + maxVal = (useMaxVal == NULL) ? csmap->getMaxVal() : *useMaxVal; + minValL = (long)minVal; maxValL = (long)maxVal; + scalingFactor = csmap->getScalingFactor(); + + imgLine = (unsigned char*)(graphEnv->dest); + IntToRGBTab15 = csmap->getCSTab15(); + IntToRGBTab24 = csmap->getCSTab24(); + + switch (bt) + { + case rbt_char: + { + TRANSLATE_TO_COLOURSPACE15(r_Char); + } + break; + case rbt_uchar: + { + TRANSLATE_TO_COLOURSPACE15(r_Octet); + } + break; + case rbt_short: + { + TRANSLATE_TO_COLOURSPACE15(r_Short); + } + break; + case rbt_ushort: + { + TRANSLATE_TO_COLOURSPACE15(r_UShort); + } + break; + case rbt_long: + { + TRANSLATE_TO_COLOURSPACE32(r_Long, minValL, maxValL, 1); + } + break; + case rbt_ulong: + { + TRANSLATE_TO_COLOURSPACE32(r_ULong, (r_ULong)minValL, (r_ULong)maxValL, 1); + } + break; + case rbt_float: + { + TRANSLATE_TO_COLOURSPACE32(r_Float, minVal, maxVal, scalingFactor); + } + break; + case rbt_double: + { + TRANSLATE_TO_COLOURSPACE32(r_Double, minVal, maxVal, scalingFactor); + } + break; + default: + { + rviewErrorbox::reportError(lman->lookup("errorBaseType"), rviewRenderImage::getFrameName(), "translateBufferToCspace"); + return; + } + } +} + + +int rviewRenderImage::setupEnvBase(int w, int h, r_Ref<r_GMarray> &mdd, colourspaceMapper **csm, r_Minterval *csdom) +{ + int needDepth, newPitch, newPad, newVirtPitch; + + needDepth = 8*baseSize; + if ((rviewImageTypes[baseType] == RVIEW_IMGTYPE_NONE) && (doValToCspace)) needDepth = 32; + if (rviewImageTypes[baseType] == RVIEW_IMGTYPE_HIGH) needDepth = 15; + if (rviewImageTypes[baseType] == RVIEW_IMGTYPE_GREY12) needDepth = 12; + newPad = 64; newPitch = (w * baseSize + 7) & ~7; + newVirtPitch = newPitch; + + if (doValToCspace) + { + // Init if necessary + setCspaceProjMode(doProjRangeCspace); + rviewCheckInitCspace(baseType, csm, mdd, doFullRangeCspace, csdom, w, &newPitch, &needDepth, &newPad, &newVirtPitch, cspar); + if (*csm != NULL) + { + mBar->Enable(MENU_IMAGE_CSPACE_EDIT, TRUE); + if (doProjRangeCspace) + { + setCspaceProjMode(doProjRangeCspace); + (*csm)->updateProjection(csdom); + } + } + deleteViewCspace(); + } + + if ((w != pixWidth) || (h != pixHeight) || (pixPad != newPad) || (pixDepth != needDepth) || (newPitch != pixPitch) || (newVirtPitch != virtualPitch)) + { + pixWidth = w; pixHeight = h; pixDepth = needDepth; + pixPitch = newPitch; pixPad = newPad; virtualPitch = newVirtPitch; + if ((imgData = (char*)malloc(virtualPitch * pixHeight)) == NULL) + { + rviewErrorbox::reportError(lman->lookup("errorMemory"), getFrameName(), "setupEnvironment"); + return -1; + } + } + + graphEnv->lineadd = virtualPitch; + + return 0; +} + + + + +/* + * Volume image renderer members + */ + +const char *rviewVolumeImage::view_VolumeMode = "volumeMode"; +const char *rviewVolumeImage::view_UseBBox = "useBBox"; + +rviewVolumeImage::rviewVolumeImage(mdd_frame *mf, unsigned int flags) : rviewRenderImage(mf, 0, flags) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewVolumeImage", "rviewVolumeImage()" ); + + // make destructor safe + texDesc = NULL; voxDesc = NULL; + + initVoxParams = FALSE; + // Mode defaults + imode = prefs->imgMode; + if ((imode != rim_surf) && (imode != rim_voxel)) imode = rim_surf; + + if (prefs->imgVoxForType != 0) + { + initVoxParams = TRUE; + switch (baseSize) + { + case 1: + setup.pixelThresholdLow = 4.0; setup.pixelThresholdHigh = 1e6; + setup.weightThreshold = 64.0; setup.voxColour = 0xff; + break; + case 2: + setup.pixelThresholdLow = 256.0; setup.pixelThresholdHigh = 65535.0; + setup.weightThreshold = 64.0; setup.voxColour = 0xffff; + break; + case 3: + setup.pixelThresholdLow = 4.0; setup.pixelThresholdHigh = 1e6; + setup.weightThreshold = 64.0; setup.voxColour = 0xffffff; + break; + case 4: + setup.pixelThresholdLow = 16384.0; setup.pixelThresholdHigh = 1e6; + setup.weightThreshold = 64.0; setup.voxColour = 0xffffffff; + break; + case 8: + setup.pixelThresholdLow = 0.0; setup.pixelThresholdHigh = 1e12; + setup.weightThreshold = 64.0; setup.voxColour = 1e12; + default: break; + } + } + + // Align bbox with projection's OK-button + boundingBox = new rviewCheckBox(ctrlPanel); + // Preferences + doBoundingBox = prefs->imgBBox; + boundingBox->SetValue(doBoundingBox); + + texDesc = new tex_desc; + voxDesc = new voxel_desc; + texDesc->floatType = ((baseType == rbt_float) || (baseType == rbt_double)) ? 1 : 0; + +} + + +int rviewVolumeImage::openViewer(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewVolumeImage", "openViewer()" ); + + if (dimMDD != 3) + { + rviewErrorbox::reportError(lman->lookup("errorModeDim"), rviewVolumeImage::getFrameName(), "openViewer"); + objectInitializedOK = FALSE; + return -1; + } + + if (rviewRenderImage::openViewer() == 0) + { + // first add the menus of the parent class + int madd = rviewRenderImage::menuBarInitHook(); + + // then add my own + wxMenu *modes; + char buffer[STRINGSIZE]; + modes = new wxMenu; + modes->Append(MENU_IMAGE_MODE_SURF, "", NULL, TRUE); + modes->Append(MENU_IMAGE_MODE_VOXEL, "", NULL, TRUE); + sprintf(buffer, "&%s", lman->lookup("menImgMode")); + mBar->Append(modes, buffer); + + switch (imode) + { + case rim_surf: lastMode = MENU_IMAGE_MODE_SURF; break; + case rim_voxel: lastMode = MENU_IMAGE_MODE_VOXEL; break; + default: break; + } + modes->Check(lastMode, TRUE); + + openViewerEpilogue(rviewVolumeImage::getFrameType()); + + return 0; + } + return -1; +} + + +rviewVolumeImage::~rviewVolumeImage(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewVolumeImage" ,"~rviewVolumeImage()" ); + + closeViewer(); + delete texDesc; + delete voxDesc; +} + + +const char *rviewVolumeImage::getFrameName(void) const +{ + return "rviewVolumeImage"; +} + +rviewFrameType rviewVolumeImage::getFrameType(void) const +{ + return rviewFrameTypeVolImage; +} + +int rviewVolumeImage::getViewerType(void) const +{ + return RVIEW_RESDISP_IMGVOLM; +} + + +void rviewVolumeImage::label(void) +{ + setDisplayTitle(lman->lookup("titleImageVolume")); + + boundingBox->SetLabel(lman->lookup("textBBox")); + + mBar->SetLabel(MENU_IMAGE_MODE_SURF, lman->lookup("menImgModeSurf")); + mBar->SetLabel(MENU_IMAGE_MODE_VOXEL, lman->lookup("menImgModeVoxel")); + mBar->SetLabelTop(fixedNumberOfMenus + 1, lman->lookup("menImgMode")); + + rviewRenderImage::label(); +} + + +int rviewVolumeImage::process(wxObject &obj, wxEvent &evt) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewVolumeImage", "process()" ); + + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_CHECKBOX_COMMAND) + { + if (&obj == (wxObject*)boundingBox) + { + doBoundingBox = boundingBox->GetValue(); + fillBuffer(); + pcanv->updateDisplay(); + return 1; + } + } + + return rviewRenderImage::process(obj, evt); +} + + +void rviewVolumeImage::OnMenuCommand(int id) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewVolumeImage", "OnMenuCommand()" ); + + rviewImageMode newMode = rim_none; + + switch (id) + { + case MENU_IMAGE_MODE_SURF: newMode = rim_surf; break; + case MENU_IMAGE_MODE_VOXEL: newMode = rim_voxel; break; + default: + rviewRenderImage::OnMenuCommand(id); + break; + } + if (newMode != rim_none) + { + configureMode(); + + // We have to do this in any case or the menus get screwed + mBar->Check(lastMode, FALSE); + mBar->Check(id, TRUE); + lastMode = id; + + if (newMode != imode) + { + imode = newMode; + fillBuffer(); + updatePixmap(imgData, imgData); + } + } +} + + +void rviewVolumeImage::OnSize(int w, int h) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewVolumeImage", "OnSize()" ); + + rviewRenderImage::OnSize(w, h); + + if (boundingBox != NULL) + boundingBox->SetSize(w + 2*display_cnvborder - 3*display_border - 4*display_pbwidth, display_border, image_bbwidth, image_bbheight); +} + + +bool rviewVolumeImage::doUpdate(int flags) +{ + if (((flags & RVIEW_IFLAG_VOXEL) != 0) && (imode == rim_voxel)) return TRUE; + if (((flags & RVIEW_IFLAG_LIGHT) != 0) && setup.useLights) return TRUE; + return FALSE; +} + + +char *rviewVolumeImage::initMode(void) +{ + RMDBGENTER(3, RMDebug::module_applications, "rviewVolumeImage", "initMode()" ); + + // Initialise the projection string. Use a static default for now, later on use the + // last value used. + sprintf(projString, "*:*, *:*, *:*"); + + project->SetValue(projString); + setModeDimension(3); + + if (boundingBox != NULL) + boundingBox->Enable(TRUE); + + texDesc->dimx = interv[0].high() - interv[0].low() + 1; + texDesc->dimy = interv[1].high() - interv[1].low() + 1; + texDesc->dimz = interv[2].high() - interv[2].low() + 1; + texDesc->baseSize = baseSize; + + RMDBGEXIT(3, RMDebug::module_applications, "rviewVolumeImage", "initMode() tx=" << texDesc->dimx << ", ty=" << texDesc->dimy << ", tz=" << texDesc->dimz << ", tbase=" << texDesc->baseSize ); + + return rviewRenderImage::initMode(); +} + + +char *rviewVolumeImage::setupEnvironment(int w, int h) +{ + RMDBGENTER(3, RMDebug::module_applications, "rviewVolumeImage", "setupEnvironment()" ); + + int i, offset; + + if (setupEnvBase(w, h, mddObj, &csmap, csInterv) != 0) + return NULL; + + // These values change for each projection + texDesc->widthx = pt2[0] - pt1[0] + 1; + texDesc->widthy = pt2[1] - pt1[1] + 1; + texDesc->widthz = pt2[2] - pt1[2] + 1; + + r_Ref<r_Marray<r_Char> > tempMdd = (r_Ref<r_Marray<r_Char> >) mddObj; + // Do it like this to avoid having to check all base types + r_Point paux = r_Point(dimMDD); + for (i=0; i<dimMDD; i++) + { + paux[i] = interv[i].low(); + } + offset = ((int)(&((*tempMdd)[pt1]) - &((*tempMdd)[paux]))) * baseSize; + + texDesc->data = (void*)((char*)(tempMdd->get_array()) + offset); + + for (i=0; i<4; i++) + { + geomData[i].x = 0; geomData[i].y = 0; geomData[i].z = 0; + } + geomData[1].x = (real_t)(pt2[0] - pt1[0] + 1); + geomData[2].y = (real_t)(pt2[1] - pt1[1] + 1); + geomData[3].z = (real_t)(pt2[2] - pt1[2] + 1); + + RMDBGMIDDLE(3, RMDebug::module_applications, "rviewVolumeImage", "w=" << pixWidth << ", h=" << pixHeight << ", p=" << pixPitch << ", cl=" << graphEnv->clipl << ", cr=" << graphEnv->clipr << ", cd=" << graphEnv->clipd << ", cu=" << graphEnv->clipu << ", mx=" << graphEnv->midx << ", my=" << graphEnv->midy << ", img=" << graphEnv->dest ); + + RMDBGMIDDLE(3, RMDebug::module_applications, "rviewVolumeImage", "twx=" << texDesc->widthx << ", twy=" << texDesc->widthy << ", twz=" << texDesc->widthz << ", offset=" << (int)((char*)(texDesc->data) - mddObj->get_array()) << ", base size " << texDesc->baseSize << ", dest=" << (void*)imgData ); + + RMDBGEXIT(3, RMDebug::module_applications, "rviewVolumeImage", "setupEnvironment()"); + return imgData; +} + + +void rviewVolumeImage::fillBuffer(void) +{ + int i; + vertex_fp v; + bool cspaceOK; + + RMDBGONCE(3, RMDebug::module_applications, "rviewVolumeImage", "fillBuffer()" ); + + // Plot bounding box or not? + graphEnv->bbox_colour = (doBoundingBox) ? 0xffffff : 0xffffffff; + // Update z-rendering parameters + graphEnv->zpro = setup.zpro; graphEnv->clipz = setup.clipz; + + for (i=1; i<4; i++) + { + // Make sure we don't get zero length vectors + // (only a problem with strongly deformed ``cubes'', like planes) + v.x = cubeScale * (geomData[i].x * rot[0].x + geomData[i].y * rot[0].y + geomData[i].z * rot[0].z); + v.y = cubeScale * (geomData[i].x * rot[1].x + geomData[i].y * rot[1].y + geomData[i].z * rot[1].z); + v.z = cubeScale * (geomData[i].x * rot[2].x + geomData[i].y * rot[2].y + geomData[i].z * rot[2].z); + geomUse[i].x = v.x; + geomUse[i].y = v.y; + geomUse[i].z = v.z; + } + + // Rotate around middle of cube + geomUse[0].x = -(geomUse[1].x + geomUse[2].x + geomUse[3].x) / 2; + geomUse[0].y = -(geomUse[1].y + geomUse[2].y + geomUse[3].y) / 2; + geomUse[0].z = -(geomUse[1].z + geomUse[2].z + geomUse[3].z) / 2 + geomData[3].z / 2 + graphEnv->zpro + zoff; + + /*for (i=0; i<4; i++) + { + cout << i << ": " << geomUse[i].x << ", " << geomUse[i].y << ", " << geomUse[i].z << endl; + }*/ + + fillBufferBackground(doValToCspace, cspaceOK, mddObj, &csmap, csInterv, baseType, doFullRangeCspace); + + if ((texDesc->floatType != 0) && (csmap != NULL)) + { + texDesc->minVal = csmap->getMinVal(); texDesc->maxVal = csmap->getMaxVal(); + } + + if (imode == rim_surf) + { + RenderCubeSurf(geomUse, graphEnv, texDesc); + } + else + { + if (initVoxParams) + { + if (csmap != NULL) + { + setup.pixelThresholdLow = csmap->getMinVal(); setup.pixelThresholdHigh = csmap->getMaxVal(); + setup.weightThreshold = (setup.pixelThresholdHigh - setup.pixelThresholdLow) / 4; + setup.voxColour = csmap->getMaxVal(); + if (setupWindow != NULL) setupWindow->updateSettings(setup); + } + initVoxParams = FALSE; + } + + voxDesc->pixelThresholdLow = setup.pixelThresholdLow; + voxDesc->pixelThresholdHigh = setup.pixelThresholdHigh; + voxDesc->weightThreshold = setup.weightThreshold; + voxDesc->weightQuantisation = setup.weightQuantisation; + voxDesc->useRgbBrightness = setup.useRgbBrightness; + voxDesc->light.ambient = (setup.useLights) ? setup.lightsAmbient : -1.0; + voxDesc->light.gain = setup.lightsGain; + voxDesc->light.cosine = cos((M_PI * setup.lightsAngle) / 180); + voxDesc->light.scintCos = cos((M_PI * setup.lightsScintAngle) / 180); + getLightPos(&(voxDesc->light.lights)); + voxDesc->kernSize = setup.kernelSize; voxDesc->kernType = setup.kernelType; + if (setup.useVoxCol) + { + switch (baseType) + { + case rbt_float: voxColour.f = (float)setup.voxColour; break; + case rbt_double: voxColour.d = (double)setup.voxColour; break; + default: voxColour.l = (unsigned long)setup.voxColour; break; + } + voxDesc->voxColour = (void*)&voxColour; + } + else + { + voxDesc->voxColour = NULL; + } + ::wxBeginBusyCursor(wxHOURGLASS_CURSOR); + RenderCubeVoxel(geomUse, graphEnv, texDesc, voxDesc); + ::wxEndBusyCursor(); + } + + if (doValToCspace && cspaceOK) + { + translateBufferToCspace(baseType); + } +} + + +int rviewVolumeImage::saveView(FILE *fp) +{ + int status = rviewRenderImage::saveView(fp); + + writeViewParam(fp, view_VolumeMode, (long)imode); + writeViewParam(fp, view_UseBBox, (long)doBoundingBox); + + return status; +} + + +int rviewVolumeImage::readView(const char *key, const char *value) +{ + int status = rviewRenderImage::readView(key, value); + + if (status == 0) + { + if (strcmp(key, view_VolumeMode) == 0) + { + imode = (rviewImageMode)atoi(value); + return 1; + } + else if (strcmp(key, view_UseBBox) == 0) + { + doBoundingBox = (bool)atoi(value); + return 1; + } + return 0; + } + return status; +} + + +void rviewVolumeImage::loadViewFinished(void) +{ + rviewRenderImage::loadViewFinished(); + + mBar->Check(MENU_IMAGE_MODE_SURF, (imode == rim_surf)); + mBar->Check(MENU_IMAGE_MODE_VOXEL, (imode == rim_voxel)); + lastMode = imode; + + boundingBox->SetValue(doBoundingBox); + + // if we're in voxel mode, the parameters loaded are OK, don't overwrite them in fillBuffer()! + if (imode == rim_voxel) + initVoxParams = FALSE; +} + + + +/* + * Height field rendered images members + */ +rviewHeightImage::rviewHeightImage(mdd_frame *mf, unsigned int flags) : rviewRenderImage(mf, 0, flags) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewHeightImage", "rviewHeightImage()" ); + + // make destructor safe + mddDesc = NULL; meshDesc = NULL; mddDesc = NULL; + + // create dummy MDD object + r_Minterval dummyInterv(2); + dummyInterv << r_Sinterval((r_Range)0, (r_Range)1) << r_Sinterval((r_Range)0, (r_Range)1); + dummyMDD = (r_Ref<r_GMarray>)(new r_Marray<r_Char>(dummyInterv)); + + meshDesc = new mesh_desc; memset(meshDesc, 0, sizeof(mesh_desc)); + mddDesc = new mdd_desc; + mddDesc->numDims = dimMDD; + mddDesc->dims = new int[dimMDD]; + mddDesc->widths = new int[dimMDD]; + mddDesc->floatType = ((baseType == rbt_float) || (baseType == rbt_double)) ? 1 : 0; +} + + +rviewHeightImage::~rviewHeightImage(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewHeightImage", "~rviewHeightImage()" ); + + closeViewer(); + dummyMDD.destroy(); + if (meshDesc != NULL) + { + RenderHeightFreeMesh(meshDesc); delete meshDesc; + } + if (mddDesc != NULL) + { + delete [] mddDesc->dims; + delete [] mddDesc->widths; + delete mddDesc; + } +} + + +int rviewHeightImage::openViewer(void) +{ + if (dimMDD < 2) + { + rviewErrorbox::reportError(lman->lookup("errorModeDim"), rviewHeightImage::getFrameName(), "openViewer"); + objectInitializedOK = FALSE; + return -1; + } + if (baseType == rbt_rgb) + { + rviewErrorbox::reportError(lman->lookup("errorModeBase"), rviewHeightImage::getFrameName(), "openViewer"); + objectInitializedOK = FALSE; + return -1; + } + + if (rviewRenderImage::openViewer() == 0) + { + openViewerEpilogue(rviewHeightImage::getFrameType()); + return 0; + } + return -1; +} + + +const char *rviewHeightImage::getFrameName(void) const +{ + return "rviewHeightImage"; +} + +rviewFrameType rviewHeightImage::getFrameType(void) const +{ + return rviewFrameTypeHghtImage; +} + +int rviewHeightImage::getViewerType(void) const +{ + return RVIEW_RESDISP_IMGHGHT; +} + + +bool rviewHeightImage::cspaceRangeHook(bool suggest) +{ + return FALSE; +} + + +bool rviewHeightImage::modeNeedsCspace(rviewBaseType bt) const +{ + return FALSE; +} + + +int rviewHeightImage::newProjection(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewHeightImage", "newProjection()" ); + + char *data, *lastData = imgData; + + meshDesc->srcData = NULL; + if ((data = setupGraphEnv()) == NULL) return -1; + fillBuffer(); + + updatePixmap(lastData, data); + + return 0; +} + + +bool rviewHeightImage::moviePossible(void) const +{ + return (dimMDD >= 3); +} + + +char *rviewHeightImage::movieNewFrame(void) +{ + char *data; + + if ((data = setupGraphEnv()) != NULL) + fillBuffer(); + return data; +} + + +bool rviewHeightImage::doUpdate(int flags) +{ + if ((flags & RVIEW_IFLAG_LIGHT) != 0) return TRUE; + if ((flags & RVIEW_IFLAG_HEIGHT) != 0) return TRUE; + return FALSE; +} + + +void rviewHeightImage::redrawSettingsChanged(void) +{ + if (depthForHeightfield() != pixDepth) + { + char *data = NULL, *lastData = imgData; + data = setupGraphEnv(); + fillBuffer(); + updatePixmap(lastData, data); + } + else + rviewRenderImage::redrawSettingsChanged(); +} + + +void rviewHeightImage::label(void) +{ + setDisplayTitle(lman->lookup("titleImageHeight")); + rviewRenderImage::label(); +} + + +int rviewHeightImage::process(wxObject &obj, wxEvent &evt) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewHeightImage", "process()" ); + + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)playStop) + playDirection = 0; + // do not intercept the call (return 1), however, because the renderer + // might also rotate the object, which'll be handled on rviewRenderImage + // level. + } + + return rviewRenderImage::process(obj, evt); +} + + +void rviewHeightImage::prepareToDie(void) +{ + playDirection = 0; + rviewRenderImage::prepareToDie(); +} + + +int rviewHeightImage::requestQuit(int level) +{ + playDirection = 0; + return rviewRenderImage::requestQuit(level); +} + + +char *rviewHeightImage::initMode(void) +{ + int i; + char *b; + + RMDBGONCE(3, RMDebug::module_applications, "rviewHeightImage", "initMode()" ); + + b = projString; + b += sprintf(b, "*:*, *:*"); + for (i=2; i<dimMDD; i++) + { + b += sprintf(b, ", %ld", interv[i].low()); + } + + project->SetValue(projString); + setModeDimension(2); + + for (i=0; i<dimMDD; i++) + { + mddDesc->dims[i] = interv[i].high() - interv[i].low() + 1; + mddDesc->widths[i] = mddDesc->dims[i]; + } + mddDesc->baseSize = baseSize; + + return rviewRenderImage::initMode(); +} + + +int rviewHeightImage::depthForHeightfield(void) const +{ + return (setup.voxColour >= 256) ? 24 : 8; +} + + +char *rviewHeightImage::setupEnvironment(int w, int h) +{ + int i, needDepth, newPitch, newPad, offset, newVirtPitch; + + RMDBGONCE(3, RMDebug::module_applications, "rviewHeightImage", "setupEnvironment()" ); + + needDepth = depthForHeightfield(); + newPad = 32; newPitch = (w * (needDepth >> 3) + 3) & ~3; + newVirtPitch = newPitch; + + if ((doValToCspace) && (needDepth == 8)) + { + setCspaceProjMode(FALSE); + rviewCheckInitCspace(rbt_char, &csmap, dummyMDD, TRUE, csInterv, w, &newPitch, &needDepth, &newPad, &newVirtPitch, cspar); + if (csmap != NULL) + { + mBar->Enable(MENU_IMAGE_CSPACE_EDIT, TRUE); + } + deleteViewCspace(); + } + + if ((w != pixWidth) || (h != pixHeight) || (pixPad != newPad) || (pixDepth != needDepth) || (newPitch != pixPitch) || (newVirtPitch != virtualPitch)) + { + pixWidth = w; pixHeight = h; pixDepth = needDepth; + pixPitch = newPitch; pixPad = newPad; virtualPitch = newVirtPitch; + if ((imgData = (char*)malloc(virtualPitch * pixHeight)) == NULL) + { + rviewErrorbox::reportError(lman->lookup("errorMemory"), rviewHeightImage::getFrameName(), "setupEnvironment"); + return NULL; + } + } + graphEnv->lineadd = virtualPitch; + + for (i=0; i<dimMDD; i++) + { + mddDesc->widths[i] = pt2[i] - pt1[i] + 1; + } + + r_Ref<r_Marray<r_Char> > tempMdd = (r_Ref<r_Marray<r_Char> >) mddObj; + r_Point paux(dimMDD); + for (i=0; i<dimMDD; i++) + { + paux[i] = interv[i].low(); + } + + offset = ((int)(&((*tempMdd)[pt1]) - &((*tempMdd)[paux]))) * baseSize; + + mddDesc->data = (void*)((char*)(tempMdd->get_array()) + offset); + + return imgData; +} + + +void rviewHeightImage::fillBackgroundCore(rviewBaseType bt, double minVal) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewHeightImage", "fillBackgroundCore()" ); + + memset(graphEnv->dest, 0, pixPitch * pixHeight); +} + + +void rviewHeightImage::fillBuffer(void) +{ + real_t gridScale, scaleHeight; + light_desc light; + bool useCspace, cspaceOK; + + RMDBGONCE(3, RMDebug::module_applications, "rviewHeightImage", "fillBuffer()" ); + + gridScale = cubeScale * setup.gridSize; scaleHeight = cubeScale * setup.scaleHeight; + graphEnv->zpro = setup.zpro; graphEnv->clipz = setup.clipz; + memcpy(geomUse+1, rot, 3*sizeof(vertex_fp)); + + // Calculate the coordinates of the center point of the base diagonal for + // centering the rotation + int dimx, dimz; + + if (RenderHeightGetDomain(mddDesc, &dimx, &dimz, NULL, NULL) != 0) return; + + geomUse[0].x = -0.5 * gridScale * (dimx * rot[0].x + dimz * rot[0].z); + geomUse[0].y = 0.0; + geomUse[0].z = -0.5 * gridScale * (dimx * rot[2].x + dimz * rot[2].z) + graphEnv->zpro + zoff; + + useCspace = doValToCspace; + + light.ambient = (setup.useLights) ? setup.lightsAmbient : -1.0; + light.gain = setup.lightsGain; + light.cosine = cos((M_PI * setup.lightsAngle) / 180); + light.scintCos = cos((M_PI * setup.lightsScintAngle) / 180); + getLightPos(&(light.lights)); + meshDesc->scaleGrid = gridScale; meshDesc->scaleHeight = scaleHeight; + meshDesc->colour = (unsigned int)(setup.voxColour); + if (meshDesc->colour >= 256) + { + meshDesc->colour |= 0xff000000; // mark as RGB + useCspace = FALSE; + } + + // Colourspace mapping in the height renderer always means a source type of + // 8bpp. Besides there's no correlation between the rendered colours and the + // MDD values, therefore the colourspace is always set to [0,255] here. + double useMinVal = 0; + fillBufferBackground(useCspace, cspaceOK, dummyMDD, &csmap, csInterv, rbt_char, true, &useMinVal); + + ::wxBeginBusyCursor(wxHOURGLASS_CURSOR); + + RenderHeightField(meshDesc, geomUse, graphEnv, mddDesc, &light); + + if (useCspace && cspaceOK) + { + double useMaxVal=255; + translateBufferToCspace(rbt_char, &useMinVal, &useMaxVal); + } + + ::wxEndBusyCursor(); +} + + + + +/* + * Prescaled image class + */ + +const double rviewScaledImage::scaleStep = 2.0; + +const char *rviewScaledImage::view_CurrentBox = "currentBox"; +const char *rviewScaledImage::view_BoxScale = "boxScale"; + +rviewScaledImage::rviewScaledImage(collection_desc *cd, r_Fast_Base_Scale *scaler, unsigned int flags) : rviewFlatBaseImage(cd->mddObjs, 0, flags) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "rviewScaledImage()" ); + + int i; + + scaleObject = scaler; + fullDomain = scaleObject->get_full_domain(); + + initialScale = scaler->get_last_scale(); + thisView.scale = initialScale; + thisView.dim1 = 0; thisView.dim2 = 1; + thisView.low = r_Point(dimMDD); + thisView.high = r_Point(dimMDD); + for (i=0; i<dimMDD; i++) + { + thisView.low[i] = fullDomain[i].low(); + thisView.high[i] = fullDomain[i].high(); + } + + dontLoad = TRUE; + loadedView = NULL; + + collDesc = cd; +} + + +rviewScaledImage::~rviewScaledImage(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "~rviewScaledImage()" ); + closeViewer(); + rviewDeleteCollection(collDesc); + // I own the scaler object! + delete scaleObject; + if (loadedView != NULL) + delete loadedView; +} + + +int rviewScaledImage::openViewer(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "openViewer()" ); + + if (rviewFlatBaseImage::openViewer() == 0) + { + zoomInBut = new rviewButton(ctrlPanel); + zoomOutBut = new rviewButton(ctrlPanel); + lastZoomBut = new rviewButton(ctrlPanel); + zoomBoxBut = new rviewButton(ctrlPanel); + scaleString = new rviewText(ctrlPanel, thisView.scale); + + lastZoomBut->Enable((viewHistory.getNumber() != 0)); + boxState = pcanv->HasDragBox(); + zoomBoxBut->Enable(boxState); + + mBar->Enable(MENU_DISP_DATA_INSERT, FALSE); + mBar->Enable(MENU_DISP_DATA_INSERTPRO, FALSE); + + openViewerEpilogue(rviewScaledImage::getFrameType()); + + dontLoad = FALSE; + + return 0; + } + return -1; +} + + +void rviewScaledImage::label(void) +{ + if (initPhaseFinished) + { + setDisplayTitle(lman->lookup("titleImageScaled")); + scaleString->SetLabel(lman->lookup("textScaleFactor")); + zoomBoxBut->SetLabel(lman->lookup("textZoomBox")); + lastZoomBut->SetLabel(lman->lookup("textLastZoom")); + zoomInBut->SetLabel(lman->lookup("textZoomIn")); + zoomOutBut->SetLabel(lman->lookup("textZoomOut")); + } + rviewFlatBaseImage::label(); +} + + +void rviewScaledImage::OnSize(int w, int h) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "OnSize()"); + + if (initPhaseFinished) + { + int x, y, step, posx, posy; + + GetClientSize(&x, &y); + + if(x < 6*image_bwidth) + { + x= 6*image_bwidth; + frameWidth=x; + frameHeight=y; + SetClientSize(x, y); + return; + } + + x -= 2*display_border; + scaleString->SetSize(display_border, image_totaly - image_theight, image_twidth, image_theight); + step = (x - image_twidth - 4*image_bwidth) / 4; + posx = image_twidth + display_border + step/2; + posy = image_totaly - image_bheight; + zoomBoxBut->SetSize(posx, posy, image_bwidth, image_bheight); + posx += step + image_bwidth; + lastZoomBut->SetSize(posx, posy, image_bwidth, image_bheight); + posx += step + image_bwidth; + zoomInBut->SetSize(posx, posy, image_bwidth, image_bheight); + posx += step + image_bwidth; + zoomOutBut->SetSize(posx, posy, image_bwidth, image_bheight); + } + + rviewFlatBaseImage::OnSize(w, h); + + if (initPhaseFinished) + { + int w, h, vw, vh; + + pcanv->GetClientSize(&w, &h); + pcanv->SetAspectRatio(((double)h) / w); + w = (int)(w / thisView.scale); h = (int)(h / thisView.scale); + vw = (int)(thisView.high[thisView.dim1] - thisView.low[thisView.dim1]); + vh = (int)(thisView.high[thisView.dim2] - thisView.low[thisView.dim2]); + if ((vw < w) || (vh < h)) + { + if (vw < w) + thisView.high[thisView.dim1] = (r_Range)(thisView.low[thisView.dim1] + w); + if (vh < h) + thisView.high[thisView.dim2] = (r_Range)(thisView.low[thisView.dim2] + h); + + newView(); + } + } +} + + +double rviewScaledImage::getLastScale(void) const +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "getLastScale()"); + view_desc_t lastView; + + if (viewHistory.peek(lastView) == 0) + return lastView.scale; + else + return initialScale; +} + + +void rviewScaledImage::scaleViewBy(double scale) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "scaleViewBy()"); + + thisView.high[thisView.dim1] = thisView.low[thisView.dim1] + + (r_Range)((thisView.high[thisView.dim1] - thisView.low[thisView.dim1]) / scale); + thisView.high[thisView.dim2] = thisView.low[thisView.dim2] + + (r_Range)((thisView.high[thisView.dim2] - thisView.low[thisView.dim2]) / scale); + thisView.scale *= scale; + + // If we magnify, check whether we can increase the view box + if (scale > 1.0) + { + int w, h; + + pcanv->GetClientSize(&w, &h); + w = (int)(w / thisView.scale); h = (int)(h / thisView.scale); + // extend the viewbox to the maximum size of the canvas + if (thisView.high[thisView.dim1] - thisView.low[thisView.dim1] < w) + thisView.high[thisView.dim1] = (r_Range)(thisView.low[thisView.dim1] + w); + if (thisView.high[thisView.dim2] - thisView.low[thisView.dim2] < h) + thisView.high[thisView.dim2] = (r_Range)(thisView.low[thisView.dim2] + h); + // no clipping necessary here, will be done in newView() + } +} + + +int rviewScaledImage::process(wxObject &obj, wxEvent &evt) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "process()"); + + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)lastZoomBut) + { + if (viewHistory.pop(thisView) == 0) + { + newView(); + } + if (viewHistory.getNumber() == 0) + lastZoomBut->Enable(FALSE); + + return 1; + } + else if (&obj == (wxObject*)zoomInBut) + { + viewHistory.push(thisView); + lastZoomBut->Enable(TRUE); + scaleViewBy(scaleStep); + newView(); + return 1; + } + else if (&obj == (wxObject*)zoomOutBut) + { + viewHistory.push(thisView); + lastZoomBut->Enable(TRUE); + scaleViewBy(1/scaleStep); + newView(); + return 1; + } + else if (&obj == (wxObject*)zoomBoxBut) + { + if (pcanv->HasDragBox()) + { + int x0, y0, x1, y1; + double relScale; + + viewHistory.push(thisView); + lastZoomBut->Enable(TRUE); + pcanv->GetDragBox(x0, y0, x1, y1); + relScale = (thisView.high[thisView.dim1] - thisView.low[thisView.dim1]) / ((double)(pixmap->getWidth())); + thisView.high[thisView.dim1] = thisView.low[thisView.dim1] + (r_Range)(x1*relScale); + thisView.low[thisView.dim1] += (r_Range)(x0 * relScale); + thisView.high[thisView.dim2] = thisView.low[thisView.dim2] + (r_Range)(y1*relScale); + thisView.low[thisView.dim2] += (r_Range)(y0 * relScale); + thisView.scale *= ((double)(pixmap->getWidth())) / (x1 - x0); + newView(); + } + return 1; + } + } + else if (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND) + { + if (&obj == (wxObject*)scaleString) + { + double newScale = atof(scaleString->GetValue()); + if (newScale != thisView.scale) + { + viewHistory.push(thisView); + lastZoomBut->Enable(TRUE); + scaleViewBy(newScale / thisView.scale); + newView(); + } + return 1; + } + } + + return rviewFlatBaseImage::process(obj, evt); +} + + +// Intercept the mouse event. +void rviewScaledImage::processMouseEvent(wxMouseEvent &mevt) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "processMouseEvent()"); + + int newbut = 0; + bool newBoxState; + + if (mevt.leftDown) newbut |= MOUSE_LEFT; + if (mevt.middleDown) newbut |= MOUSE_MIDDLE; + if (mevt.rightDown) newbut |= MOUSE_RIGHT; + + // Drag a box + if ((newbut & MOUSE_LEFT) != 0) + { + if ((mousebut & MOUSE_LEFT) == 0) + { + pcanv->SetDragBox(mevt.x, mevt.y, mevt.x, mevt.y); + } + else + { + pcanv->UpdateDragBox(mevt.x, mevt.y); + } + } + else if ((newbut & MOUSE_RIGHT) != 0) + { + if ((mousebut & MOUSE_RIGHT) == 0) + { + pcanv->AdjustDragBox(mevt.x, mevt.y); + } + else + { + pcanv->UpdateDragBox(mevt.x, mevt.y); + } + } + mousebut = newbut; + mousex = mevt.x; mousey = mevt.y; + + newBoxState = pcanv->HasDragBox(); + if (newBoxState != boxState) + { + zoomBoxBut->Enable(newBoxState); + boxState = newBoxState; + } +} + + +const char *rviewScaledImage::getFrameName(void) const +{ + return "rviewPrescaledFrame"; +} + + +rviewFrameType rviewScaledImage::getFrameType(void) const +{ + return rviewFrameTypeScaledImage; +} + + +int rviewScaledImage::getViewerType(void) const +{ + return RVIEW_RESDISP_IMGSCLD; +} + + +bool rviewScaledImage::showScaleSlider(void) const +{ + return FALSE; +} + + +void rviewScaledImage::projectionStringForView(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "projectionStringView()"); + + int i; + char *b = projString; + + for (i=0; i<thisView.dim1; i++) + { + b += sprintf(b, "%ld, ", thisView.low[i]); + } + b += sprintf(b, "%ld:%ld, ", thisView.low[i], thisView.high[i]); + i++; + for (; i<thisView.dim2; i++) + { + b += sprintf(b, "%ld, ", thisView.low[i]); + } + b += sprintf(b, "%ld:%ld, ", thisView.low[i], thisView.high[i]); + i++; + for (; i<dimMDD; i++) + { + b += sprintf(b, "%ld, ", thisView.low[i]); + } + if (b != projString) + b[-2] = '\0'; +} + + +char *rviewScaledImage::initMode(void) +{ + return rviewFlatBaseImage::initMode(); +} + + +const r_Minterval &rviewScaledImage::getVirtualDomain(void) const +{ + + return fullDomain; +} + + +void rviewScaledImage::projectObjectHook(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "projectObjectHook()"); + int i, j; + + for (i=0; i<(int)dimMDD; i++) + { + thisView.low[i] = pt1[i]; + thisView.high[i] = pt2[i]; + } + for (i=0, j=0; i<(int)dimMDD; i++) + { + if ((freeDims & (1<<i)) != 0) + { + if (j == 0) + { + thisView.dim1 = i; j++; + } + else + thisView.dim2 = i; + } + } + + r_Minterval projFull(dimMDD); + r_Minterval projScaled; + + for (i=0; i<dimMDD; i++) + { + // shift point into full domain's origin... + projFull << r_Sinterval(fullDomain[i].low(), fullDomain[i].low() + (pt2[i] - pt1[i])); + } + // now adapt the values to the actual MDD object + if (scaleObject->get_scaled_domain(projFull, projScaled, thisView.scale) != 0) + { + //cout << projFull << ", " << projScaled << ", " << interv << endl; + // for now just make sure we're not outside the range... + for (i=0; i<dimMDD; i++) + { + r_Range offset = pt1[i] - fullDomain[i].low(); + pt1[i] = projScaled[i].low() + offset; + if (pt1[i] < interv[i].low()) pt1[i] = interv[i].low(); + if (pt1[i] > interv[i].high()) pt1[i] = interv[i].high(); + pt2[i] = projScaled[i].high() + offset; + if (pt2[i] < interv[i].low()) pt2[i] = interv[i].low(); + if (pt2[i] > interv[i].high()) pt2[i] = interv[i].high(); + } + //cout << "Translated " << pt1 << ", " << pt2 << endl; + } + else + { + rviewErrorbox::reportError(lman->lookup("errorScaledObjSize")); + } +} + + +char *rviewScaledImage::projectImage(void) +{ + return rviewFlatBaseImage::projectImage(); +} + + +bool rviewScaledImage::compareViews(const view_desc_t &v1, const view_desc_t &v2) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "compareViews()"); + if ((v1.scale == v2.scale) && (v1.dim1 == v2.dim1) && (v1.dim2 == v2.dim2) + && (v1.low == v2.low) && (v1.high == v2.high)) + return TRUE; + + return FALSE; +} + + +int rviewScaledImage::newProjection(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "newProjection()"); + int status; + view_desc_t lastView = thisView; + + if ((status = rviewFlatBaseImage::newProjection()) == 0) + { + if (!compareViews(thisView, lastView)) + { + viewHistory.push(lastView); + lastZoomBut->Enable(TRUE); + } + } + return status; +} + + +void rviewScaledImage::newView(bool loadImage) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewScaledImage", "newView()"); + int i; + + // Remove drag boxes + pcanv->SetDragBox(-1, -1, -1, -1); + boxState = FALSE; + zoomBoxBut->Enable(boxState); + + // Clip rectangle to actual domain. Ratio already ensured by canvas + for (i=0; i<(int)dimMDD; i++) + { + if (thisView.low[i] < fullDomain[i].low()) + thisView.low[i] = fullDomain[i].low(); + if (thisView.high[i] > fullDomain[i].high()) + thisView.high[i] = fullDomain[i].high(); + } + + if (!dontLoad) // or loadImage? + { + r_Minterval orgIv(dimMDD); + r_Minterval scaledDom; + int i; + + for (i=0; i<(int)dimMDD; i++) + { + orgIv << r_Sinterval(thisView.low[i], thisView.high[i]); + } + + collDesc->mddObjs[0].mdd.destroy(); + + ::wxBeginBusyCursor(wxHOURGLASS_CURSOR); + + collDesc->mddObjs[0].mdd = rviewDatabase::getScaledObject(scaleObject, orgIv, thisView.scale); + + ::wxEndBusyCursor(); + + if (collDesc->mddObjs[0].mdd.is_null()) + { + Close(TRUE); return; + } + mddObj = collDesc->mddObjs[0].mdd; + interv = mddObj->spatial_domain(); + } + + projectionStringForView(); + project->SetValue(projString); + scaleString->SetValue(thisView.scale); + scaleValue = 100; + + rviewFlatBaseImage::newProjection(); +} + + +int rviewScaledImage::saveView(FILE *fp) +{ + int status = rviewFlatBaseImage::saveView(fp); + + long *boxdesc = new long[2*dimMDD + 2]; + + boxdesc[0] = (long)(thisView.dim1); + boxdesc[1] = (long)(thisView.dim2); + for (unsigned int i=0; i<dimMDD; i++) + { + boxdesc[2+i] = (long)(thisView.low[i]); + boxdesc[2+i+dimMDD] = (long)(thisView.high[i]); + } + writeViewParam(fp, view_CurrentBox, 2*dimMDD + 2, boxdesc); + writeViewParam(fp, view_BoxScale, thisView.scale); + + delete [] boxdesc; + + return status; +} + + +int rviewScaledImage::readView(const char *key, const char *value) +{ + int status = rviewFlatBaseImage::readView(key, value); + + if (status == 0) + { + if (strcmp(key, view_CurrentBox) == 0) + { + long *boxdesc = new long[2*dimMDD + 2]; + + if (readVector(value, 2*dimMDD + 2, boxdesc) == 0) + { + ensureLoadedView(); + + loadedView->dim1 = (int)(boxdesc[0]); + loadedView->dim2 = (int)(boxdesc[1]); + // why do I always forget these !*&@^$()&^ dim constructors...??? + loadedView->low = r_Point(dimMDD); + loadedView->high = r_Point(dimMDD); + for (unsigned int i=0; i<dimMDD; i++) + { + loadedView->low[i] = (r_Range)(boxdesc[2+i]); + loadedView->high[i] = (r_Range)(boxdesc[2+i+dimMDD]); + } + } + delete [] boxdesc; + return 1; + } + else if (strcmp(key, view_BoxScale) == 0) + { + ensureLoadedView(); + loadedView->scale = atof(value); + return 1; + } + return 0; + } + return status; +} + + +void rviewScaledImage::loadViewFinished(void) +{ + rviewFlatBaseImage::loadViewFinished(); + + viewHistory.push(thisView); + lastZoomBut->Enable(TRUE); + + thisView.scale = loadedView->scale; + thisView.low = loadedView->low; + thisView.high = loadedView->high; + thisView.dim1 = loadedView->dim1; + thisView.dim2 = loadedView->dim2; + + delete loadedView; + loadedView = NULL; + + newView(TRUE); +} + + +void rviewScaledImage::ensureLoadedView(void) +{ + if (loadedView == NULL) + { + loadedView = new view_desc_t; + memset(loadedView, 0, sizeof(view_desc_t)); + } +} + + + + + + + +/* + * Functions performing the actual translation of MDD into a scaled + * image. Code shared between rviewFlatImage and rviewThumb. + */ + +// Number of fractional fixpoint bits +#define IMAGE_FIXPREC 16 + +// A macro for recurring code in projectImage +#define PROJECT_HEADER(type) \ + if (penv.pt1[penv.dim1] == penv.pt2[penv.dim1]) stepx=0; \ + else \ + { \ + prun[penv.dim1]=penv.pt1[penv.dim1]+1; \ + prun[penv.dim2]=penv.pt1[penv.dim2]; \ + stepx = &((*mddPtr)[prun]) - &((*mddPtr)[penv.pt1]); \ + } \ + if (penv.pt1[penv.dim2] == penv.pt2[penv.dim2]) stepy=0; \ + else \ + { \ + prun[penv.dim1]=penv.pt1[penv.dim1]; \ + prun[penv.dim2]=penv.pt1[penv.dim2]+1; \ + stepy = &((*mddPtr)[prun]) - &((*mddPtr)[penv.pt1]); \ + } \ + scalestepx = stepx * (int)(1 / penv.scale); \ + scalestepy = stepy * (int)(1 / penv.scale); \ + fracstepx = ((int)((1<<IMAGE_FIXPREC) / penv.scale)) & ((1<<IMAGE_FIXPREC)-1); \ + fracstepy = ((int)((1<<IMAGE_FIXPREC) / penv.scale)) & ((1<<IMAGE_FIXPREC)-1); \ + imgLine = (type*)(&((*mddPtr)[penv.pt1])); + +#define PROJECT_IMAGE_INCREMENTX \ + imgPtr += scalestepx; fracx += fracstepx; \ + if (fracx >= (1<<IMAGE_FIXPREC)) \ + { \ + imgPtr += stepx; fracx &= ((1<<IMAGE_FIXPREC)-1); \ + } + +#define PROJECT_IMAGE_INCREMENTY \ + imgLine += scalestepy; fracy += fracstepy; \ + if (fracy >= (1<<IMAGE_FIXPREC)) \ + { \ + imgLine += stepy; fracy &= ((1<<IMAGE_FIXPREC)-1); \ + } + +// A macro for colourspace translators +// Do not terminate this macro with a semicolon when using it! +#define PROJECT_IMAGE_START(type) \ + r_Marray<type> *mddPtr = (r_Marray<type>*) (penv.mddPtr); \ + type *imgLine, *imgPtr; \ + PROJECT_HEADER(type); \ + for (h=0; h<penv.height; h++, destLine.c+=penv.pitch) \ + { \ + for (w=0, imgPtr=imgLine, destPtr.c=destLine.c, fracx=0; w<penv.width; w++) + +#define PROJECT_IMAGE_BASE(type) \ + PROJECT_IMAGE_START(type) \ + { \ + PROJECT_IMAGE_TRANSFER_PIXEL(type); \ + PROJECT_IMAGE_INCREMENTX; \ + } \ + PROJECT_IMAGE_INCREMENTY; \ + } + +#define PROJECT_COLOURSPACE32(type, minval, maxval, scale) \ + if ((IntToRGBTab24 = penv.csmap->getCSTab24()) == NULL) \ + { \ + PROJECT_IMAGE_START(type) \ + { \ + *destPtr.l++ = penv.csmap->ValToCS24((double)(*imgPtr) - minVal); \ + PROJECT_IMAGE_INCREMENTX; \ + } \ + PROJECT_IMAGE_INCREMENTY; \ + } \ + } \ + else \ + { \ + PROJECT_IMAGE_START(type) \ + { \ + type value; \ + value = *imgPtr; \ + if (value > maxval) value = maxval; value -= minval; if (value < 0) value = 0;\ + *destPtr.l++ = IntToRGBTab24[(unsigned long)(value * scale)]; \ + PROJECT_IMAGE_INCREMENTX; \ + } \ + PROJECT_IMAGE_INCREMENTY; \ + } \ + } + + +int rviewPrepareFlatProjection(rviewFlatProjEnv &penv) +{ + int baseSize = penv.mddPtr->get_type_length(); + + penv.width = (int)((penv.pt2[penv.dim1] - penv.pt1[penv.dim1] + 1) * penv.scale); + penv.height = (int)((penv.pt2[penv.dim2] - penv.pt1[penv.dim2] + 1) * penv.scale); + + switch (rviewImageTypes[penv.bt]) + { + case RVIEW_IMGTYPE_NONE: + { + if (penv.doCspace) + { + penv.pitch = penv.width * 4; penv.pad = 32; penv.depth = 32; + } + else + { + rviewErrorbox::reportError(lman->lookup("errorUnknownBase"), "rviewPrepareFlatProjection"); + return -1; + } + } + break; + case RVIEW_IMGTYPE_MONO: + penv.pitch = (penv.width + 7) >> 3; penv.pad = 8; penv.depth = 1; + break; + case RVIEW_IMGTYPE_HIGH: + penv.pitch = (penv.width * 2 + 3) & ~3; penv.pad = 32; penv.depth = 15; + break; + case RVIEW_IMGTYPE_GREY12: + // unsigned short = 12bpp grey, gets transformed to 8bpp grey + penv.pitch = (penv.width + 3) & ~3; penv.pad = 32; penv.depth = 8; + break; + default: + penv.pitch = (penv.width * baseSize + 3) & ~3; + penv.pad = 32; penv.depth = 8*baseSize; + break; + } + +#ifdef wx_msw + if (penv.bt == rbt_rgb) + { + penv.pitch = (penv.width * baseSize); penv.pad = 8; + } +#endif + + return 0; +} + + +int rviewPerformFlatProjection(rviewFlatProjEnv &penv, char *data) +{ + int stepx, stepy, scalestepx, scalestepy; + int fracstepx, fracstepy, fracx, fracy; + union {char *c; short *s; RGBPixel *r; long *l;} destPtr, destLine; + r_Point prun; + int w, h; + + destLine.c = data; + prun = penv.pt1; + fracy = 0; + + // Do a colourspace mapping? + if (penv.cspaceState != 0) + { + double minVal, maxVal; + long minValL, maxValL; + double scalingFactor; + unsigned short *IntToRGBTab15; + unsigned long *IntToRGBTab24; + + minVal = penv.csmap->getMinVal(); minValL = (long)minVal; + maxVal = penv.csmap->getMaxVal(); maxValL = (long)maxVal; + scalingFactor = penv.csmap->getScalingFactor(); + + IntToRGBTab15 = penv.csmap->getCSTab15(); + + switch (penv.bt) + { +#define PROJECT_IMAGE_TRANSFER_PIXEL(type) long value = (long)(*imgPtr); if (value > maxValL) value = maxValL; value -= minValL; if (value < 0) value = 0; *destPtr.s++ = IntToRGBTab15[value] + case rbt_char: + { + PROJECT_IMAGE_BASE(r_Char); + } + break; + case rbt_uchar: + { + PROJECT_IMAGE_BASE(r_Octet); + } + break; + case rbt_short: + { + PROJECT_IMAGE_BASE(r_Short); + } + break; + case rbt_ushort: + { + PROJECT_IMAGE_BASE(r_UShort); + } + break; + case rbt_long: + { + PROJECT_COLOURSPACE32(r_Long, minValL, maxValL, 1); + } + break; + case rbt_ulong: + { + PROJECT_COLOURSPACE32(r_ULong, (r_ULong)minValL, (r_ULong)maxValL, 1); + } + break; + case rbt_float: + { + PROJECT_COLOURSPACE32(r_Float, minVal, maxVal, scalingFactor); + } + break; + case rbt_double: + { + PROJECT_COLOURSPACE32(r_Double, minVal, maxVal, scalingFactor); + } + break; + default: + { + rviewErrorbox::reportError(lman->lookup("errorBaseType"), "rviewPerformFlatProjectionCmap"); + return -1; + } + break; + } + } + // No, use actual source data with no transformations. + else + { + switch (rviewImageTypes[penv.bt]) + { + case RVIEW_IMGTYPE_MONO: + { + r_Marray<r_Boolean> *mddPtr = (r_Marray<r_Boolean> *)(penv.mddPtr); + r_Boolean *imgLine, *imgPtr; + char val; + int mask; + + PROJECT_HEADER(r_Boolean); + for (h=0; h<penv.height; h++, destLine.c+=penv.pitch) + { +#if (WX_PIXMAP_SRC_BITORDER == 0) + mask = 1; +#else + mask = 0x80; +#endif + val = 0; + for (w=0, imgPtr=imgLine, destPtr.c=destLine.c, fracx=0; w<penv.width; w++) + { + if (*imgPtr) val |= mask; +#if (WX_PIXMAP_SRC_BITORDER == 0) + mask <<= 1; if (mask == 0x100) {*destPtr.c++ = val; val = 0; mask = 1;} +#else + mask >>= 1; if (mask == 0) {*destPtr.c++ = val; val = 0; mask = 0x80;} +#endif + PROJECT_IMAGE_INCREMENTX; + } +#if (WX_PIXMAP_SRC_BITORDER == 0) + if (mask != 1) +#else + if (mask != 0x80) +#endif + *destPtr.c++ = val; + PROJECT_IMAGE_INCREMENTY; + } + } + break; + +#undef PROJECT_IMAGE_TRANSFER_PIXEL +#define PROJECT_IMAGE_TRANSFER_PIXEL(type) *destPtr.c++ = *imgPtr + case RVIEW_IMGTYPE_GREY: + { + PROJECT_IMAGE_BASE(r_Char) + } + break; + +#undef PROJECT_IMAGE_TRANSFER_PIXEL +#define PROJECT_IMAGE_TRANSFER_PIXEL(type) *destPtr.s++ = *imgPtr + case RVIEW_IMGTYPE_HIGH: + { + PROJECT_IMAGE_BASE(r_Short) + } + break; + +#undef PROJECT_IMAGE_TRANSFER_PIXEL +#define PROJECT_IMAGE_TRANSFER_PIXEL(type) *destPtr.c++ = (char)((*imgPtr) >> 4) + case RVIEW_IMGTYPE_GREY12: + { + PROJECT_IMAGE_BASE(r_UShort) + } + break; + +#undef PROJECT_IMAGE_TRANSFER_PIXEL +#define PROJECT_IMAGE_TRANSFER_PIXEL(type) *destPtr.l++ = *imgPtr + case RVIEW_IMGTYPE_TRUE32: + { + PROJECT_IMAGE_BASE(r_Long) + } + break; + +#undef PROJECT_IMAGE_TRANSFER_PIXEL +#define PROJECT_IMAGE_TRANSFER_PIXEL(type) *destPtr.r++ = *imgPtr + case RVIEW_IMGTYPE_TRUE24: + { + PROJECT_IMAGE_BASE(RGBPixel) + } + break; + + default: + { + rviewErrorbox::reportError(lman->lookup("errorBaseType"), "rviewPerformFlatProjection"); + return -1; + } + break; + } + } + + return 0; +} diff --git a/applications/rview/rviewMDD.cpp b/applications/rview/rviewMDD.cpp new file mode 100644 index 0000000..4f175fc --- /dev/null +++ b/applications/rview/rviewMDD.cpp @@ -0,0 +1,1006 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * The rviewMDD module encapsulates various generic operations on + * MDD objects; it relies heavily on templates for this. All functions + * provided here are a) completely independent from the rest of rView + * and b) work on any kind of MDD, i.e. no restrictions whatsoever on + * dimensionality or base type. Most functions utilize + * mdd_objectFunctionType() for this which iterates over the MDD object + * basetype first and calls functions provided in the mdd_function_pointers + * structure which perform the required operation on each of the primitive + * basetypes. + * + * Functions provided are: + * + * - mdd_objectRange(): return the minimum and maximum cell values. + * - mdd_createSubcube(): creates a new MDD object by copying a subcube + * (or all of) a source MDD object; optionally persistent. + * - mdd_objectScaleInter(): creates new object by resampling (part of) + * the source object using n-linear interpolation. Use for upsampling. + * - mdd_objectScaleAverage(): creates new object by resampling (part of) + * the source object using averaging. Use for downsampling. + * - mdd_objectScaleSimple(): creates new object by scaling (part of) + * the source object using simple nearest neighbour. Fast but blocky. + * - mdd_objectChangeEndianness(): creates a new object where the + * endianness is inverted compared to the source object, or changes + * the object itself. + * + * COMMENTS: + * No comments + */ + + + +// For early templates +#ifndef _RVIEW_MDD_CPP_ +#define _RVIEW_MDD_CPP_ + + + +#ifdef __GNUG__ +#pragma implementation +#endif + + + +#include <limits.h> +#include <float.h> +#include <string.h> + + + +// Was this file included from rviewMDD.hh? +#ifdef EARLY_TEMPLATE +#ifndef __EXECUTABLE__ +#define __EXECUTABLE__ +#define _RVIEW_MDD_EXECUTABLE_ +#endif +#endif + +#include "rasodmg/transaction.hh" +#include "raslib/primitivetype.hh" +#include "raslib/structuretype.hh" +#include "raslib/endian.hh" +#include "rasodmg/alignedtiling.hh" +#include "rasodmg/storagelayout.hh" +#include "rviewMDD.hh" +#include "rviewTypes.hh" + +#ifdef _RVIEW_MDD_EXECUTABLE_ +#undef __EXECUTABLE__ +#endif + + + + +#define MDD_FIXPREC 16 + + + + + +// auxData interpreted as double[2] containing min, max, initialised externally +template<class T> +int objectRange_temp(T *dest, const T *src, const mdd_function_desc *mfd, int dim, int datastep, void *auxData) +{ + long *pos; + char *srcdata; + T **srcptr; + int i, j; + double min, max, val; + + pos = new long[dim]; srcptr = new T *[dim]; + + srcdata = objectCalcStart((const char *)src, mfd, dim); + + for (i=0; i<dim; i++) + { + pos[i] = mfd[i].useLow; srcptr[i] = (T*)srcdata; + } + + min = ((double*)auxData)[0]; max = ((double*)auxData)[1]; + + do + { + val = (double)(*srcptr[dim-1]); + if (val < min) min = val; + if (val > max) max = val; + i = dim-1; + while (i >= 0) + { + pos[i]++; srcptr[i] = (T*)(((char*)(srcptr[i])) + mfd[i].srcoff); + if (pos[i] <= mfd[i].useHigh) break; + pos[i] = mfd[i].useLow; i--; + } + if (i >= 0) + { + for (j=i+1; j<dim; j++) srcptr[j] = srcptr[i]; + } + } + while (i >= 0); + + ((double*)auxData)[0] = min; ((double*)auxData)[1] = max; + + delete [] srcptr; delete [] pos; + + return 1; +} + + +// Additional template code for objectScaleInter template +inline void objectScaleInterICore(double *srcf, double *destf, double pos, int idx1, int idx2) +{ + destf[idx1] = srcf[idx1] + pos * (srcf[idx2] - srcf[idx1]); +} + +template<class T> +int objectScaleInter_temp(T *dest, const T *src, const mdd_function_desc *mfd, int dim, int datastep, void *auxData) +{ + double *pos, *f, *fwork; + long *lastpos; + long *ipos; + long *dp; + char **srcptr; + char *srcbase; + long offset; + int i, j, k; + int slowOffset; + T *srcdata, *destdata; + long *cubeoff = (long*)auxData; + + f = new double[(1<<dim)]; fwork = new double[(1<<dim)]; + pos = new double[dim]; ipos = new long[dim]; lastpos = new long[dim]; srcptr = new char*[dim]; + dp = new long[dim]; + + // Calculate the starting point + srcbase = objectCalcStart((const char*)src, mfd, dim); + + for (i=0; i<dim; i++) + { + pos[i] = (double)(mfd[i].useLow); lastpos[i] = mfd[i].useLow; + srcptr[i] = srcbase; dp[i] = mfd[i].newLow; + } + + destdata = dest; + + // Main loop: iterate over all dimensions except for the last which is handled separately + // in its own loop for efficiency reasons. + do + { + pos[dim-1] = (double)(mfd[dim-1].useLow); dp[dim-1] = mfd[dim-1].newLow; + + // slowOffset determines whether the fast table-lookup offset or the slower dimension looping + // offset calculation can be used. Table-lookup is always possible as long as the current + // position in the cube is not at the cube's boundary. In the innermost loop slowOffset only + // has to be updated, not recalculated, because there is only a (new) overflow possible in the + // innermost loop-dimension. + slowOffset = 0; + for (i=0; i<dim; i++) + { + ipos[i] = (long)(pos[i]); + // Check for high rather than useHigh because we only get problems if its > high. + if (ipos[i] >= mfd[i].high) {ipos[i] = mfd[i].useHigh; slowOffset = 1;} + } + + srcbase = srcptr[dim-1]; + + if (slowOffset != 0) + { + for (i=0; i<(1<<dim); i++) + { + offset = 0; + for (j=0; j<dim; j++) + { + // Check against high, not useHigh (see above) + if (((i & (1<<j)) != 0) && (ipos[j] < mfd[j].high)) offset += mfd[j].srcoff; + } + srcdata = (T*)(srcbase + offset); + f[i] = (double)(*srcdata); + //if (srcdata+offset >= srchigh) cout << "Overflow1" << endl; + } + } + else + { + for (i=0; i<(1<<dim); i++) + { + srcdata = (T*)(srcbase + cubeoff[i]); + f[i] = (double)(*srcdata); + } + } + + lastpos[dim-1] = (long)(pos[dim-1]); + // Innermost loop + for (k = mfd[dim-1].newHigh - mfd[dim-1].newLow + 1; k>0; k--) + { + // n-linear interpolation + for (j=0; j<(1<<dim); j+=2) + { + objectScaleInterICore(f, fwork, (double)(pos[0] - ipos[0]), j, j+1); + } + for (i=1; i<dim; i++) + { + for (j=0; j<(1<<dim); j+=(1<<(i+1))) + { + objectScaleInterICore(fwork, fwork, (double)(pos[i] - ipos[i]), j, j + (1<<i)); + } + } + // We go in sequential order (highest coordinate first) + *destdata = (T)(fwork[0]); + destdata = (T*)(((char*)destdata) + datastep); + + // end of innermost loop + pos[dim-1] += mfd[dim-1].step; offset = (long)(pos[dim-1]); + if (offset != lastpos[dim-1]) + { + offset -= lastpos[dim-1]; lastpos[dim-1] = (long)(pos[dim-1]); + // Check against high, not useHigh + if (lastpos[dim-1] <= mfd[dim-1].high) + { + srcbase += offset*mfd[dim-1].srcoff; ipos[dim-1] = lastpos[dim-1]; + } + // high, not useHigh + if (lastpos[dim-1] >= mfd[dim-1].high) slowOffset = 1; + offset = (1<<(dim-1)); + memcpy(f, f + offset, offset*sizeof(double)); + + if (slowOffset != 0) + { + for (i=(1<<(dim-1)); i<(1<<dim); i++) + { + offset = 0; + for (j=0; j<dim-1; j++) + { + // high, not useHigh + if (((i & (1<<j)) != 0) && (ipos[j] < mfd[j].high)) offset += mfd[j].srcoff; + } + srcdata = (T*)(srcbase + offset); + f[i] = (double)(*srcdata); + //if (srcdata+offset >= srchigh) cout << "Overflow2" << endl; + } + } + else + { + for (i=(1<<(dim-1)); i<(1<<dim); i++) + { + srcdata = (T*)(srcbase + cubeoff[i - (1<<(dim-1))]); + f[i] = (double)(*srcdata); + } + } + } + } + i = dim-2; + while (i >= 0) + { + dp[i]++; + pos[i] += mfd[i].step; offset = (long)(pos[i]); + if (offset != lastpos[i]) + { + offset -= lastpos[i]; lastpos[i] = (long)(pos[i]); + // high, not useHigh + if (lastpos[i] <= mfd[i].high) + { + srcptr[i] += offset*mfd[i].srcoff; ipos[i] = lastpos[i]; + } + } + if (dp[i] <= mfd[i].newHigh) break; + dp[i] = mfd[i].newLow; pos[i] = (double)(mfd[i].useLow); lastpos[i] = mfd[i].useLow; i--; + } + if (i >= 0) + { + for (j=i+1; j<dim; j++) srcptr[j] = srcptr[i]; + } + } + while (i >= 0); + + delete [] pos; delete [] f; delete [] fwork; + delete [] ipos; delete [] lastpos; delete [] dp; delete [] srcptr; + + return 1; +} + + + + +template<class T> +int objectScaleAverage_temp(T *dest, const T *src, const mdd_function_desc *mfd, int dim, int datastep, void *aux) +{ + long *pos, *subpos, *dp; + T **srcptr, **subptr; + long lastpos, number; + double avg; + int i, j; + T *destdata; + char *srcdata; + + pos = new long[dim]; subpos = new long[dim]; dp = new long[dim]; + srcptr = new T*[dim]; subptr = new T*[dim]; + + srcdata = objectCalcStart((const char*)src, mfd, dim); + + for (i=0; i<dim; i++) + { + pos[i] = (mfd[i].useLow << MDD_FIXPREC); + dp[i] = mfd[i].newLow; srcptr[i] = (T*)srcdata; + } + + destdata = dest; + // Iterate over all cells in the destination object + do + { + //for (i=0; i<dim; i++) cout << (pos[i]>>MDD_FIXPREC) << ':' << (void*)(srcptr[i]) << ' '; cout << endl; + // start averaging over a subcube + avg = 0; number = 0; + // init subcube iteration + for (i=0; i<dim; i++) + { + subpos[i] = pos[i]; subptr[i] = srcptr[dim-1]; // no mistake! + } + do + { + avg += (double)(*(subptr[dim-1])); number++; + i = dim-1; + while (i >= 0) + { + subpos[i] += (1<<MDD_FIXPREC); + subptr[i] = (T*)(((char*)(subptr[i])) + mfd[i].srcoff); + if ((subpos[i] >> MDD_FIXPREC) < ((pos[i] + mfd[i].lstep) >> MDD_FIXPREC)) break; + subpos[i] = pos[i]; + i--; + } + if (i >= 0) + { + for (j=i+1; j<dim; j++) subptr[j] = subptr[i]; + } + } + while (i >= 0); + + *destdata = (T)(avg/number); + destdata = (T*)(((char*)destdata) + datastep); + + i = dim-1; + do + { + dp[i]++; lastpos = (pos[i] >> MDD_FIXPREC); pos[i] += mfd[i].lstep; + srcptr[i] = (T*)(((char*)(srcptr[i])) + ((pos[i] >> MDD_FIXPREC) - lastpos) * mfd[i].srcoff); + if (dp[i] <= mfd[i].newHigh) break; + dp[i] = mfd[i].newLow; pos[i] = (mfd[i].useLow << MDD_FIXPREC); + i--; + } + while (i >= 0); + if (i >= 0) + { + for (j=i+1; j<dim; j++) srcptr[j] = srcptr[i]; + } + } + while (i >= 0); + + delete [] subptr; delete [] srcptr; + delete [] dp; delete [] subpos; delete [] pos; + + return 1; +} + + + +template<class T> +int objectScaleSimple_temp(T *dest, const T *src, const mdd_function_desc *mfd, int dim, int datastep, void *aux) +{ + long *pos, *dp; + long lastpos; + T **srcptr; + T *destdata; + char *srcdata; + int i, j; + + pos = new long[dim]; dp = new long[dim]; srcptr = new T*[dim]; + + srcdata = objectCalcStart((const char*)src, mfd, dim); + + for (i=0; i<dim; i++) + { + pos[i] = (mfd[i].useLow << MDD_FIXPREC); dp[i] = mfd[i].newLow; + srcptr[i] = (T*)srcdata; + } + destdata = dest; + + do + { + *destdata = *(srcptr[dim-1]); + destdata = (T*)(((char*)destdata) + datastep); + i=dim-1; + while (i >= 0) + { + dp[i]++; lastpos = (pos[i] >> MDD_FIXPREC); pos[i] += mfd[i].lstep; + srcptr[i] = (T*)(((char*)srcptr[i]) + ((pos[i] >> MDD_FIXPREC) - lastpos)*mfd[i].srcoff); + if (dp[i] <= mfd[i].newHigh) break; + dp[i] = mfd[i].newLow; pos[i] = (mfd[i].useLow << MDD_FIXPREC); + i--; + } + if (i >= 0) + { + for (j=i+1; j<dim; j++) srcptr[j] = srcptr[i]; + } + } + while (i >= 0); + + delete [] srcptr; delete [] dp; delete [] pos; + + return 1; +} + + + +/* Must not compile non-template code when this file was just included */ +#ifndef __EXECUTABLE__ + +// in raslib/odmgtypes.hh there are the following typedefs: +// typedef unsigned char r_Boolean; +// typedef unsigned char r_Char; +// so the template instantiation is done twice for the same type. gcc 2.95.2 doesn't +// like this. + +// Sadly, this is necessary with VisualC... +#define DECLARE_MDD_FUNC(f) \ +// template int f<r_Boolean> MDD_FUNCTION_SIGNATURE(r_Boolean); \ + template int f<r_Char> MDD_FUNCTION_SIGNATURE(r_Char); \ + template int f<r_Octet> MDD_FUNCTION_SIGNATURE(r_Octet); \ + template int f<r_Short> MDD_FUNCTION_SIGNATURE(r_Short); \ + template int f<r_UShort> MDD_FUNCTION_SIGNATURE(r_UShort); \ + template int f<r_Long> MDD_FUNCTION_SIGNATURE(r_Long); \ + template int f<r_ULong> MDD_FUNCTION_SIGNATURE(r_ULong); \ + template int f<r_Float> MDD_FUNCTION_SIGNATURE(r_Float); \ + template int f<r_Double> MDD_FUNCTION_SIGNATURE(r_Double); + +DECLARE_MDD_FUNC(objectScaleInter_temp); +DECLARE_MDD_FUNC(objectScaleAverage_temp); +DECLARE_MDD_FUNC(objectScaleSimple_temp); +DECLARE_MDD_FUNC(objectRange_temp); + + + +// Calculate the starting point in the source cube +char *objectCalcStart(const char *src, const mdd_function_desc *mfd, int dim) +{ + int i; + char *d; + + d = (char*)src; + for (i=0; i<dim; i++) + { + d += (mfd[i].useLow - mfd[i].low) * mfd[i].srcoff; + } + return d; +} + + +/* + * Wrapper functions for those templates + */ + +#define CAST_MDDPTR(t, src) \ + r_Ref<r_Marray<t> > mddPtr = (r_Ref<r_Marray<t> >)(src) + + +int mdd_createSubcube(r_Ref<r_GMarray> srcMdd, r_Ref<r_GMarray> &newMdd, r_Minterval *domain, r_Database *db) +{ + r_Dimension dim; + char *destdata, *srcdata; + r_Ref<r_GMarray> newMddPtr; + r_Minterval interv; + r_Minterval newInterv; + int tpsize; + int i, j; + long length; + mdd_function_desc *mfd; + long *pos; + char **srcptr; + + interv = srcMdd->spatial_domain(); dim = interv.dimension(); + + if (dim < 1) return 0; + + tpsize = srcMdd->get_type_length(); + + newInterv = r_Minterval(dim); + for (i=0; i<(int)dim; i++) + { + if (domain == NULL) + { + newInterv << interv[i]; + } + else + { + newInterv << (*domain)[i]; + } + } + + if ((destdata = mdd_objectFunctionInitMdd(srcMdd, newMddPtr, newInterv, tpsize, dim, db)) == NULL) + { + cerr << "Couldn't create new MDD object" << endl; + return 0; + } + + mfd = mdd_objectFunctionInitData(interv, newInterv, newInterv, tpsize, MDD_OBJECT_INIT_NEWIV); + + srcdata = objectCalcStart(srcMdd->get_array(), mfd, (int)dim); + + pos = new long[dim]; srcptr = new char *[dim]; + + for (i=0; i<(int)dim; i++) + { + pos[i] = mfd[i].useLow; srcptr[i] = srcdata; + } + + // Lenght of one strip of data to copy + length = (mfd[dim-1].useHigh - mfd[dim-1].useLow + 1) * tpsize; + do + { + memcpy(destdata, srcptr[dim-1], length); + destdata += length; + i = (int)dim-2; + while (i >= 0) + { + pos[i]++; srcptr[i] += mfd[i].srcoff; + if (pos[i] <= mfd[i].useHigh) break; + pos[i] = mfd[i].useLow; i--; + } + if (i >= 0) + { + for (j=i+1; j<(int)dim; j++) srcptr[j] = srcptr[i]; + } + } + while (i >= 0); + + delete [] srcptr; delete [] pos; + delete [] mfd; + + newMdd = newMddPtr; + + return 1; +} + + + +// Support functions for (recursive) member-by-member resampling of a data cube via interpolation +int mdd_objectFunctionPrim(const mdd_function_pointers *mfp, r_Primitive_Type *primType, const char *src, char *dest, const mdd_function_desc *mfd, int dim, int tpsize, void *auxData) +{ + switch (primType->type_id()) + { + case r_Primitive_Type::BOOL: + if (mfp->mddf_bool != NULL) + mfp->mddf_bool((r_Boolean*)dest, (const r_Boolean *)src, mfd, dim, tpsize, auxData); + break; + case r_Primitive_Type::CHAR: + if (mfp->mddf_char != NULL) + mfp->mddf_char((r_Char*)dest, (const r_Char *)src, mfd, dim, tpsize, auxData); + break; + case r_Primitive_Type::OCTET: + if (mfp->mddf_octet != NULL) + mfp->mddf_octet((r_Octet *)dest, (const r_Octet*)src, mfd, dim, tpsize, auxData); + break; + case r_Primitive_Type::SHORT: + if (mfp->mddf_short != NULL) + mfp->mddf_short((r_Short *)dest, (const r_Short*)src, mfd, dim, tpsize, auxData); + break; + case r_Primitive_Type::USHORT: + if (mfp->mddf_ushort != NULL) + mfp->mddf_ushort((r_UShort *)dest, (const r_UShort*)src, mfd, dim, tpsize, auxData); + break; + case r_Primitive_Type::LONG: + if (mfp->mddf_long != NULL) + mfp->mddf_long((r_Long *)dest, (const r_Long*)src, mfd, dim, tpsize, auxData); + break; + case r_Primitive_Type::ULONG: + if (mfp->mddf_ulong != NULL) + mfp->mddf_ulong((r_ULong *)dest, (const r_ULong*)src, mfd, dim, tpsize, auxData); + break; + case r_Primitive_Type::FLOAT: + if (mfp->mddf_float != NULL) + mfp->mddf_float((r_Float *)dest, (const r_Float*)src, mfd, dim, tpsize, auxData); + break; + case r_Primitive_Type::DOUBLE: + if (mfp->mddf_double != NULL) + mfp->mddf_double((r_Double *)dest, (const r_Double*)src, mfd, dim, tpsize, auxData); + break; + default: return 0; + } + return 1; +} + + +// Support functions for (recursive) member-by-member resampling of a data cube via interpolation +int mdd_objectFunctionStruct(const mdd_function_pointers *mfp, r_Structure_Type *structType, const char *src, char *dest, const mdd_function_desc *mfd, int dim, int tpsize, void *auxData) +{ + r_Type *newType; + unsigned long offset; + char *inc, *outc; + + r_Structure_Type::attribute_iterator iter(structType->defines_attribute_begin()); + while (iter != structType->defines_attribute_end()) + { + newType = (*iter).type_of().clone(); + offset = (*iter).offset(); + inc = (char*)src + offset; outc = (char*)dest + offset; + + if (newType->isStructType()) + { + r_Structure_Type *newStructType = (r_Structure_Type*)newType; + mdd_objectFunctionStruct(mfp, newStructType, inc, outc, mfd, dim, tpsize, auxData); + } + else + { + r_Primitive_Type *newPrimType = (r_Primitive_Type*)newType; + mdd_objectFunctionPrim(mfp, newPrimType, inc, outc, mfd, dim, tpsize, auxData); + } + delete newType; + iter++; + } + return 1; +} + + +int mdd_objectFunctionType(const mdd_function_pointers *mfp, const r_Type *baseType, const char *src, char *dest, const mdd_function_desc *mfd, int dim, int tpsize, void *auxData) +{ + if (((r_Type*)baseType)->isStructType()) + { + r_Structure_Type *structType = (r_Structure_Type*)baseType; + return mdd_objectFunctionStruct(mfp, structType, src, dest, mfd, dim, tpsize, auxData); + } + else + { + r_Primitive_Type *primType = (r_Primitive_Type*)baseType; + return mdd_objectFunctionPrim(mfp, primType, src, dest, mfd, dim, tpsize, auxData); + } +} + + +char *mdd_objectFunctionInitMdd(r_Ref<r_GMarray> mddPtr, r_Ref<r_GMarray> &newMddPtr, r_Minterval &newInterv, int tpsize, r_Dimension dim, r_Database *db) +{ + char *destdata; + const char *b; + r_Storage_Layout *store; + + if (mddPtr->get_storage_layout() != NULL) + { + store = new r_Storage_Layout(*(mddPtr->get_storage_layout())); + } + else + { + r_Minterval tileInterv(dim); + r_Aligned_Tiling *dfltTiling; + int dim, i; + + dim = newInterv.dimension(); + for (i=0; i<dim; i++) + { + tileInterv << r_Sinterval((r_Range)0, (r_Range)256); + } + dfltTiling = new r_Aligned_Tiling(tileInterv, tpsize * tileInterv.cell_count()); + store = new r_Storage_Layout(dfltTiling); + } + + if (db == NULL) + { + newMddPtr = new r_GMarray(newInterv, tpsize, store); + } + else + { + newMddPtr = new (db) r_GMarray(newInterv, tpsize, store); + } + + destdata = newMddPtr->get_array(); + newMddPtr->set_current_format(mddPtr->get_current_format()); + if ((b = mddPtr->get_type_name()) != NULL) newMddPtr->set_type_by_name(b); + if ((b = mddPtr->get_type_structure()) != NULL) newMddPtr->set_type_structure(b); + + return destdata; +} + + +mdd_function_desc *mdd_objectFunctionInitData(r_Minterval &interv, r_Minterval &useInterv, r_Minterval &newInterv, int tpsize, unsigned int flags) +{ + int i, offset; + r_Dimension dim; + mdd_function_desc *mfd; + + dim = interv.dimension(); + mfd = new mdd_function_desc[dim]; + + // Calculate scaling factors + for (i=0; i<(int)dim; i++) + { + mfd[i].low = interv[i].low(); mfd[i].high = interv[i].high(); + mfd[i].useLow = useInterv[i].low(); mfd[i].useHigh = useInterv[i].high(); + if ((flags & MDD_OBJECT_INIT_NEWIV) != 0) + { + mfd[i].newLow = newInterv[i].low(); mfd[i].newHigh = newInterv[i].high(); + if ((flags & MDD_OBJECT_INIT_FPSTEP) == MDD_OBJECT_INIT_FPSTEP) + { + // In order to avoid doubling right-boundary pixels we have to use (h-l), not (h-l+1) + // as nominator + mfd[i].step = ((double)(mfd[i].useHigh - mfd[i].useLow)) / (mfd[i].newHigh - mfd[i].newLow + 1); + } + else + { + double h = ((double)(mfd[i].useHigh - mfd[i].useLow + 1)) / (mfd[i].newHigh - mfd[i].newLow + 1); + mfd[i].lstep = (long)(h * (1<<MDD_FIXPREC)); + } + } + } + + offset = tpsize; + for (i=(int)dim-1; i>=0; i--) + { + mfd[i].srcoff = offset; + offset *= (mfd[i].high - mfd[i].low + 1); + } + + return mfd; +} + + + +int mdd_objectRange(r_Ref<r_GMarray> mddPtr, r_Minterval &useInterv, double &min, double &max) +{ + r_Dimension dim; + double minmax[2]; + r_Ref<r_GMarray> newMddPtr; // Dummy... + r_Minterval newInterv; // Dummy... + r_Minterval interv; + const r_Type *baseType; + int tpsize; + mdd_function_desc *mfd; + mdd_function_pointers mfp; + + interv = mddPtr->spatial_domain(); dim = interv.dimension(); + + if (dim < 1) return 0; + + if ((baseType = mddPtr->get_base_type_schema()) == NULL) + { + cerr << "No schema information available!" << endl; + return 0; + } + + tpsize = mddPtr->get_type_length(); + + MDD_INIT_FUNCTIONS(mfp, objectRange_temp); + + mfd = mdd_objectFunctionInitData(interv, useInterv, newInterv, tpsize, 0); + + minmax[0] = DBL_MAX; minmax[1] = -DBL_MAX; // these are unsigned!!! + + mdd_objectFunctionType(&mfp, baseType, mddPtr->get_array(), NULL, mfd, (int)dim, tpsize, (void*)minmax); + + min = minmax[0]; max = minmax[1]; + + delete [] mfd; + + return 1; +} + + + +// Scale an object using n-linear interpolation (recommended for magnification) +int mdd_objectScaleInter(r_Ref<r_GMarray> mddPtr, r_Minterval &useInterv, r_Ref<r_GMarray> &newMdd, r_Minterval &newInterv) +{ + r_Dimension dim; + r_Minterval interv; + r_Ref<r_GMarray> newMddPtr; + mdd_function_desc *mfd; + int i, j; + long *cubeoff; + char *srcdata, *destdata; + int tpsize; + long offset; + const r_Type *baseType; + mdd_function_pointers mfp; + + interv = mddPtr->spatial_domain(); + dim = interv.dimension(); + if ((dim < 1) || (dim != newInterv.dimension())) return 0; + + // Type information available? + if ((baseType = mddPtr->get_base_type_schema()) == NULL) + { + cerr << "No schema information available!" << endl; + return 0; + } + + tpsize = mddPtr->get_type_length(); + + if ((destdata = mdd_objectFunctionInitMdd(mddPtr, newMddPtr, newInterv, tpsize, dim)) == NULL) + return 0; + + MDD_INIT_FUNCTIONS(mfp, objectScaleInter_temp); + + // create temporary arrays + mfd = mdd_objectFunctionInitData(interv, useInterv, newInterv, tpsize, MDD_OBJECT_INIT_FPSTEP); + cubeoff = new long[(1<<dim)]; + + // Debugging + /*T *srchigh; + offset=1; + for (i=0; i<dim; i++) offset *= (interv[i].high() - interv[i].low() + 1); + srchigh = (T*)(mddPtr->get_array()) + offset;*/ + + srcdata = (char*)(mddPtr->get_array()); + + // Precalculate cube-offsets for more speed in inner loop + for (i=0; i<(1<<dim); i++) + { + offset = 0; + for (j=0; j<(int)dim; j++) + { + if ((i & (1<<j)) != 0) offset += mfd[j].srcoff; + } + cubeoff[i] = offset; + } + + mdd_objectFunctionType(&mfp, baseType, srcdata, destdata, mfd, (int)dim, tpsize, (void*)cubeoff); + + delete [] cubeoff; delete [] mfd; + + newMdd = newMddPtr; + + return 1; +} + + +// Scale an object by averaging cell values -- recommended for scaling down +int mdd_objectScaleAverage(r_Ref<r_GMarray> mddPtr, r_Minterval &useInterv, r_Ref<r_GMarray> &newMdd, r_Minterval &newInterv) +{ + r_Dimension dim; + r_Minterval interv; + r_Ref<r_GMarray> newMddPtr; + mdd_function_desc *mfd; + char *srcdata, *destdata; + int tpsize; + const r_Type *baseType; + mdd_function_pointers mfp; + + interv = mddPtr->spatial_domain(); + dim = interv.dimension(); + + if ((dim < 1) || (dim != newInterv.dimension())) return 0; + + if ((baseType = mddPtr->get_base_type_schema()) == NULL) + { + cerr << "No schema information available!" << endl; + return 0; + } + + tpsize = mddPtr->get_type_length(); + + if ((destdata = mdd_objectFunctionInitMdd(mddPtr, newMddPtr, newInterv, tpsize, dim)) == NULL) + return 0; + + MDD_INIT_FUNCTIONS(mfp, objectScaleAverage_temp); + + mfd = mdd_objectFunctionInitData(interv, useInterv, newInterv, tpsize, MDD_OBJECT_INIT_NEWIV); + + srcdata = mddPtr->get_array(); + + mdd_objectFunctionType(&mfp, baseType, srcdata, destdata, mfd, (int)dim, tpsize, NULL); + + delete [] mfd; + + newMdd = (r_Ref<r_GMarray>)newMddPtr; + + return 1; +} + + +int mdd_objectScaleSimple(r_Ref<r_GMarray> mddPtr, r_Minterval &useInterv, r_Ref<r_GMarray> &newMdd, r_Minterval &newInterv) +{ + r_Dimension dim; + r_Minterval interv; + r_Ref<r_GMarray> newMddPtr; + mdd_function_desc *mfd; + char *srcdata, *destdata; + int tpsize; + const r_Type *baseType; + mdd_function_pointers mfp; + + interv = mddPtr->spatial_domain(); + dim = interv.dimension(); + + if ((dim < 1) || (dim != newInterv.dimension())) return 0; + + if ((baseType = mddPtr->get_base_type_schema()) == NULL) + { + cerr << "No schema information available!" << endl; + return 0; + } + + tpsize = mddPtr->get_type_length(); + + if ((destdata = mdd_objectFunctionInitMdd(mddPtr, newMddPtr, newInterv, tpsize, dim)) == NULL) + return 0; + + MDD_INIT_FUNCTIONS(mfp, objectScaleSimple_temp); + + mfd = mdd_objectFunctionInitData(interv, useInterv, newInterv, tpsize, MDD_OBJECT_INIT_NEWIV); + + srcdata = mddPtr->get_array(); + + mdd_objectFunctionType(&mfp, baseType, srcdata, destdata, mfd, (int)dim, tpsize, NULL); + + newMdd = (r_Ref<r_GMarray>)newMddPtr; + + delete [] mfd; + + return 1; +} + + +int mdd_objectChangeEndianness(r_Ref<r_GMarray> mddPtr, r_Minterval &useInterv, r_Ref<r_GMarray> *newMdd, r_Minterval *newInterv) +{ + char *srcdata; + const r_Base_Type *baseType; + r_Ref<r_GMarray> newMddPtr; // only used in "new" mode + r_Minterval dom, *iterDom; + int tpsize; + + if ((baseType = (r_Base_Type*)(mddPtr->get_base_type_schema())) == NULL) + { + cerr << "No schema information available!" << endl; + return 0; + } + + tpsize = mddPtr->get_type_length(); + + srcdata = mddPtr->get_array(); + + dom = mddPtr->spatial_domain(); + + if (newMdd == NULL) + { + r_Endian::swap_array( baseType, dom, dom, srcdata, srcdata ); + } + else + { + void *destdata; + r_Dimension dim = dom.dimension(); + + if ((destdata = mdd_objectFunctionInitMdd(mddPtr, newMddPtr, *newInterv, tpsize, dim)) == NULL) + return 0; + + r_Endian::swap_array( baseType, dom, *newInterv, *newInterv, *newInterv, srcdata, destdata ); + } + + if (newMdd != NULL) + { + *newMdd = newMddPtr; + } + + return 1; +} + +#endif + +#endif diff --git a/applications/rview/rviewMDD.hh b/applications/rview/rviewMDD.hh new file mode 100644 index 0000000..0248b35 --- /dev/null +++ b/applications/rview/rviewMDD.hh @@ -0,0 +1,185 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * The rviewMDD module encapsulates various generic operations on + * MDD objects; it relies heavily on templates for this. All functions + * provided here are a) completely independent from the rest of rView + * and b) work on any kind of MDD, i.e. no restrictions whatsoever on + * dimensionality or base type. Most functions utilize + * mdd_objectFunctionType() for this which iterates over the MDD object + * basetype first and calls functions provided in the mdd_function_pointers + * structure which perform the required operation on each of the primitive + * basetypes. + * + * Functions provided are: + * + * - mdd_objectRange(): return the minimum and maximum cell values. + * - mdd_createSubcube(): creates a new MDD object by copying a subcube + * (or all of) a source MDD object; optionally persistent. + * - mdd_objectScaleInter(): creates new object by resampling (part of) + * the source object using n-linear interpolation. Use for upsampling. + * - mdd_objectScaleAverage(): creates new object by resampling (part of) + * the source object using averaging. Use for downsampling. + * - mdd_objectScaleSimple(): creates new object by scaling (part of) + * the source object using simple nearest neighbour. Fast but blocky. + * - mdd_objectChangeEndianness(): creates a new object where the + * endianness is inverted compared to the source object, or changes + * the object itself. + * + * COMMENTS: + * None + */ + + + +#ifndef _RVIEW_MDD_H_ +#define _RVIEW_MDD_H_ + + + +#ifdef __GNUG__ +#pragma interface +#endif + + +#include "rasodmg/ref.hh" +#include "rasodmg/marray.hh" +#include "rasodmg/gmarray.hh" +#include "rasodmg/database.hh" +#include "raslib/odmgtypes.hh" + +#include "raslib/primitivetype.hh" +#include "raslib/structuretype.hh" + + + + + +/* + * Generic functionality on MDD objects -- data types and support functions + */ + +// 1) Data types + +// A descriptor of the mdd iteration +typedef struct mdd_function_desc { + double step; + long lstep; + long srcoff; + long low; // Entire domain of source cube + long high; + long useLow; // Domain that should be used, must be wholly contained in source cube + long useHigh; + long newLow; // Domain of resulting cube + long newHigh; +} mdd_function_desc; + + + +// The signature of a basic mdd function +// Important note: VISUAL C needs the variable pointers first, followed by the +// constant pointers, or the variable pointers will be interpreted as constant +// ones too. No joke. +#define MDD_FUNCTION_SIGNATURE(type) \ + (type *dest, const type *src, const mdd_function_desc *mfd, int dim, int datastep, void *auxData) + +typedef int (*mdd_func_bool)MDD_FUNCTION_SIGNATURE(r_Boolean); +typedef int (*mdd_func_char)MDD_FUNCTION_SIGNATURE(r_Char); +typedef int (*mdd_func_octet)MDD_FUNCTION_SIGNATURE(r_Octet); +typedef int (*mdd_func_short)MDD_FUNCTION_SIGNATURE(r_Short); +typedef int (*mdd_func_ushort)MDD_FUNCTION_SIGNATURE(r_UShort); +typedef int (*mdd_func_long)MDD_FUNCTION_SIGNATURE(r_Long); +typedef int (*mdd_func_ulong)MDD_FUNCTION_SIGNATURE(r_ULong); +typedef int (*mdd_func_float)MDD_FUNCTION_SIGNATURE(r_Float); +typedef int (*mdd_func_double)MDD_FUNCTION_SIGNATURE(r_Double); + +// A structure of mdd functions for all atomic base types +typedef struct mdd_function_pointers { + mdd_func_bool mddf_bool; + mdd_func_char mddf_char; + mdd_func_octet mddf_octet; + mdd_func_short mddf_short; + mdd_func_ushort mddf_ushort; + mdd_func_long mddf_long; + mdd_func_ulong mddf_ulong; + mdd_func_float mddf_float; + mdd_func_double mddf_double; +} mdd_function_pointers; + +// Init a mdd_function_pointers structure with a mdd function template (therefore the +// same function name for all members) +#define MDD_INIT_FUNCTIONS(mfp, tf) \ + mfp.mddf_bool = (mdd_func_bool)tf; \ + mfp.mddf_char = (mdd_func_char)tf; \ + mfp.mddf_octet = (mdd_func_octet)tf; \ + mfp.mddf_short = (mdd_func_short)tf; \ + mfp.mddf_ushort = (mdd_func_ushort)tf; \ + mfp.mddf_long = (mdd_func_long)tf; \ + mfp.mddf_ulong = (mdd_func_ulong)tf; \ + mfp.mddf_float = (mdd_func_float)tf; \ + mfp.mddf_double = (mdd_func_double)tf; + + + +// 2) Support functions +extern char *objectCalcStart(const char *src, const mdd_function_desc *mfd, int dim); + +// execute mdd functions over arbitrary objects +extern int mdd_objectFunctionPrim(const mdd_function_pointers *mfp, r_Primitive_Type *primType, const char *src, char *dest, const mdd_function_desc *mfd, int dim, int tpsize, void *auxData); +extern int mdd_objectFunctionStruct(const mdd_function_pointers *mfp, r_Structure_Type *structType, const char *src, char *dest, const mdd_function_desc *mfd, int dim, int tpsize, void *auxData); +extern int mdd_objectFunctionType(const mdd_function_pointers *mfp, const r_Type *baseType, const char *src, char *dest, const mdd_function_desc *mfd, int dim, int tpsize, void *auxData); + +// Initialising mdd functions +#define MDD_OBJECT_INIT_NEWIV 1 +#define MDD_OBJECT_INIT_FPSTEP (2 | MDD_OBJECT_INIT_NEWIV) +extern char *mdd_objectFunctionInitMdd(r_Ref<r_GMarray> mddPtr, r_Ref<r_GMarray> &newMddPtr, r_Minterval &newInterv, int tpsize, r_Dimension dim, r_Database *db=NULL); +extern mdd_function_desc *mdd_objectFunctionInitData(r_Minterval &interv, r_Minterval &useInterv, r_Minterval &newInterv, int tpsize, unsigned int flags=0); + + + + + + +/* + * Wrapper functions for mdd function templates + */ +int mdd_objectRange(r_Ref<r_GMarray> mddObj, r_Minterval &useInterv, double &min, double &max); + +int mdd_createSubcube(r_Ref<r_GMarray> srcMdd, r_Ref<r_GMarray> &newMdd, r_Minterval *domain, r_Database *db); + +int mdd_objectScaleInter(r_Ref<r_GMarray> srcMdd, r_Minterval &useInterv, r_Ref<r_GMarray> &newMdd, r_Minterval &newInterv); + +int mdd_objectScaleAverage(r_Ref<r_GMarray> srcMdd, r_Minterval &useInterv, r_Ref<r_GMarray> &newMdd, r_Minterval &newInterv); + +int mdd_objectScaleSimple(r_Ref<r_GMarray> srcMdd, r_Minterval &useInterv, r_Ref<r_GMarray> &newMdd, r_Minterval &newInterv); + +int mdd_objectChangeEndianness(r_Ref<r_GMarray> srcMdd, r_Minterval &useInterv, r_Ref<r_GMarray> *newMdd=NULL, r_Minterval *newInterv=NULL); + +#if (defined(EARLY_TEMPLATE) && defined(__EXECUTABLE__)) +#include "rviewMDD.cpp" +#endif + +#endif diff --git a/applications/rview/rviewOSection.cpp b/applications/rview/rviewOSection.cpp new file mode 100644 index 0000000..f41346d --- /dev/null +++ b/applications/rview/rviewOSection.cpp @@ -0,0 +1,1313 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * A viewer for orthosections in 3D images (bias on medical images) + * + * COMENTS: + * None + */ + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + + +#include <string.h> +#include <stdlib.h> +#include <iostream.h> +#include <math.h> + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + +#include "raslib/rmdebug.hh" + +#include "rviewApp.hh" +#include "rviewOSection.hh" +#include "rviewTypes.hh" +#include "rviewPrefs.hh" + +#include "cube_render.h" + + + +const int rviewOSectionImage::osection_ctrly = 140; +const int rviewOSectionImage::osection_rcwidth = 60; +const int rviewOSectionImage::osection_sheight = 40; +const int rviewOSectionImage::osection_chkwidth = 60; +const int rviewOSectionImage::osection_chkheight = 20; +const int rviewOSectionImage::osection_twidth = 40; +const int rviewOSectionImage::osection_theight = 50; +const int rviewOSectionImage::osection_bwidth = 50; +const int rviewOSectionImage::osection_bheight = 30; + + + + + + +static void outputGeomData(const vertex_fp *v) +{ + unsigned int j; + for (j=0; j<4; j++) + { + cout << '(' << v[j].x << ',' << v[j].y << ',' << v[j].z << ')'; + } + cout << endl; +} + + + + + +const unsigned int rviewOSectionImage::numSections = 3; + +const char *rviewOSectionImage::sliderLabels[rviewOSectionImage::numSections] = { + "textSliceX", + "textSliceY", + "textSliceZ" +}; + +const char *rviewOSectionImage::view_Thickness = "thickness"; +const char *rviewOSectionImage::view_MidPoint = "midPoint"; +const char *rviewOSectionImage::view_UseBBox = "useBBox"; + +/* + * The actual viewer class + */ + +// types for internal use defined here (to avoid including cube_render.h from headers) + +// the dimensions a section is mapped to (z is the flat one) +struct rviewOSectionImage::section_map_s { + unsigned int x, y, z; +}; + +// a partition descriptor (each quadrant is described by one such partition; +// normally there are 12 in total) +struct rviewOSectionImage::section_part_s { + unsigned int section; + unsigned long offset; + tex_desc td; + vertex_fp gr[4]; // fully rotated and translated + vertex_fp grav; // center of gravity + real_t distance; + int quadrant; +}; + + +rviewOSectionImage::rviewOSectionImage(mdd_frame *mf, unsigned int flags) : + rviewRenderImage(mf, osection_ctrly, flags) +{ + secmap = NULL; + partition = NULL; + sliders = NULL; + sltexts = NULL; + thickness = prefs->imgOrthoThick; + + unsigned int i, j; + + secmap = new section_map_t[numSections]; + memset(secmap, 0, numSections*sizeof(section_map_t)); + currentSection = numSections + 1; + + for (i=0; i<numSections; i++) + { + secmap[i].x = i; + secmap[i].y = (i >= 2) ? i-2 : i+1; + secmap[i].z = (i >= 1) ? i-1 : i+2; + } + + intersection = r_Point(dimMDD); + for (i=0; i<dimMDD; i++) + { + intersection[i] = (interv[i].high() + interv[i].low()) / 2; + } + + boundingBox = new rviewCheckBox(ctrlPanel); + doBoundingBox = prefs->imgOrthoBBox; + boundingBox->SetValue(doBoundingBox); + + //sliders = new rviewSlider*[numSections]; + sliders = new rviewSpecialSlider*[numSections]; + sltexts = new rviewText*[numSections]; + + for (i=0; i<numSections; i++) + { + // little precaution against MDD with less than 2D. Proper error in openViewer() + j = (secmap[i].z < dimMDD) ? secmap[i].z : dimMDD-1; + //sliders[i] = new rviewSlider(ctrlPanel, intersection[j], interv[j].low(), interv[j].high(), 100, lman->lookup(sliderLabels[i])); + sliders[i] = new rviewSpecialSlider(this, ctrlPanel, intersection[j], interv[j].low(), interv[j].high(), 100, lman->lookup(sliderLabels[i])); + sltexts[i] = new rviewText(ctrlPanel, intersection[j]); + } + + thickText = new rviewText(ctrlPanel, thickness); + thickText->SetValue(thickness); +} + + +int rviewOSectionImage::openViewer(void) +{ + if (dimMDD != 3) + { + rviewErrorbox::reportError(lman->lookup("errorModeDim"), rviewOSectionImage::getFrameName(), "openViewer"); + objectInitializedOK = FALSE; + return -1; + } + + partition = new section_part_t[numSections * 4]; + memset(partition, 0, numSections * 4 * sizeof(section_part_t)); + numPartitions = 0; + + if (rviewRenderImage::openViewer() == 0) + { + openViewerEpilogue(rviewOSectionImage::getFrameType()); + + return 0; + } + return -1; +} + + +rviewOSectionImage::~rviewOSectionImage(void) +{ + if (secmap != NULL) + delete [] secmap; + + if (partition != NULL) + delete [] partition; + + if (sliders != NULL) + delete [] sliders; + + if (sltexts != NULL) + delete [] sltexts; +} + + +const char *rviewOSectionImage::getFrameName(void) const +{ + return "rviewOSectionImage"; +} + +rviewFrameType rviewOSectionImage::getFrameType(void) const +{ + return rviewFrameTypeOSectionImage; +} + +int rviewOSectionImage::getViewerType(void) const +{ + return RVIEW_RESDISP_IMGOSECT; +} + + +void rviewOSectionImage::label(void) +{ + unsigned int i; + + setDisplayTitle(lman->lookup("titleImageOrtho")); + boundingBox->SetLabel(lman->lookup("textBBox")); + thickText->SetLabel(lman->lookup("textOrthoThickness")); + for (i=0; i<numSections; i++) + { + sliders[i]->SetLabel(lman->lookup(sliderLabels[i])); + } + rviewRenderImage::label(); +} + + +void rviewOSectionImage::updateSlice(unsigned int num, long value, bool useDummy) +{ + unsigned int map = secmap[num].z; + //cout << "update slice " << num << endl; + // make an update if the value has changed or we need a full update and the current + // section is a dummy. + if ((intersection[map] != value) || (!useDummy && (getSectionProjection(num) != value))) + { + if ((interv[map].low() <= value) && (value <= interv[map].high())) + { + intersection[map] = value; + sliders[num]->SetValue(value); + sltexts[num]->SetValue(value); + + if (useDummy) + createDummySection(num); + else + ensureSections(); + + fillBuffer(); + updatePixmap(imgData, imgData); + } + } +} + + +void rviewOSectionImage::refreshSlices(bool force) +{ + if (force) + flushSlices(); + + ensureSections(); + fillBuffer(); + updatePixmap(imgData, imgData); +} + + +int rviewOSectionImage::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_CHECKBOX_COMMAND) + { + if (&obj == (wxObject*)boundingBox) + { + doBoundingBox = boundingBox->GetValue(); + fillBuffer(); + pcanv->updateDisplay(); + return 1; + } + } + else if (type == wxEVENT_TYPE_SLIDER_COMMAND) + { + unsigned int i; + + for (i=0; i<numSections; i++) + { + if (&obj == (wxObject*)(sliders[i])) + { + updateSlice(i, sliders[i]->GetValue(), TRUE); + return 1; + } + } + } + else if (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND) + { + unsigned int i; + + for (i=0; i<numSections; i++) + { + if (&obj == (wxObject*)(sltexts[i])) + { + updateSlice(i, atoi(sltexts[i]->GetValue()), FALSE); + return 1; + } + } + + if (&obj == (wxObject*)thickText) + { + int newThick = atoi(thickText->GetValue()); + if ((newThick > 0) && (newThick != thickness)) + { + thickness = newThick; + refreshSlices(TRUE); + return 1; + } + } + } + + return rviewRenderImage::process(obj, evt); +} + + +void rviewOSectionImage::childMouseEvent(wxWindow *child, wxMouseEvent &mevt) +{ + unsigned int i; + + for (i=0; i<numSections; i++) + { + if (child == (wxWindow*)(sliders[i])) + { + float mx, my; + unsigned int newSect; + + mevt.Position(&mx, &my); + if (sliders[i]->PositionInWell(mx, my)) + newSect = i; + else + newSect = numSections + 1; + + if (newSect != currentSection) + { + currentSection = newSect; + fillBuffer(); + updatePixmap(imgData, imgData); + } + break; + } + } + // pass on event + rviewRenderImage::childMouseEvent(child, mevt); +} + + +void rviewOSectionImage::OnSize(int w, int h) +{ + rviewRenderImage::OnSize(w, h); + + int x, y, width, orgy, posx, posy; + unsigned int i; + + ctrlPanel->GetClientSize(&x, &y); + + boundingBox->SetSize(w + 2*display_cnvborder - 3*display_border - 4*display_pbwidth, display_border, osection_chkwidth, osection_chkheight); + + x -= 2*display_border; + orgy = totalCtrlHeight - osection_ctrly + display_border; + width = x - osection_rcwidth - osection_twidth; + if (width < 100) + width = 100; + + posx = display_border; + posy = orgy; + for (i=0; i<numSections; i++) + { + sliders[i]->SetSize(posx, posy, width - display_border, osection_sheight); + sltexts[i]->SetSize(posx + width, posy + display_border, osection_twidth, osection_sheight); + posy += osection_sheight; + } + + posx = x - osection_rcwidth + 3*display_border/2; + posy = orgy; + thickText->SetSize(posx, posy, osection_rcwidth, osection_theight); +} + + +bool rviewOSectionImage::doUpdate(int flags) +{ + return FALSE; +} + + +char *rviewOSectionImage::initMode(void) +{ + sprintf(projString, "*:*, *:*, *:*"); + project->SetValue(projString); + setModeDimension(3); + + ensureSections(); + + return rviewRenderImage::initMode(); +} + + +char *rviewOSectionImage::setupEnvironment(int w, int h) +{ + if (setupEnvBase(w, h, getCsmapArray(), &csmap, csInterv) != 0) + return NULL; + + return imgData; +} + + +void rviewOSectionImage::fillBuffer(void) +{ + bool cspaceOK; + unsigned int i, j; + + performPartition(); + + // bounding boxes? + graphEnv->bbox_colour = (doBoundingBox) ? 0xffffff : 0xffffffff; + // z params + graphEnv->zpro = setup.zpro; graphEnv->clipz = setup.clipz; + + fillBufferBackground(doValToCspace, cspaceOK, getCsmapArray(), &csmap, csInterv, baseType, doFullRangeCspace); + + real_t ztranslate = graphEnv->zpro + zoff; + for (i=0; i<numPartitions; i++) + { + vertex_fp *v = &(partition[i].grav); + partition[i].distance = sqrt((v->x * v->x) + (v->y * v->y) + ((v->z + ztranslate) * (v->z + ztranslate))); + } + + // finally sort it by the distance to the observer + // this is basically at most 12 entries, so there's no point in using complicated sorting techniques + for (i=0; i<numPartitions-1; i++) + { + for (j=i; j<numPartitions; j++) + { + if (partition[j].distance > partition[i].distance) + { + section_part_t aux; + memcpy(&aux, partition+i, sizeof(section_part_t)); + memcpy(partition+i, partition+j, sizeof(section_part_t)); + memcpy(partition+j, &aux, sizeof(section_part_t)); + } + } + } + //cout << "PARTITIONS " << numPartitions << endl; + for (i=0; i<numPartitions; i++) + { + if (sectionValid(partition[i].section)) + { + tex_desc *td = &(partition[i].td); + if ((td->floatType != 0) && (csmap != NULL)) + { + td->minVal = csmap->getMinVal(); + td->maxVal = csmap->getMaxVal(); + } + td->data = getSectionArray(partition[i].section) + partition[i].offset; + memcpy(geomUse, &(partition[i].gr), 4 * sizeof(vertex_fp)); + geomUse[0].z += ztranslate; + //outputGeomData(geomUse); + RenderCubeSurf(geomUse, graphEnv, td); + + if (partition[i].section == currentSection) + { + render_desc renderDesc; + vertex_fp from, to; + vertex_fp *ax1, *ax2; + + renderDesc.graphEnv = graphEnv; + renderDesc.texDesc = td; + ax1 = geomUse + (secmap[partition[i].section].x + 1); + ax2 = geomUse + (secmap[partition[i].section].y + 1); + from.x = geomUse[0].x + ax1->x; + from.y = geomUse[0].y + ax1->y; + from.z = geomUse[0].z + ax1->z; + to.x = geomUse[0].x + ax2->x; + to.y = geomUse[0].y + ax2->y; + to.z = geomUse[0].z + ax2->z; + Render3DLine(&from, &to, &renderDesc, graphEnv->bbox_colour); + from.x = geomUse[0].x; + from.y = geomUse[0].y; + from.z = geomUse[0].z; + to.x = geomUse[0].x + ax1->x + ax2->x; + to.y = geomUse[0].y + ax1->y + ax2->y; + to.z = geomUse[0].z + ax1->z + ax2->z; + Render3DLine(&from, &to, &renderDesc, graphEnv->bbox_colour); + } + } + } + + if (doValToCspace && cspaceOK) + { + translateBufferToCspace(baseType); + } +} + + +int rviewOSectionImage::makeMinterval(unsigned int num, r_Minterval &dom) +{ + r_Range low, high; + r_Sinterval aux[3]; + dom = r_Minterval(dimMDD); + unsigned int map; + + aux[secmap[num].x] = interv[secmap[num].x]; + aux[secmap[num].y] = interv[secmap[num].y]; + // projection dimension + map = secmap[num].z; + low = intersection[map] - thickness/2; + high = low + thickness - 1; + if (low < interv[map].low()) + low = interv[map].low(); + if (high > interv[map].high()) + high = interv[map].high(); + aux[map] = r_Sinterval(low, high); + + dom << aux[0] << aux[1] << aux[2]; + //cout << "INTERVAL " << dom << endl; + + return 0; +} + + +int rviewOSectionImage::performPartition(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewOSectionImage", "performPartition()"); + unsigned int i, j; + section_part_t *part = partition; + r_Point midpoint(dimMDD); + + for (i=0; i<dimMDD; i++) + midpoint[i] = (interv[i].high() + interv[i].low() + 1) / 2; + + for (i=0; i<numSections; i++) + { + if (sectionValid(i)) + { + const r_Minterval secdom = getSectionDomain(i); + r_Range low, high; + int map; + + // note: all MDD have 3D domains, describing their actual position within + // the entire cube, therefore these dimensions can be used directly for the + // spatial representation, making things a lot easier than using 2D domains + // would. + map = secmap[i].z; + low = secdom[secmap[i].z].low(); + high = secdom[secmap[i].z].high(); + // check whether this section should be displayed at all (-> projection string) + if ((pt1[map] <= high) && (pt2[map] >= low)) + { + // yes, now clip it + if (low < pt1[map]) + low = pt1[map]; + if (high > pt2[map]) + high = pt2[map]; + + r_Sinterval ziv(low, high); + + for (j=0; j<4; j++) + { + r_Sinterval auxdom[3]; + + auxdom[secmap[i].z] = ziv; + + map = secmap[i].x; + if ((j & 1) == 0) + { + low = pt1[map]; high = intersection[map] - thickness/2; + } + else + { + low = intersection[map] + thickness/2; high = pt2[map]; + } + // the current projection string might project some of these partitions away + if (low <= high) + { + // this can still happen if the intersection point is outside the current projection + // domain for the still visible quadrant. + if (low < pt1[map]) + low = pt1[map]; + if (high > pt2[map]) + high = pt2[map]; + + auxdom[map] = r_Sinterval(low, high); + + map = secmap[i].y; + if ((j & 2) == 0) + { + low = pt1[map]; high = intersection[map] - thickness/2; + } + else + { + low = intersection[map] + thickness/2; high = pt2[map]; + } + // ditto + if (low <= high) + { + // see above + if (low < pt1[map]) + low = pt1[map]; + if (high > pt2[map]) + high = pt2[map]; + + r_Minterval dom(dimMDD); // domain of quadrant in absolute coordinates + + auxdom[map] = r_Sinterval(low, high); + dom << auxdom[0] << auxdom[1] << auxdom[2]; + + // now calculate the descriptor for this domain + tex_desc *td = &(part->td); + part->section = i; + const r_Minterval &pardom = getSectionParent(i); + td->dimx = pardom[0].high() - pardom[0].low() + 1; + td->dimy = pardom[1].high() - pardom[1].low() + 1; + td->dimz = pardom[2].high() - pardom[2].low() + 1; + td->widthx = dom[0].high() - dom[0].low() + 1; + td->widthy = dom[1].high() - dom[1].low() + 1; + td->widthz = dom[2].high() - dom[2].low() + 1; + part->offset = (((dom[0].low() - pardom[0].low()) * td->dimy + (dom[1].low() - pardom[1].low())) * td->dimz + (dom[2].low() - pardom[2].low())) * baseSize; + td->baseSize = baseSize; + td->floatType = ((baseType == rbt_float) || (baseType == rbt_double)); + part->quadrant = j; + //cout << "DOM " << dom << ", PARENT " << pardom << ", OFFSET " << part->offset << ", size " << pardom.cell_count() << endl; + + vertex_fp *v, *w; + + // this rotates the subcube's origin. The total origin is 0 for now. + // use gr[1] as temporary workspace for the unrotated origin + w = &(part->gr[1]); + w->x = dom[0].low() - midpoint[0]; // or is it secmap? + w->y = dom[1].low() - midpoint[1]; + w->z = dom[2].low() - midpoint[2]; + + v = &(part->gr[0]); + v->x = cubeScale * (w->x * rot[0].x + w->y * rot[0].y + w->z * rot[0].z); + v->y = cubeScale * (w->x * rot[1].x + w->y * rot[1].y + w->z * rot[1].z); + v->z = cubeScale * (w->x * rot[2].x + w->y * rot[2].y + w->z * rot[2].z); + + // then rotate the whole thing and calculate the center of gravity (= origin + 0.5 * diagonal) + unsigned int k; + + part->grav.x = part->gr[0].x; + part->grav.y = part->gr[0].y; + part->grav.z = part->gr[0].z; + for (k=1; k<4; k++) + { + geomUse[k].x = 0; geomUse[k].y = 0; geomUse[k].z = 0; + } + geomUse[1].x = td->widthx; + geomUse[2].y = td->widthy; + geomUse[3].z = td->widthz; + + for (k=1; k<4; k++) + { + v = &(part->gr[k]); w = &(geomUse[k]); + // this rotates the cube's axes (edge lengths are width = dom.high() - dom.low() + 1); + v->x = cubeScale * (w->x * rot[0].x + w->y * rot[0].y + w->z * rot[0].z); + v->y = cubeScale * (w->x * rot[1].x + w->y * rot[1].y + w->z * rot[1].z); + v->z = cubeScale * (w->x * rot[2].x + w->y * rot[2].y + w->z * rot[2].z); + part->grav.x += 0.5 * (v->x); + part->grav.y += 0.5 * (v->y); + part->grav.z += 0.5 * (v->z); + } + + // advance partition pointer + part++; + } + } + } + } // entire section clipped away by projection string + } + } + numPartitions = part - partition; + + // sorting isn't done here because it depends on the exact current perspective view. + RMDBGIF(4, RMDebug::module_applications, "rviewOSection", \ + for (i=0; i<numPartitions; i++){\ + RMInit::dbgFileOut << i << ": " << partition[i].grav.z << ' ';\ + outputGeomData(&(partition[i].gr[j]));\ + };\ + RMInit::dbgFileOut << endl ; + ); + // done; the first entry in partitions is the remotest + return 0; +} + + +int rviewOSectionImage::saveView(FILE *fp) +{ + int status = rviewRenderImage::saveView(fp); + + writeViewParam(fp, view_Thickness, (long)thickness); + writeViewParam(fp, view_UseBBox, (long)doBoundingBox); + + long middle[numSections]; + for (unsigned int i=0; i<numSections; i++) + middle[i] = (long)(intersection[i]); + writeViewParam(fp, view_MidPoint, numSections, middle); + + return status; +} + + +int rviewOSectionImage::readView(const char *key, const char *value) +{ + int status = rviewRenderImage::readView(key, value); + + if (status == 0) + { + if (strcmp(key, view_Thickness) == 0) + { + thickness = atoi(value); + return 1; + } + else if (strcmp(key, view_UseBBox) == 0) + { + doBoundingBox = (bool)atoi(value); + return 1; + } + else if (strcmp(key, view_MidPoint) == 0) + { + long middle[numSections]; + if (readVector(value, numSections, middle) == 0) + { + for (unsigned int i=0; i<numSections; i++) + intersection[i] = (r_Range)(middle[i]); + } + return 1; + } + return 0; + } + return status; +} + + +void rviewOSectionImage::loadViewFinished(void) +{ + rviewRenderImage::loadViewFinished(); + + thickText->SetValue(thickness); + boundingBox->SetValue(doBoundingBox); + + unsigned char modified[numSections]; + unsigned int i; + + for (i=0; i<numSections; i++) + { + long value = intersection[secmap[i].z]; + modified[i] = (value != (r_Range)(sliders[i]->GetValue())); + //cout << i << ": IS " << sliders[i]->GetValue() << ", NEW " << value << ", MOD " << (int)(modified[i]) << endl; + if (modified[i]) + { + sliders[i]->SetValue((int)value); + sltexts[i]->SetValue((int)value); + } + } + for (i=0; i<numSections; i++) + { + if (modified[i]) + updateSlice(i, intersection[secmap[i].z], FALSE); + } +} + + + + + + +/* + * Partial orthosection: only the slices are in main memory, new ones are + * loaded from the database on demand. + */ + +// a section descriptor (the three slices through the volume are sections) +struct rviewOSectionPartImage::section_desc_s { + r_GMarray *mdd; + r_Range proj; +}; + + +/* + * The creator for the viewer + * This is needed because the interface differs from the other viewers in that + * we don't own the object to visualize but have to read descriptors from a + * collection first + */ +rviewOSectionPartImage *rviewOSectionPartImage::createViewer(const char *collname, const double *loid) +{ + char queryBuffer[STRINGSIZE]; + collection_desc *desc; + r_Minterval dom; + r_Dimension di; + + if (rmanClientApp::theApp()->getMinterval(dom, collname, loid) == 0) + { + cerr << "rviewOSectionImage::create(): unable to read spatial domain"; + return NULL; + } + + r_Minterval queryDom(dom.dimension()); + for (di=0; di<dom.dimension(); di++) + queryDom << r_Sinterval(dom[di].low(), dom[di].low()); + + ostrstream memstr(queryBuffer, STRINGSIZE); + memstr << "SELECT x" << queryDom << " FROM " << collname << " AS x"; + if (loid != NULL) + memstr << " WHERE OID(x) = " << *loid; + memstr << '\0'; + + //cout << "QUERY: " << queryBuffer << endl; + + if ((desc = rmanClientApp::theApp()->executeQuerySync(queryBuffer, NULL, FALSE)) != NULL) + { + if (desc->mddObjs != NULL) + { + unsigned int i; + r_GMarray *dummyMDD; + const r_GMarray *mdd; + mdd_frame dummyFrame; + + mdd = desc->mddObjs[0].mdd.ptr(); + // create a fake MDD with all the necessary meta data (spatial domain, base type, ...) + dummyMDD = new r_GMarray(); + dummyMDD->set_spatial_domain(dom); + dummyMDD->set_type_length(mdd->get_type_length()); + dummyMDD->set_type_structure(mdd->get_type_structure()); + dummyMDD->set_type_by_name(mdd->get_type_name()); + dummyMDD->initialize_oid(mdd->get_oid()); + //dummyMDD->set_type_schema(mdd->get_type_schema()); + + dummyFrame.mdd = r_Ref<r_GMarray>(dummyMDD); + dummyFrame.flags = 0; + // the collection is useless now + rviewDeleteCollection(desc); + rviewOSectionPartImage *viewer = new rviewOSectionPartImage(&dummyFrame, collname, dummyFrame.mdd->get_oid(), display_flag_standalone); + + // also open it automatically + if (viewer->openViewer() != 0) + { + delete viewer; + return NULL; + } + return viewer; + } + rviewDeleteCollection(desc); + } + return NULL; +} + + + +#define INIT_DUMMY_MDD(type, min, max) \ + csDummy = new r_Marray<type>(dummyDom, dummySL); \ + ((type*)(csDummy->get_array()))[0] = min; \ + ((type*)(csDummy->get_array()))[1] = max; + +rviewOSectionPartImage::rviewOSectionPartImage(mdd_frame *mf, const char *cname, const r_OId &oid, unsigned int flags) : + rviewOSectionImage(mf, flags), + objOId(oid), + collName(cname) +{ + sections = NULL; + // create a dummy MDD for use with the colourspace mapper. This contains 2 cells, the first + // is min, the second is max. It must be kept up to date as new data is loaded. + r_Minterval dummyDom(1); + dummyDom << r_Sinterval((r_Range)0, (r_Range)1); + r_Storage_Layout *dummySL = NULL; + switch(baseType) + { + case rbt_bool: + INIT_DUMMY_MDD(r_Boolean, 0, 1); + break; + case rbt_char: + INIT_DUMMY_MDD(r_Char, 0, 255); + break; + case rbt_uchar: + INIT_DUMMY_MDD(r_Octet, -128, 127); + break; + + // from here on the defaults are rather arbitrary... + case rbt_short: + INIT_DUMMY_MDD(r_Short, -4096, 4095); + break; + case rbt_ushort: + INIT_DUMMY_MDD(r_UShort, 0, 8191); + break; + case rbt_long: + INIT_DUMMY_MDD(r_Long, -65536, 65535); + break; + break; + case rbt_ulong: + INIT_DUMMY_MDD(r_ULong, 0, 131071); + break; + case rbt_float: + INIT_DUMMY_MDD(r_Float, -1000.0, 1000.0); + break; + case rbt_double: + INIT_DUMMY_MDD(r_Double, -1000.0, 1000.0); + break; + case rbt_rgb: + { + // basically no colourspace mapping is possible here, but I don't want a totally + // uninitialized marray around, it's asking for trouble... + RGBPixel *ptr; + csDummy = new r_Marray<RGBPixel>(dummyDom, dummySL); + ptr = (RGBPixel*)(csDummy->get_array()); + ptr[0].red = 0; ptr[0].green = 0; ptr[0].blue = 0; + ptr[1].red = 255; ptr[1].green = 255; ptr[1].blue = 255; + } + break; + default: + // this shouldn't happen... + break; + } + csDummy->set_type_by_name(mddObj->get_type_name()); + csDummy->set_type_structure(mddObj->get_type_structure()); + + unsigned int i; + + sections = new section_desc_t[numSections]; + memset(sections, 0, numSections*sizeof(section_desc_t)); + for (i=0; i<numSections; i++) + { + sections[i].mdd = NULL; + // init current slice projection to impossible value + sections[i].proj = interv[secmap[i].z].low() - 1; + } + + fireDragRelease = new rviewCheckBox(ctrlPanel); + fireDragRelease->SetValue(prefs->imgOrthoDragRel); + fireButton = new rviewButton(ctrlPanel); +} + + +rviewOSectionPartImage::~rviewOSectionPartImage(void) +{ + if (sections != NULL) + { + unsigned int i; + + for (i=0; i<numSections; i++) + { + if (sections[i].mdd != NULL) + { + delete sections[i].mdd; + sections[i].mdd = NULL; + } + } + delete [] sections; + } + + csDummy.destroy(); +} + + +void rviewOSectionPartImage::label(void) +{ + rviewOSectionImage::label(); + fireDragRelease->SetLabel(lman->lookup("textOrthoDragRelease")); + fireButton->SetLabel(lman->lookup("textOrthoFireButton")); +} + + +int rviewOSectionPartImage::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)fireButton) + { + // also read the thickness while we're at it + int newThick = atoi(thickText->GetValue()); + if ((newThick > 0) && (newThick != thickness)) + { + thickness = newThick; + refreshSlices(TRUE); + } + else + refreshSlices(FALSE); + + return 1; + } + } + return rviewOSectionImage::process(obj, evt); +} + + +void rviewOSectionPartImage::childMouseEvent(wxWindow *child, wxMouseEvent &mevt) +{ + int type = mevt.GetEventType(); + + //cout << "MOUSE EVENT " << type << endl; + if (((type == wxEVENT_TYPE_LEFT_UP) || (type == wxEVENT_TYPE_RIGHT_UP)) && fireDragRelease->GetValue()) + refreshSlices(FALSE); + // pass event on to bottom layer + rviewOSectionImage::childMouseEvent(child, mevt); +} + + +void rviewOSectionPartImage::OnSize(int w, int h) +{ + rviewOSectionImage::OnSize(w, h); + + int x, y; + + ctrlPanel->GetClientSize(&x, &y); + + x -= osection_rcwidth; + y = totalCtrlHeight - osection_ctrly + display_border + osection_theight; + fireDragRelease->SetSize(x, y, osection_chkwidth, osection_chkheight); + y += osection_chkheight; + fireButton->SetSize(x, y, osection_bwidth, osection_bheight); +} + + +void rviewOSectionPartImage::flushSlices(void) +{ + unsigned int i; + + for (i=0; i<numSections; i++) + { + sections[i].proj = interv[secmap[i].z].low() - 1; + } +} + + +int rviewOSectionPartImage::ensureSections(void) +{ + unsigned int i; + + for (i=0; i<numSections; i++) + { + if (sections[i].proj != intersection[secmap[i].z]) + { + if (sections[i].mdd != NULL) + { + delete sections[i].mdd; + sections[i].mdd = NULL; + } + + r_Minterval slicedom; + makeMinterval(i, slicedom); + + char buffer[STRINGSIZE]; + ostrstream memstr(buffer, STRINGSIZE); + memstr << "SELECT x" << slicedom << " FROM " << collName.ptr() << " AS x WHERE OID(x) = " << objOId.get_local_oid(); + memstr << '\0'; + + //cout << "QUERY: " << buffer << endl; + + collection_desc *desc; + + if ((desc = rmanClientApp::theApp()->executeQuerySync(buffer, NULL, FALSE)) != NULL) + { + if (desc->number != 0) + { + sections[i].mdd = desc->mddObjs[0].mdd.ptr(); + desc->mddObjs[0].mdd = NULL; + rviewDeleteCollection(desc); + sections[i].proj = intersection[secmap[i].z]; + } + } + + if (sections[i].mdd == NULL) + { + createDummySection(i, &slicedom); + } + } + } + + return 0; +} + + +#define MAKE_DUMMY_SECTION(type, init) \ + { \ + sections[num].mdd = new r_Marray<type>(*domp); \ + type *ptr = (type*)(sections[num].mdd->get_array()); \ + for (i=0; i<numCells; i++) ptr[i] = (init); \ + } + +int rviewOSectionPartImage::createDummySection(unsigned int num, const r_Minterval *dom) +{ + if (sections[num].mdd != NULL) + { + delete sections[num].mdd; + sections[num].mdd = NULL; + } + + sections[num].proj = interv[secmap[num].z].low() - 1; + + r_Minterval *usedom = NULL; + const r_Minterval *domp; + unsigned long numCells, i; + + if (dom == NULL) + { + usedom = new r_Minterval; + makeMinterval(num, *usedom); + domp = usedom; + } + else + domp = dom; + + numCells = domp->cell_count(); + + switch (baseType) + { + case rbt_bool: + MAKE_DUMMY_SECTION(r_Boolean, 1); + break; + case rbt_char: + MAKE_DUMMY_SECTION(r_Char, 80 + num*32); + break; + case rbt_uchar: + MAKE_DUMMY_SECTION(r_Octet, -48 + num*32); + break; + case rbt_short: + MAKE_DUMMY_SECTION(r_Short, (-48 + num*32)*256); + break; + case rbt_ushort: + MAKE_DUMMY_SECTION(r_UShort, (80 + num*32)*256); + break; + case rbt_long: + MAKE_DUMMY_SECTION(r_Long, (-48 + num*32)*0x1000000); + break; + case rbt_ulong: + MAKE_DUMMY_SECTION(r_ULong, (80 + num*32)*0x1000000); + break; + case rbt_float: + MAKE_DUMMY_SECTION(r_Float, 10.0); + break; + case rbt_double: + MAKE_DUMMY_SECTION(r_Double, 10.0); + break; + case rbt_rgb: + { + sections[num].mdd = new r_Marray<RGBPixel>(*domp); + RGBPixel *ptr = (RGBPixel*)(sections[num].mdd->get_array()); + r_Char val = 80 + 32*num; + for (i=0; i<numCells; i++) + { + ptr[i].red = val; ptr[i].green = val; ptr[i].blue = val; + } + } + break; + default: + break; + } + + if (usedom != NULL) + delete usedom; + + return 0; +} + + +bool rviewOSectionPartImage::sectionValid(unsigned int num) +{ + return (sections[num].mdd != NULL); +} + + +const r_Minterval &rviewOSectionPartImage::getSectionDomain(unsigned int num) +{ + return sections[num].mdd->spatial_domain(); +} + + +const r_Minterval &rviewOSectionPartImage::getSectionParent(unsigned int num) +{ + return sections[num].mdd->spatial_domain(); +} + + +char *rviewOSectionPartImage::getSectionArray(unsigned int num) +{ + return sections[num].mdd->get_array(); +} + + +long rviewOSectionPartImage::getSectionProjection(unsigned int num) +{ + return sections[num].proj; +} + + +r_Ref<r_GMarray> &rviewOSectionPartImage::getCsmapArray(void) +{ + return csDummy; +} + + + + + + +/* + * Full orthosection: the entire object resides in main memory; this integrates + * seamlessly with the other viewer types. + */ + +rviewOSectionFullImage::rviewOSectionFullImage(mdd_frame *mf, unsigned int flags) : + rviewOSectionImage(mf, flags) +{ + sections = new r_Minterval[numSections]; +}; + + +rviewOSectionFullImage::~rviewOSectionFullImage(void) +{ + closeViewer(); + delete [] sections; + sections=0; +} + + +int rviewOSectionFullImage::ensureSections(void) +{ + unsigned int i; + + for (i=0; i<numSections; i++) + makeMinterval(i, sections[i]); + + return 0; +} + + +int rviewOSectionFullImage::createDummySection(unsigned int num, const r_Minterval *dom) +{ + if (dom == NULL) + makeMinterval(num, sections[num]); + else + sections[num] = *dom; + + return 0; +} + + +void rviewOSectionFullImage::flushSlices(void) +{ +} + + +bool rviewOSectionFullImage::sectionValid(unsigned int num) +{ + return (sections[num].dimension() == dimMDD); +} + + +const r_Minterval &rviewOSectionFullImage::getSectionDomain(unsigned int num) +{ + return sections[num]; +} + + +const r_Minterval &rviewOSectionFullImage::getSectionParent(unsigned int num) +{ + return interv; +} + + +char *rviewOSectionFullImage::getSectionArray(unsigned int num) +{ + return mddObj->get_array(); +} + + +long rviewOSectionFullImage::getSectionProjection(unsigned int num) +{ + return intersection[secmap[num].z]; +} + + +r_Ref<r_GMarray> &rviewOSectionFullImage::getCsmapArray(void) +{ + return mddObj; +} diff --git a/applications/rview/rviewOSection.hh b/applications/rview/rviewOSection.hh new file mode 100644 index 0000000..962c3de --- /dev/null +++ b/applications/rview/rviewOSection.hh @@ -0,0 +1,230 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * A viewer for orthosections in 3D images (bias on medical images) + * + * COMMENTS: + * None + */ + +#ifndef _RVIEW_OSECTION_H_ +#define _RVIEW_OSECTION_H_ + +#include "rviewDModes.hh" + + +/* + * Abstract base class for orthosection views of 3D image volumes + * Client classes implement the data sources (everything in memory + * vs. loading slices on demand) + */ + +class rviewOSectionImage : public rviewRenderImage +{ + public: + + rviewOSectionImage(mdd_frame *mf, unsigned int flags=0); + virtual ~rviewOSectionImage(void); + + virtual void label(void); + virtual int process(wxObject &obj, wxEvent &evt); + virtual void OnSize(int w, int h); + virtual int openViewer(void); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + virtual int getViewerType(void) const; + virtual void childMouseEvent(wxWindow *child, wxMouseEvent &mev); + + // new virtual methods + virtual bool sectionValid(unsigned int num) = 0; + virtual const r_Minterval &getSectionDomain(unsigned int num) = 0; + virtual const r_Minterval &getSectionParent(unsigned int num) = 0; + virtual char *getSectionArray(unsigned int num) = 0; + virtual long getSectionProjection(unsigned int num) = 0; + virtual r_Ref<r_GMarray> &getCsmapArray(void) = 0; + + // internal structures + struct section_map_s; + struct section_part_s; + typedef struct section_map_s section_map_t; + typedef struct section_part_s section_part_t; + + // constants + // additional height of control panel + static const int osection_ctrly; + // width of righthand column (next to sliders) + static const int osection_rcwidth; + // height of slider bar + static const int osection_sheight; + // Checkbox dimensions + static const int osection_chkwidth; + static const int osection_chkheight; + // Text widget dimensions + static const int osection_twidth; + static const int osection_theight; + // Button dimensions + static const int osection_bwidth; + static const int osection_bheight; + + + protected: + + virtual char *initMode(void); + virtual char *setupEnvironment(int w, int h); + virtual bool doUpdate(int updateFlags); + virtual void fillBuffer(void); + + // view management + virtual int saveView(FILE *fp); + virtual int readView(const char *key, const char *value); + virtual void loadViewFinished(void); + + // create the currently relevant spatial domain for a slice + int makeMinterval(unsigned int num, r_Minterval &dom); + // partition sections into quadrants for rendering + int performPartition(void); + // update a slice + void updateSlice(unsigned int num, long value, bool useDummy=TRUE); + // refresh all slices that need to (or all, if force=TRUE) + void refreshSlices(bool force=FALSE); + + // load the correct slices from the database if necessary + virtual int ensureSections(void) = 0; + // create a dummy slice (empty) + virtual int createDummySection(unsigned int num, const r_Minterval *dom=NULL) = 0; + // flush out all slices + virtual void flushSlices(void) = 0; + + void setOId(const r_OId &oid); + + //rviewSlider **sliders; + rviewSpecialSlider **sliders; + rviewText **sltexts; + rviewCheckBox *boundingBox; + rviewText *thickText; + + // intersection point of the (3) sections + r_Point intersection; + // thickness of a section (1) + int thickness; + // mapping sections to dimensions + struct section_map_s *secmap; + // mapping partitions (section quadrants) to sections and 3D space + struct section_part_s *partition; + unsigned int numPartitions; + bool doBoundingBox; + // currently selected section + unsigned int currentSection; + // static members + static const unsigned int numSections; + static const char *sliderLabels[]; + + // view parameters + static const char *view_Thickness; + static const char *view_MidPoint; + static const char *view_UseBBox; +}; + + + + +/* + * Orthosection class where slices are loaded on demand from the database. + */ + +class rviewOSectionPartImage : public rviewOSectionImage +{ + public: + rviewOSectionPartImage(mdd_frame *mf, const char *cname, const r_OId &oid, unsigned int flags=0); + ~rviewOSectionPartImage(void); + + virtual void label(void); + virtual int process(wxObject &obj, wxEvent &evt); + virtual void OnSize(int w, int h); + virtual void childMouseEvent(wxWindow *child, wxMouseEvent &mev); + + virtual bool sectionValid(unsigned int num); + virtual const r_Minterval &getSectionDomain(unsigned int num); + virtual const r_Minterval &getSectionParent(unsigned int num); + virtual char *getSectionArray(unsigned int num); + virtual long getSectionProjection(unsigned int num); + virtual r_Ref<r_GMarray> &getCsmapArray(void); + + // create a new instance. + static rviewOSectionPartImage *createViewer(const char *collname, const double *loid=NULL); + + struct section_desc_s; + typedef struct section_desc_s section_desc_t; + + + protected: + virtual int ensureSections(void); + virtual int createDummySection(unsigned int num, const r_Minterval *dom=NULL); + virtual void flushSlices(void); + + rviewCheckBox *fireDragRelease; + rviewButton *fireButton; + + // the sections (slices through the cube; all technically 3D) + struct section_desc_s *sections; + r_OId objOId; + DynamicString collName; + // dummy MDD used for colourspace configuration + r_Ref<r_GMarray> csDummy; +}; + + + + + +/* + * Orthosection class where the entire object is in client memory + */ + +class rviewOSectionFullImage : public rviewOSectionImage +{ + public: + rviewOSectionFullImage(mdd_frame *mf, unsigned int flags = 0); + ~rviewOSectionFullImage(void); + + virtual bool sectionValid(unsigned int num); + virtual const r_Minterval &getSectionDomain(unsigned int num); + virtual const r_Minterval &getSectionParent(unsigned int num); + virtual char *getSectionArray(unsigned int num); + virtual long getSectionProjection(unsigned int num); + virtual r_Ref<r_GMarray> &getCsmapArray(void); + + + protected: + virtual int ensureSections(void); + virtual int createDummySection(unsigned int num, const r_Minterval *dom=NULL); + virtual void flushSlices(void); + + r_Minterval *sections; +}; + +#endif diff --git a/applications/rview/rviewPrefs.cpp b/applications/rview/rviewPrefs.cpp new file mode 100644 index 0000000..895800c --- /dev/null +++ b/applications/rview/rviewPrefs.cpp @@ -0,0 +1,1571 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * Management of rView's preferences. This includes an object encapsulating + * the preferences and providing IO of these preferences (rviewPrefs) and a + * frame class that allows displaying / editing the current preferences + * (rviewPrefsWindow). + * + * COMMENTS: + * none + */ + + + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <iostream.h> +#include <math.h> + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + + +#include "raslib/rmdebug.hh" +#include "compression/tilecompression.hh" + +#include "rviewPrefs.hh" +#include "labelManager.hh" + + + + +rviewPrefs *prefs; + + +enum prefsVarId { + PVar_Void, + PVar_ServerName, + PVar_ServerPort, + PVar_DBName, + PVar_UserName, + PVar_LastColl, + PVar_LastScColl, + PVar_LastOrColl, + PVar_LastDisp, + PVar_FilePath, + PVar_QueryPath, + PVar_QueryFont, + PVar_MaxDWidth, + PVar_MaxDHeight, + PVar_VffParams, + PVar_ImgDither, + PVar_DitherBest, + PVar_RGBSpace, + PVar_MovieMode, + PVar_ImgMode, + PVar_ImgBBox, + PVar_ImgZpro, + PVar_ImgClipZ, + PVar_ImgScale, + PVar_ImgPTL, + PVar_ImgPTH, + PVar_ImgWT, + PVar_ImgWQ, + PVar_ImgRGBBr, + PVar_ImgVoxType, + PVar_ImgLight, + PVar_ImgLightAn, + PVar_ImgLightSc, + PVar_ImgLightAm, + PVar_ImgLightGn, + PVar_ImgKernSz, + PVar_ImgKernTp, + PVar_ImgUseVCol, + PVar_ImgVoxCol, + PVar_ImgLightDs, + PVar_ImgLightDr, + PVar_ImgHgtGrd, + PVar_ImgHgtScl, + PVar_ImgOrtBBox, + PVar_ImgOrtDrag, + PVar_ImgOrtThick, + PVar_ChartMode, + PVar_ChartCosys, + PVar_ChartStep, + PVar_ChartMarkX, + PVar_ChartMarkY, + PVar_TableMode, + PVar_TableCosys, + PVar_TableStepX, + PVar_TableStepY, + PVar_ThumbPDim, + PVar_ThumbPStep, + PVar_ThumbWidth, + PVar_ThumbCols, + PVar_SoundFrq, + PVar_SoundLat, + PVar_SoundLoop, + PVar_CMpeakR, + PVar_CMpeakG, + PVar_CMpeakB, + PVar_CMsigmR, + PVar_CMsigmG, + PVar_CMsigmB, + PVar_CMtype, + PVar_ComTrFmt, + PVar_ComTrParm, + PVar_ComStFmt, + PVar_ComStParm, + PVar_NUMBER +}; + + + +const keyword_to_ident_c rviewPrefs::prefsVarDesc[] = { + {PVar_ServerName, "serverName"}, + {PVar_ServerPort, "serverPort"}, + {PVar_DBName, "databaseName"}, + {PVar_UserName, "userName"}, + {PVar_LastColl, "lastCollection"}, + {PVar_LastScColl, "lastScaledColl"}, + {PVar_LastOrColl, "lastOrthoColl"}, + {PVar_LastDisp, "lastDisplay"}, + {PVar_FilePath, "filePath"}, + {PVar_QueryPath, "queryPath"}, + {PVar_QueryFont, "queryFont"}, + {PVar_MaxDWidth, "maxDWidth"}, + {PVar_MaxDHeight, "maxDHeight"}, + {PVar_VffParams, "vffParams"}, + {PVar_ImgDither, "imgDither"}, + {PVar_DitherBest, "ditherBest"}, + {PVar_RGBSpace, "rgbSpace"}, + {PVar_MovieMode, "movieMode"}, + {PVar_ImgMode, "imageMode"}, + {PVar_ImgBBox, "imageBBox"}, + {PVar_ImgZpro, "imageZpro"}, + {PVar_ImgClipZ, "imageClipz"}, + {PVar_ImgScale, "imageScale"}, + {PVar_ImgPTL, "imagePixThreshLow"}, + {PVar_ImgPTH, "imagePixThreshHigh"}, + {PVar_ImgWT, "imageWgtThresh"}, + {PVar_ImgWQ, "imageWgtQuant"}, + {PVar_ImgRGBBr, "imageRgbBrightness"}, + {PVar_ImgVoxType, "voxelSetupForType"}, + {PVar_ImgLight, "imageLight"}, + {PVar_ImgLightAn, "imageLightAngle"}, + {PVar_ImgLightSc, "imageLightScintAng"}, + {PVar_ImgLightAm, "imageLightAmbient"}, + {PVar_ImgLightGn, "imageLightGain"}, + {PVar_ImgKernSz, "imageKernelSize"}, + {PVar_ImgKernTp, "imageKernelType"}, + {PVar_ImgUseVCol, "imageUseVCol"}, + {PVar_ImgVoxCol, "imageVoxColour"}, + {PVar_ImgLightDr, "imageLightDir"}, + {PVar_ImgLightDs, "imageLightDist"}, + {PVar_ImgHgtGrd, "imageHeightGrid"}, + {PVar_ImgHgtScl, "imageHeightScale"}, + {PVar_ImgOrtBBox, "imageOrthoBBox"}, + {PVar_ImgOrtDrag, "imageOrthoFireDragRel"}, + {PVar_ImgOrtThick, "imageOrthoThickness"}, + {PVar_ChartMode, "chartMode"}, + {PVar_ChartCosys, "chartCosys"}, + {PVar_ChartStep, "chartStep"}, + {PVar_ChartMarkX, "chartMarkx"}, + {PVar_ChartMarkY, "chartMarky"}, + {PVar_TableMode, "tableMode"}, + {PVar_TableCosys, "tableCosys"}, + {PVar_TableStepX, "tableStepx"}, + {PVar_TableStepY, "tableStepy"}, + {PVar_ThumbPDim, "thumbProjdim"}, + {PVar_ThumbPStep, "thumbProjstep"}, + {PVar_ThumbWidth, "thumbWidth"}, + {PVar_ThumbCols, "thumbCols"}, + {PVar_SoundFrq, "soundFrequency"}, + {PVar_SoundLat, "soundLatency"}, + {PVar_SoundLoop, "soundLoop"}, + {PVar_CMpeakR, "cspacePeakRed"}, + {PVar_CMpeakG, "cspacePeakGreen"}, + {PVar_CMpeakB, "cspacePeakBlue"}, + {PVar_CMsigmR, "cspaceSigmaRed"}, + {PVar_CMsigmG, "cspaceSigmaGreen"}, + {PVar_CMsigmB, "cspaceSigmaBlue"}, + {PVar_CMtype, "cspaceType"}, + {PVar_ComTrFmt, "transferFormat"}, + {PVar_ComTrParm, "transferParameters"}, + {PVar_ComStFmt, "storageFormat"}, + {PVar_ComStParm, "storageParameters"}, + {PVar_Void, NULL} +}; + + + + +/* + * rviewPrefs members + */ + +const unsigned long rviewPrefs::buffExtendGranularity = 64; + +rviewPrefs::rviewPrefs(void) +{ + setupVariables(); +} + + +// Load-constructor +rviewPrefs::rviewPrefs(const char *file) +{ + setupVariables(); + load(file); +} + + +// Copy-constructor +rviewPrefs::rviewPrefs(const rviewPrefs &srcPrefs) +{ + setupVariables(); + copyPrefs(srcPrefs, *this); +} + + +void rviewPrefs::copyPrefs(const rviewPrefs &src, rviewPrefs &dest) +{ + dest.serverName = src.serverName; + dest.serverPort = src.serverPort; + dest.databaseName = src.databaseName; + dest.userName = src.userName; + dest.lastColl = src.lastColl; + dest.lastScColl = src.lastScColl; + dest.lastOrthoColl = src.lastOrthoColl; + dest.filePath = src.filePath; + dest.queryPath = src.queryPath; + dest.queryFont = src.queryFont; + dest.lastDisplay = src.lastDisplay; + dest.maxDWidth = src.maxDWidth; + dest.maxDHeight = src.maxDHeight; + dest.vffParams = src.vffParams; + dest.imgDither = src.imgDither; + dest.ditherBest = src.ditherBest; + dest.rgbSpace = src.rgbSpace; + dest.movieMode = src.movieMode; + dest.imgMode = src.imgMode; + dest.imgBBox = src.imgBBox; + dest.imgZpro = src.imgZpro; + dest.imgClipz = src.imgClipz; + dest.imgScale = src.imgScale; + dest.imgPixThreshLow = src.imgPixThreshLow; + dest.imgPixThreshHigh = src.imgPixThreshHigh; + dest.imgWgtThresh = src.imgWgtThresh; + dest.imgWgtQuant = src.imgWgtQuant; + dest.imgRgbBrightness = src.imgRgbBrightness; + dest.imgVoxForType = src.imgVoxForType; + dest.imgLight = src.imgLight; + dest.imgLightAngle = src.imgLightAngle; + dest.imgLightScintAngle = src.imgLightScintAngle; + dest.imgLightAmbient = src.imgLightAmbient; + dest.imgLightGain = src.imgLightGain; + dest.imgKernSize = src.imgKernSize; + dest.imgKernType = src.imgKernType; + dest.imgUseVCol = src.imgUseVCol; + dest.imgVoxColour = src.imgVoxColour; + dest.imgLightDir = src.imgLightDir; + dest.imgLightDist = src.imgLightDist; + dest.imgHeightGrid = src.imgHeightGrid; + dest.imgHeightScale = src.imgHeightScale; + dest.imgOrthoBBox = src.imgOrthoBBox; + dest.imgOrthoDragRel = src.imgOrthoDragRel; + dest.imgOrthoThick = src.imgOrthoThick; + dest.chartMode = src.chartMode; + dest.chartCosys = src.chartCosys; + dest.chartStep = src.chartStep; + dest.chartMarkx = src.chartMarkx; + dest.chartMarky = src.chartMarky; + dest.tableMode = src.tableMode; + dest.tableCosys = src.tableCosys; + dest.tableStepx = src.tableStepx; + dest.tableStepy = src.tableStepy; + dest.thumbProjdim = src.thumbProjdim; + dest.thumbProjstep = src.thumbProjstep; + dest.thumbWidth = src.thumbWidth; + dest.thumbCols = src.thumbCols; + dest.soundFreq = src.soundFreq; + dest.soundLatency = src.soundLatency; + dest.soundLoop = src.soundLoop; + dest.transferFmt = src.transferFmt; + dest.transferParm = src.transferParm; + dest.storageFmt = src.storageFmt; + dest.storageParm = src.storageParm; + memcpy(&(dest.csp), &(src.csp), sizeof(colourspace_params)); +} + + +rviewPrefs::~rviewPrefs(void) +{ + if (pwin != NULL) {pwin->unlinkParent(); pwin->Close(TRUE);} + if (inbuff != NULL) delete [] inbuff; +} + + + +char *rviewPrefs::getValue(char *b) +{ + char *d; + + // This shouldn't happen. + while (*b != '=') {if (*b == '\0') return b; b++;} + b++; + while (isspace((unsigned int)(*b))) b++; + d = b; + // Allow only printable characters + while (isprint((unsigned int)(*d))) d++; + // Make sure it's terminated by 0. + *d = '\0'; + return b; +} + + +// read a line of arbitrary length from a file +char *rviewPrefs::readLine(FILE *fp) +{ + if (inbuff == NULL) + { + buffSize = buffExtendGranularity; + inbuff = new char[buffSize]; + } + inbuff[0] = '\0'; + inbuff[buffSize-2] = '\0'; + fgets(inbuff, buffSize, fp); + unsigned long currentOff = 0; + char last = inbuff[buffSize-2]; + while ((last != '\0') && (last != '\n') && (last != '\r')) + { + unsigned long newsize = buffSize + buffExtendGranularity; + char *newbuff; + + if ((newbuff = new char[newsize]) == NULL) + return NULL; + memcpy(newbuff, inbuff, buffSize-1); + currentOff = buffSize-1; + delete [] inbuff; + inbuff = newbuff; buffSize = newsize; + inbuff[currentOff] = '\0'; + fgets(inbuff + currentOff, buffSize - currentOff, fp); + last = inbuff[buffSize-2]; + } + return inbuff; +} + + +int rviewPrefs::load(const char *file) +{ + FILE *fp; + char *b, *val; + int number; + + if ((fp = fopen(file, "r")) == NULL) + { + prefsModified = TRUE; + return 0; + } + + while (!feof(fp)) + { + b = readLine(fp); + while ((*b == ' ') || (*b == '\t')) b++; + if ((*b != '\n') && (*b != '#') && (*b != 0)) + { + number = 0; + while (prefsVarDesc[number].keyword != NULL) + { + int len; + + len = strlen(prefsVarDesc[number].keyword); + if ((strncmp(b, prefsVarDesc[number].keyword, len) == 0) && (!isalnum(b[len]))) break; + number++; + } + if (prefsVarDesc[number].keyword == NULL) + { + cerr << "Bad line in config file: " << b << endl; + } + else + { + val = getValue(b); + switch (prefsVarDesc[number].ident) + { + case PVar_ServerName: serverName = val; break; + case PVar_ServerPort: serverPort = atoi(val); break; + case PVar_DBName: databaseName = val; break; + case PVar_UserName: userName = val; break; + case PVar_LastColl: lastColl = val; break; + case PVar_LastScColl: lastScColl = val; break; + case PVar_LastOrColl: lastOrthoColl = val; break; + case PVar_LastDisp: lastDisplay = atoi(val); break; + case PVar_FilePath: filePath = val; break; + case PVar_QueryPath: queryPath = val; break; + case PVar_QueryFont: queryFont = val; break; + case PVar_MaxDWidth: maxDWidth = atoi(val); break; + case PVar_MaxDHeight: maxDHeight = atoi(val); break; + case PVar_VffParams: vffParams = val; break; + case PVar_ImgDither: imgDither = atoi(val); break; + case PVar_DitherBest: ditherBest = atoi(val); break; + case PVar_RGBSpace: rgbSpace = atoi(val); break; + case PVar_MovieMode: movieMode = atoi(val); break; + case PVar_ImgMode: imgMode = (rviewImageMode)atoi(val); break; + case PVar_ImgBBox: imgBBox = atoi(val); break; + case PVar_ImgZpro: imgZpro = atoi(val); break; + case PVar_ImgClipZ: imgClipz = atoi(val); break; + case PVar_ImgScale: imgScale = atof(val); break; + case PVar_ImgPTL: imgPixThreshLow = atof(val); break; + case PVar_ImgPTH: imgPixThreshHigh = atof(val); break; + case PVar_ImgWT: imgWgtThresh = atof(val); break; + case PVar_ImgWQ: imgWgtQuant = atoi(val); break; + case PVar_ImgVoxType: imgVoxForType = atoi(val); break; + case PVar_ImgRGBBr: imgRgbBrightness = atoi(val); break; + case PVar_ImgLight: imgLight = atoi(val); break; + case PVar_ImgLightAn: imgLightAngle = atof(val); break; + case PVar_ImgLightSc: imgLightScintAngle = atof(val); break; + case PVar_ImgLightAm: imgLightAmbient = atof(val); break; + case PVar_ImgLightGn: imgLightGain = atof(val); break; + case PVar_ImgKernSz: imgKernSize = atoi(val); break; + case PVar_ImgKernTp: imgKernType = atoi(val); break; + case PVar_ImgUseVCol: imgUseVCol = atoi(val); break; + case PVar_ImgVoxCol: imgVoxColour = atof(val); break; + case PVar_ImgLightDr: imgLightDir = val; break; + case PVar_ImgLightDs: imgLightDist = atoi(val); break; + case PVar_ImgHgtGrd: imgHeightGrid = atoi(val); break; + case PVar_ImgHgtScl: imgHeightScale = atof(val); break; + case PVar_ImgOrtBBox: imgOrthoBBox = atoi(val); break; + case PVar_ImgOrtDrag: imgOrthoDragRel = atoi(val); break; + case PVar_ImgOrtThick: imgOrthoThick = atoi(val); break; + case PVar_ChartMode: chartMode = (rviewChartMode)atoi(val); break; + case PVar_ChartCosys: chartCosys = atoi(val); break; + case PVar_ChartStep: chartStep = atoi(val); break; + case PVar_ChartMarkX: chartMarkx = atoi(val); break; + case PVar_ChartMarkY: chartMarky = atof(val); break; + case PVar_TableMode: tableMode = atoi(val); break; + case PVar_TableCosys: tableCosys = atoi(val); break; + case PVar_TableStepX: tableStepx = atoi(val); break; + case PVar_TableStepY: tableStepy = atoi(val); break; + case PVar_ThumbPDim: thumbProjdim = atoi(val); break; + case PVar_ThumbPStep: thumbProjstep = atoi(val); break; + case PVar_ThumbWidth: thumbWidth = atoi(val); break; + case PVar_ThumbCols: thumbCols = atoi(val); break; + case PVar_SoundFrq: soundFreq = atoi(val); break; + case PVar_SoundLat: soundLatency = atoi(val); break; + case PVar_SoundLoop: soundLoop = atoi(val); break; + case PVar_CMpeakR: csp.peak_red = atof(val); break; + case PVar_CMpeakG: csp.peak_green = atof(val); break; + case PVar_CMpeakB: csp.peak_blue = atof(val); break; + case PVar_CMsigmR: csp.sigm_red = atof(val); break; + case PVar_CMsigmG: csp.sigm_green = atof(val); break; + case PVar_CMsigmB: csp.sigm_blue = atof(val); break; + case PVar_CMtype: csp.type = (cspaceType)atoi(val); break; + case PVar_ComTrFmt: transferFmt = atoi(val); break; + case PVar_ComTrParm: fromExternal(val, transferParm); break; + case PVar_ComStFmt: storageFmt = atoi(val); break; + case PVar_ComStParm: fromExternal(val, storageParm); break; + default: + cout << "Bad prefs ID " << prefsVarDesc[number].ident << endl; + break; + } + } + } + } + fclose(fp); + + return 1; +} + + +int rviewPrefs::save(const char *file) +{ + char backname[STRINGSIZE]; + FILE *fp; + int i; + + if (!prefsModified) return 1; + + // Make a backup copy of the old file + sprintf(backname, "%s~", file); + remove(backname); rename(file, backname); + + if ((fp = fopen(file, "w")) == NULL) + return 0; + + fprintf(fp, "# RasDaView preferences\n\n"); + + for (i=0; prefsVarDesc[i].keyword != NULL; i++) + { + const char *name = prefsVarDesc[i].keyword; + + fprintf(fp, "%s\t=\t", name); + switch (prefsVarDesc[i].ident) + { + case PVar_ServerName: fprintf(fp, "%s", serverName.ptr()); break; + case PVar_ServerPort: fprintf(fp, "%d", serverPort); break; + case PVar_DBName: fprintf(fp, "%s", databaseName.ptr()); break; + case PVar_UserName: fprintf(fp, "%s", userName.ptr()); break; + case PVar_LastColl: fprintf(fp, "%s", lastColl.ptr()); break; + case PVar_LastScColl: fprintf(fp, "%s", lastScColl.ptr()); break; + case PVar_LastOrColl: fprintf(fp, "%s", lastOrthoColl.ptr()); break; + case PVar_LastDisp: fprintf(fp, "%d", lastDisplay); break; + case PVar_FilePath: fprintf(fp, "%s", filePath.ptr()); break; + case PVar_QueryPath: fprintf(fp, "%s", queryPath.ptr()); break; + case PVar_QueryFont: fprintf(fp, "%s", queryFont.ptr()); break; + case PVar_MaxDWidth: fprintf(fp, "%d", maxDWidth); break; + case PVar_MaxDHeight: fprintf(fp, "%d", maxDHeight); break; + case PVar_VffParams: fprintf(fp, "%s", vffParams.ptr()); break; + case PVar_ImgDither: fprintf(fp, "%d", imgDither); break; + case PVar_DitherBest: fprintf(fp, "%d", ditherBest); break; + case PVar_RGBSpace: fprintf(fp, "%d", rgbSpace); break; + case PVar_MovieMode: fprintf(fp, "%d", movieMode); break; + case PVar_ImgMode: fprintf(fp, "%d", imgMode); break; + case PVar_ImgBBox: fprintf(fp, "%d", imgBBox); break; + case PVar_ImgZpro: fprintf(fp, "%ld", imgZpro); break; + case PVar_ImgClipZ: fprintf(fp, "%ld", imgClipz); break; + case PVar_ImgScale: fprintf(fp, "%f", imgScale); break; + case PVar_ImgPTL: fprintf(fp, "%g", imgPixThreshLow); break; + case PVar_ImgPTH: fprintf(fp, "%g", imgPixThreshHigh); break; + case PVar_ImgWT: fprintf(fp, "%g", imgWgtThresh); break; + case PVar_ImgWQ: fprintf(fp, "%ld", imgWgtQuant); break; + case PVar_ImgRGBBr: fprintf(fp, "%d", imgRgbBrightness); break; + case PVar_ImgVoxType: fprintf(fp, "%d", imgVoxForType); break; + case PVar_ImgLight: fprintf(fp, "%d", imgLight); break; + case PVar_ImgLightAn: fprintf(fp, "%f", imgLightAngle); break; + case PVar_ImgLightSc: fprintf(fp, "%f", imgLightScintAngle); break; + case PVar_ImgLightAm: fprintf(fp, "%f", imgLightAmbient); break; + case PVar_ImgLightGn: fprintf(fp, "%f", imgLightGain); break; + case PVar_ImgKernSz: fprintf(fp, "%d", imgKernSize); break; + case PVar_ImgKernTp: fprintf(fp, "%d", imgKernType); break; + case PVar_ImgUseVCol: fprintf(fp, "%d", imgUseVCol); break; + case PVar_ImgVoxCol: fprintf(fp, "%f", imgVoxColour); break; + case PVar_ImgLightDr: fprintf(fp, "%s", imgLightDir.ptr()); break; + case PVar_ImgLightDs: fprintf(fp, "%d", imgLightDist); break; + case PVar_ImgHgtGrd: fprintf(fp, "%d", imgHeightGrid); break; + case PVar_ImgHgtScl: fprintf(fp, "%f", imgHeightScale); break; + case PVar_ImgOrtBBox: fprintf(fp, "%d", imgOrthoBBox); break; + case PVar_ImgOrtDrag: fprintf(fp, "%d", imgOrthoDragRel); break; + case PVar_ImgOrtThick: fprintf(fp, "%d", imgOrthoThick); break; + case PVar_ChartMode: fprintf(fp, "%d", chartMode); break; + case PVar_ChartCosys: fprintf(fp, "%d", chartCosys); break; + case PVar_ChartStep: fprintf(fp, "%d", chartStep); break; + case PVar_ChartMarkX: fprintf(fp, "%d", chartMarkx); break; + case PVar_ChartMarkY: fprintf(fp, "%f", chartMarky); break; + case PVar_TableMode: fprintf(fp, "%d", tableMode); break; + case PVar_TableCosys: fprintf(fp, "%d", tableCosys); break; + case PVar_TableStepX: fprintf(fp, "%d", tableStepx); break; + case PVar_TableStepY: fprintf(fp, "%d", tableStepy); break; + case PVar_ThumbPDim: fprintf(fp, "%d", thumbProjdim); break; + case PVar_ThumbPStep: fprintf(fp, "%d", thumbProjstep); break; + case PVar_ThumbWidth: fprintf(fp, "%d", thumbWidth); break; + case PVar_ThumbCols: fprintf(fp, "%d", thumbCols); break; + case PVar_SoundFrq: fprintf(fp, "%d", soundFreq); break; + case PVar_SoundLat: fprintf(fp, "%d", soundLatency); break; + case PVar_SoundLoop: fprintf(fp, "%d", soundLoop); break; + case PVar_CMpeakR: fprintf(fp, "%f", csp.peak_red); break; + case PVar_CMpeakG: fprintf(fp, "%f", csp.peak_green); break; + case PVar_CMpeakB: fprintf(fp, "%f", csp.peak_blue); break; + case PVar_CMsigmR: fprintf(fp, "%f", csp.sigm_red); break; + case PVar_CMsigmG: fprintf(fp, "%f", csp.sigm_green); break; + case PVar_CMsigmB: fprintf(fp, "%f", csp.sigm_blue); break; + case PVar_CMtype: fprintf(fp, "%d", csp.type); break; + case PVar_ComTrFmt: fprintf(fp, "%d", transferFmt); break; + case PVar_ComTrParm: + { + char *ext = toExternal(transferParm); + fwrite(ext, 1, strlen(ext), fp); + delete [] ext; + } + break; + case PVar_ComStFmt: fprintf(fp, "%d", storageFmt); break; + case PVar_ComStParm: + { + char *ext = toExternal(storageParm); + fwrite(ext, 1, strlen(ext), fp); + delete [] ext; + } + break; + default: break; + } + fprintf(fp, "\n"); + } + fclose(fp); + + prefsModified = FALSE; + + return 1; +} + + +void rviewPrefs::setupVariables(void) +{ + serverPort = 7001; + lastDisplay = 0; + maxDWidth = 1024; maxDHeight = 768; + imgDither = FALSE; ditherBest = FALSE; rgbSpace = 0; movieMode = 0; + imgMode = rim_surf; chartMode = rcm_bar; tableMode = 10; + imgBBox = TRUE; + imgZpro = 256; imgClipz = 128; + imgScale = 1.0; + imgPixThreshLow = 4.0; imgPixThreshHigh = 1e6; + imgWgtThresh = 64.0; imgWgtQuant = 4; + imgRgbBrightness = TRUE; imgVoxForType = TRUE; + imgLight = FALSE; imgLightAmbient = 0.5; imgLightGain = 1.0; + imgLightAngle = 90.0; imgLightScintAngle = 90.0; + imgKernSize = 2; imgKernType = 2; + imgUseVCol = FALSE; imgVoxColour = 255.0; + imgHeightGrid = 8; imgHeightScale = 1.0; + imgLightDir = "ru"; imgLightDist = 512; + imgOrthoBBox = TRUE; imgOrthoDragRel = TRUE; + imgOrthoThick = 1; + chartCosys = TRUE; + chartStep = 8; chartMarkx = 20; chartMarky = 20.0; + tableCosys = TRUE; + tableStepx = -1; tableStepy = -1; + thumbProjdim = 2; thumbProjstep = 1; thumbWidth = 100; thumbCols = 4; + soundFreq = 11025; soundLatency = 200; soundLoop = 0; + csp.peak_red = 1.0; csp.peak_green = 2.0/3; csp.peak_blue = 1.0/3; + csp.sigm_red = 1.0 / (6 * sqrt( log(2.0) / log( exp(1.0) ) ) ); + csp.sigm_green = csp.sigm_red; csp.sigm_blue = csp.sigm_red; + csp.type = cst_gauss; + transferFmt = 0; storageFmt = 0; + + pwin = NULL; + prefsModified = FALSE; + inbuff = NULL; buffSize = 0; +} + + +int rviewPrefs::edit(void) +{ + // Only open one preferences editor! + if (pwin == NULL) + { + // The prefs window will work on a copy of these prefs. + pwin = new rviewPrefsWindow(this); + } + return 0; +} + + +void rviewPrefs::editorClosed(void) +{ + pwin = NULL; +} + +void rviewPrefs::updatePrefs(rviewPrefs *newPrefs) +{ + copyPrefs(*newPrefs, *this); + prefsModified = TRUE; +} + +void rviewPrefs::closeEditor(rviewPrefs *newPrefs) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewPrefs", "closeEditor( " << newPrefs << " )"); + + // Did the user specify OK or Cancel? OK ==> set newPrefs + if (newPrefs != NULL) + { + updatePrefs(newPrefs); + delete newPrefs; + } + // Delete the editing window (implicitly closes it). + pwin->Close(TRUE); + pwin = NULL; +} + + +void rviewPrefs::markModified(void) +{ + prefsModified = TRUE; +} + + +r_Data_Format rviewPrefs::getTransferFormat(void) const +{ + r_Data_Format fmt; + + r_Tile_Compression::get_format_info((unsigned int)transferFmt, fmt); + return fmt; +} + + +r_Data_Format rviewPrefs::getStorageFormat( void ) const +{ + r_Data_Format fmt; + + r_Tile_Compression::get_format_info((unsigned int)storageFmt, fmt); + return fmt; +} + + +char *rviewPrefs::toExternal(const DynamicString &str) +{ + const char *d = str.ptr(); + unsigned int numspecial = 0; + char *ext, *b; + + while (*d != '\0') + { + if ((*d == '\n') || (*d == '\t') || (*d == '\\')) numspecial++; + d++; + } + ext = new char[strlen(str.ptr()) + numspecial + 1]; + d = str.ptr(); b = ext; + while (*d != '\0') + { + switch (*d) + { + case '\n': + *b++ = '\\'; *b++ = 'n'; break; + case '\t': + *b++ = '\\'; *b++ = 't'; break; + case '\\': + *b++ = '\\'; *b++ = '\\'; break; + default: + *b++ = *d; + } + d++; + } + *b = '\0'; + + return ext; +} + + +void rviewPrefs::fromExternal(const char *ext, DynamicString &str) +{ + const char *d; + char *intern, *b; + + intern = new char[strlen(ext)+1]; + d = ext; b = intern; + while (*d != '\0') + { + if (*d == '\\') + { + d++; + switch(*d) + { + case 'n': + *b++ = '\n'; break; + case 't': + *b++ = '\t'; break; + case '\\': + *b++ = '\\'; break; + default: + cerr << "Warning: parse error in long string " << str << endl; + } + } + else + *b++ = *d; + + d++; + } + *b = '\0'; + str = intern; + delete [] intern; +} + + + + + +/* + * rviewPrefsWindow member functions + */ + +const char *rviewPrefsWindow::soundLatencyChoices[] = { + "100ms", + "200ms", + "300ms", + "400ms", + "500ms" +}; + +const char *rviewPrefsWindow::soundFrequencyChoices[] = { + "8000Hz", + "11025Hz", + "22050Hz", + "44100Hz", + "48000Hz" +}; + +const int rviewPrefsWindow::prefs_width = 450; +const int rviewPrefsWindow::prefs_height = 300; +const int rviewPrefsWindow::prefs_border = 8; +const int rviewPrefsWindow::prefs_bottom = 40; +const int rviewPrefsWindow::prefs_bwidth = 80; +const int rviewPrefsWindow::prefs_bheight = 30; +const int rviewPrefsWindow::prefs_theight = 60; +const int rviewPrefsWindow::prefs_chkheight = 25; +const int rviewPrefsWindow::prefs_eheight = 50; +const int rviewPrefsWindow::prefs_scrwidth = 16; +const int rviewPrefsWindow::prefs_twheight = 80; +const int rviewPrefsWindow::prefs_mheight = 30; +const int rviewPrefsWindow::prefs_grpmsc_height = (11*rviewPrefsWindow::prefs_theight)/2; +const int rviewPrefsWindow::prefs_grpimg_height = (65*rviewPrefsWindow::prefs_theight)/4; +const int rviewPrefsWindow::prefs_grpren_height = (15*rviewPrefsWindow::prefs_theight)/2; +const int rviewPrefsWindow::prefs_grphgt_height = (3*rviewPrefsWindow::prefs_theight)/2; +const int rviewPrefsWindow::prefs_grport_height = (3*rviewPrefsWindow::prefs_theight)/2; +const int rviewPrefsWindow::prefs_grpthb_height = (5*rviewPrefsWindow::prefs_theight)/2; +const int rviewPrefsWindow::prefs_grpcht_height = (5*rviewPrefsWindow::prefs_theight)/2; +const int rviewPrefsWindow::prefs_grptab_height = (5*rviewPrefsWindow::prefs_theight)/2; +const int rviewPrefsWindow::prefs_grpsnd_height = (3*rviewPrefsWindow::prefs_theight)/2; +const int rviewPrefsWindow::prefs_grpcom_height = (7*rviewPrefsWindow::prefs_theight)/2; +const int rviewPrefsWindow::prefs_pheight = rviewPrefsWindow::prefs_grpmsc_height + + rviewPrefsWindow::prefs_grpimg_height + + rviewPrefsWindow::prefs_grpcht_height + + rviewPrefsWindow::prefs_grptab_height + + rviewPrefsWindow::prefs_grpsnd_height + + rviewPrefsWindow::prefs_grpcom_height + + 6*rviewPrefsWindow::prefs_border; + + +rviewPrefsWindow::rviewPrefsWindow(void): rviewFrame(NULL, "", 0, 0, prefs_width, prefs_height) +{ + setupVariables(); +} + + +rviewPrefsWindow::rviewPrefsWindow(rviewPrefs *Prefs): rviewFrame(NULL, "", 0, 0, prefs_width, prefs_height) +{ + setupVariables(); + setPrefs(Prefs); +} + + +rviewPrefsWindow::~rviewPrefsWindow(void) +{ + if (csmap != NULL) delete csmap; + if (editPrefs != NULL) delete editPrefs; + + if (myParent != NULL) myParent->editorClosed(); +} + + +const char *rviewPrefsWindow::getFrameName(void) const +{ + return "rviewPrefsWindow"; +} + +rviewFrameType rviewPrefsWindow::getFrameType(void) const +{ + return rviewFrameTypePrefs; +} + + +void rviewPrefsWindow::unlinkParent(void) +{ + myParent = NULL; +} + + +rviewChoice *rviewPrefsWindow::buildFormatMenu(wxPanel *parent, int fmtNum, const char *label) +{ + rviewChoice *menu; + r_Data_Format fmt; + unsigned int i, numFormats; + char **formatNames; + + for (numFormats=0; r_Tile_Compression::get_format_info(numFormats, fmt) != NULL; numFormats++) ; + formatNames = new char*[numFormats]; + for (i=0; i<(int)numFormats; i++) + { + formatNames[i] = (char*)r_Tile_Compression::get_format_info((unsigned int)i, fmt); + } + menu = new rviewChoice(parent, numFormats, formatNames, lman->lookup(label)); + delete [] formatNames; + menu->SetSelection(fmtNum); + + return menu; +} + +void rviewPrefsWindow::setPrefs(rviewPrefs *Prefs) +{ + int x, y, i; + char *choices[4]; + + myParent = Prefs; + + editPrefs = new rviewPrefs(*Prefs); + + GetClientSize(&x, &y); + + // Create control panel first; it will be superimposed by the scrolling panel + butPanel = new wxPanel((wxWindow*)this, 0, 300, x, prefs_bottom); + butOK = new rviewButton(butPanel); + butCancel = new rviewButton(butPanel); + butApply = new rviewButton(butPanel); + + i = y - prefs_bottom; if (i > prefs_pheight) i = prefs_pheight; + scroll = new wxScrollBar(butPanel, (wxFunction)rviewEventHandler, x - prefs_scrwidth, 0, prefs_scrwidth, i, wxVERTICAL); + scroll->SetValue(0); + // Mis-documentation: you have to set the object length before the view length, + // not the other way around. + scroll->SetObjectLength(prefs_pheight); + scroll->SetViewLength(i); + scroll->SetPageLength((3*i)/4); + //cout << "view length = " << i << ", object length = " << prefs_pheight << ", page length = " << (3*i)/4 << endl; + + panel = new wxPanel((wxWindow*)this, 0, 0, x, y - prefs_bottom); + + // Put labels above the widgets rather than to the left. + panel->SetLabelPosition(wxVERTICAL); + + // Just create everything here. Take care of positioning in OnSize(...) + // Misc + miscGroup = new wxGroupBox(panel, "", 0, 0, 100, 100); + filePath = new rviewText(panel, editPrefs->filePath); + queryPath = new rviewText(panel, editPrefs->queryPath); + queryFont = new rviewText(panel, editPrefs->queryFont); + + maxDWidth = new rviewText(panel, editPrefs->maxDWidth); + maxDHeight = new rviewText(panel, editPrefs->maxDHeight); + + vffParams = new rviewText(panel, editPrefs->vffParams); + + // Image group + imgGroup = new wxGroupBox(panel, "", 0, 0, 100, 100); + imgDither = new rviewCheckBox(panel); + imgDither->SetValue(editPrefs->imgDither); + ditherBest = new rviewCheckBox(panel); + ditherBest->SetValue(editPrefs->ditherBest); + choices[0] = lman->lookup("textOff"); + choices[1] = lman->lookup("prefsCspaceAct"); + choices[2] = lman->lookup("prefsCspaceFull"); + choices[3] = lman->lookup("prefsCspaceProj"); + rgbSpace = new rviewChoice(panel, 4, choices, lman->lookup("prefsCspace")); + rgbSpace->SetSelection(editPrefs->rgbSpace); + cstrap = new rviewButton(panel); + choices[0] = lman->lookup("prefsMovieOnce"); + choices[1] = lman->lookup("prefsMovieStart"); + choices[2] = lman->lookup("prefsMovieSwitch"); + movieMode = new rviewChoice(panel, 3, choices, lman->lookup("prefsMovieMode")); + movieMode->SetSelection(editPrefs->movieMode); + renderGroup = new wxGroupBox(panel, "", 0, 0, 100, 100); + choices[0] = lman->lookup("menImgModeSurf"); + choices[1] = lman->lookup("menImgModeVoxel"); + imgMode = new rviewChoice(panel, 2, choices, lman->lookup("textImageMode")); + imgScale = new rviewText(panel, editPrefs->imgScale, FALSE); + // Image renderer subgroup + imgZpro = new rviewText(panel, (long)(editPrefs->imgZpro)); + imgClipz = new rviewText(panel, (long)(editPrefs->imgClipz)); + imgBBox = new rviewCheckBox(panel); + imgBBox->SetValue(editPrefs->imgBBox); + imgPixThreshLow = new rviewText(panel, editPrefs->imgPixThreshLow, TRUE); + imgPixThreshHigh = new rviewText(panel, editPrefs->imgPixThreshHigh, TRUE); + imgRgbBrightness = new rviewCheckBox(panel); + imgRgbBrightness->SetValue(editPrefs->imgRgbBrightness); + imgVoxForType = new rviewCheckBox(panel); + imgVoxForType->SetValue(editPrefs->imgVoxForType); + imgWgtThresh = new rviewText(panel, editPrefs->imgWgtThresh, TRUE); + imgWgtQuant = new rviewText(panel, (long)(editPrefs->imgWgtQuant)); + imgLight = new rviewCheckBox(panel); + imgLight->SetValue(editPrefs->imgLight); + choices[0] = "0"; + choices[1] = "1"; + choices[2] = "2"; + choices[3] = "3"; + imgKernSize = new rviewChoice(panel, 4, choices, lman->lookup("prefsKernSize")); + imgKernSize->SetSelection(editPrefs->imgKernSize); + choices[0] = lman->lookup("kernelTypeAvg"); + choices[1] = lman->lookup("kernelTypeLin"); + choices[2] = lman->lookup("kernelTypeGauss"); + imgKernType = new rviewChoice(panel, 3, choices, lman->lookup("prefsKernType")); + imgKernType->SetSelection(editPrefs->imgKernType); + imgLightAngle = new rviewText(panel, editPrefs->imgLightAngle); + imgLightAmbient = new rviewText(panel, editPrefs->imgLightAmbient); + imgLightGain = new rviewText(panel, editPrefs->imgLightGain); + imgLightScintAngle = new rviewText(panel, editPrefs->imgLightScintAngle); + imgLightDir = new rviewText(panel, editPrefs->imgLightDir); + imgLightDist = new rviewText(panel, editPrefs->imgLightDist); + imgUseVCol = new rviewCheckBox(panel); + imgUseVCol->SetValue(editPrefs->imgUseVCol); + imgVoxColour = new rviewText(panel, editPrefs->imgVoxColour); + // Image heightfield subgroup + heightGroup = new wxWidowBase(panel, "", 0, 0, 100, 100); + imgHeightGrid = new rviewText(panel, editPrefs->imgHeightGrid); + imgHeightScale = new rviewText(panel, editPrefs->imgHeightScale); + // Image orthosection subgroup + orthoGroup = new wxGroupBox(panel, "", 0, 0, 100, 100); + imgOrthoBBox = new rviewCheckBox(panel); + imgOrthoBBox->SetValue(editPrefs->imgOrthoBBox); + imgOrthoDragRel = new rviewCheckBox(panel); + imgOrthoDragRel->SetValue(editPrefs->imgOrthoDragRel); + imgOrthoThick = new rviewText(panel, editPrefs->imgOrthoThick); + // Image thumbnails subgroup + thumbGroup = new wxGroupBox(panel, "", 0, 0, 100, 100); + thumbProjdim = new rviewText(panel, editPrefs->thumbProjdim); + thumbProjstep = new rviewText(panel, editPrefs->thumbProjstep); + thumbWidth = new rviewText(panel, editPrefs->thumbWidth); + thumbCols = new rviewText(panel, editPrefs->thumbCols); + + // Chart group + chartGroup = new wxGroupBox(panel, "", 0, 0, 100, 100); + choices[0] = lman->lookup("menChartModeBar"); + choices[1] = lman->lookup("menChartModeLine"); + choices[2] = lman->lookup("menChartModeSpline"); + chartMode = new rviewChoice(panel, 3, choices, lman->lookup("textChartMode")); + chartCosys = new rviewCheckBox(panel); + chartCosys->SetValue(editPrefs->chartCosys); + chartStep = new rviewText(panel, editPrefs->chartStep); + chartMarkx = new rviewText(panel, editPrefs->chartMarkx); + chartMarky = new rviewText(panel, editPrefs->chartMarky); + + // Table group + tableGroup = new wxGroupBox(panel, "", 0, 0, 100, 100); + choices[0] = lman->lookup("menTabModeDec"); + choices[1] = lman->lookup("menTabModeOct"); + choices[2] = lman->lookup("menTabModeHex"); + tableMode = new rviewChoice(panel, 3, choices, lman->lookup("textTableMode")); + tableCosys = new rviewCheckBox(panel); + tableCosys->SetValue(editPrefs->tableCosys); + tableStepx = new rviewText(panel, editPrefs->tableStepx); + tableStepy = new rviewText(panel, editPrefs->tableStepy); + + // Sound group + soundGroup = new wxGroupBox(panel, "", 0, 0, 100, 100); + i = sizeof(soundFrequencyChoices) / sizeof(char*); + soundFreq = new rviewChoice(panel, i, soundFrequencyChoices, lman->lookup("soundFrequency")); + soundFreq->SetSelection(findInChoices(editPrefs->soundFreq, (const char**)soundFrequencyChoices, i)); + i = sizeof(soundLatencyChoices) / sizeof(char*); + soundLatency = new rviewChoice(panel, i, soundLatencyChoices, lman->lookup("soundLatency")); + soundLatency->SetSelection(findInChoices(editPrefs->soundLatency, (const char**)soundLatencyChoices, i)); + soundLoop = new rviewCheckBox(panel); + soundLoop->SetValue(editPrefs->soundLoop); + + // Communication group + commGroup = new wxGroupBox(panel, "", 0, 0, 100, 100); + + transferFmt = buildFormatMenu(panel, editPrefs->transferFmt, "textTransferFormats"); + transferMsg = new wxMessage(panel, lman->lookup("textTransferParams")); + transferParm = new wxTextWindow(panel, 0, 0, 100, 100); + transferParm->WriteText((char*)(editPrefs->transferParm.ptr())); + storageFmt = buildFormatMenu(panel, editPrefs->storageFmt, "textStorageFormats"); + storageMsg = new wxMessage(panel, lman->lookup("textStorageParams")); + storageParm = new wxTextWindow(panel, 0, 0, 100, 100); + storageParm->WriteText((char*)(editPrefs->storageParm.ptr())); + + switch (editPrefs->imgMode) + { + case rim_voxel: imgMode->SetSelection(1); break; + default: imgMode->SetSelection(0); break; + } + switch (editPrefs->chartMode) + { + case rcm_line: chartMode->SetSelection(1); break; + case rcm_spline: chartMode->SetSelection(2); break; + default: chartMode->SetSelection(0); break; + } + switch (editPrefs->tableMode) + { + case 8: tableMode->SetSelection(1); break; + case 16: tableMode->SetSelection(2); break; + default: tableMode->SetSelection(0); break; + } + +#ifdef wx_msw + // Windows -- you just gotta love it. Obviously there are huge problems when + // creating two panels in one window. Thus make sure the button panel is _below_ + // the configuration panel. That's only possible by directly using Windows calls. + SetWindowPos(panel->GetHWND(), HWND_BOTTOM, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE); + SetWindowPos(butPanel->GetHWND(), HWND_BOTTOM, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE); +#endif + + label(); + + frameWidth = -1; frameHeight = -1; + + OnSize(x, y); + OnSize(x, y); + + Show(TRUE); +} + + +void rviewPrefsWindow::label(void) +{ + SetTitle(lman->lookup("titlePrefs")); + miscGroup->SetLabel(lman->lookup("textMiscPrefs")); + butOK->SetLabel(lman->lookup("textOK")); + butCancel->SetLabel(lman->lookup("textCancel")); + butApply->SetLabel(lman->lookup("textApply")); + filePath->SetLabel(lman->lookup("prefsFilePath")); + queryPath->SetLabel(lman->lookup("prefsQueryPath")); + queryFont->SetLabel(lman->lookup("prefsQueryFont")); + maxDWidth->SetLabel(lman->lookup("prefsMaxDWidth")); + maxDHeight->SetLabel(lman->lookup("prefsMaxDHeight")); + vffParams->SetLabel(lman->lookup("prefsVffParams")); + imgGroup->SetLabel(lman->lookup("textImages")); + imgDither->SetLabel(lman->lookup("prefsImgDither")); + ditherBest->SetLabel(lman->lookup("prefsDitherBest")); + rgbSpace->SetLabel(lman->lookup("prefsCspace")); + cstrap->SetLabel(lman->lookup("prefsCspaceEdit")); + movieMode->SetLabel(lman->lookup("prefsMovieMode")); + imgMode->SetLabel(lman->lookup("textImageMode")); + renderGroup->SetLabel(lman->lookup("menImgSetupRender")); + imgBBox->SetLabel(lman->lookup("textBBox")); + imgZpro->SetLabel(lman->lookup("imgSetRenZpro")); + imgClipz->SetLabel(lman->lookup("imgSetRenClipz")); + imgScale->SetLabel(lman->lookup("textScaleFactor")); + imgPixThreshLow->SetLabel(lman->lookup("imgSetVoxPixThreshLow")); + imgPixThreshHigh->SetLabel(lman->lookup("imgSetVoxPixThreshHigh")); + imgWgtThresh->SetLabel(lman->lookup("imgSetVoxWgtThresh")); + imgWgtQuant->SetLabel(lman->lookup("imgSetVoxWgtQuant")); + imgRgbBrightness->SetLabel(lman->lookup("imgSetVoxRgbBright")); + imgVoxForType->SetLabel(lman->lookup("imgSetVoxForType")); + imgLight->SetLabel(lman->lookup("prefsLight")); + imgLightAngle->SetLabel(lman->lookup("prefsLightAngle")); + imgLightAmbient->SetLabel(lman->lookup("prefsLightAmbient")); + imgLightGain->SetLabel(lman->lookup("prefsLightGain")); + imgKernSize->SetLabel(lman->lookup("prefsKernSize")); + imgKernType->SetLabel(lman->lookup("prefsKernType")); + imgLightScintAngle->SetLabel(lman->lookup("prefsLightScAngle")); + imgLightDir->SetLabel(lman->lookup("prefsLightDir")); + imgLightDist->SetLabel(lman->lookup("prefsLightDist")); + imgUseVCol->SetLabel(lman->lookup("prefsUseVCol")); + imgVoxColour->SetLabel(lman->lookup("prefsVoxColour")); + heightGroup->SetLabel(lman->lookup("prefsHeightGroup")); + imgHeightGrid->SetLabel(lman->lookup("prefsHgtGrid")); + imgHeightScale->SetLabel(lman->lookup("prefsHgtScale")); + orthoGroup->SetLabel(lman->lookup("prefsOrthoGroup")); + imgOrthoBBox->SetLabel(lman->lookup("textBBox")); + imgOrthoDragRel->SetLabel(lman->lookup("prefsOrthoDragRel")); + imgOrthoThick->SetLabel(lman->lookup("prefsOrthoThick")); + thumbGroup->SetLabel(lman->lookup("textThumb")); + thumbProjdim->SetLabel(lman->lookup("textThumbProjDim")); + thumbProjstep->SetLabel(lman->lookup("textThumbProjStep")); + thumbWidth->SetLabel(lman->lookup("textThumbWidth")); + thumbCols->SetLabel(lman->lookup("textThumbColumns")); + chartGroup->SetLabel(lman->lookup("textCharts")); + chartMode->SetLabel(lman->lookup("textChartMode")); + chartCosys->SetLabel(lman->lookup("textCosys")); + chartStep->SetLabel(lman->lookup("textStepC")); + chartMarkx->SetLabel(lman->lookup("textDataStep")); + chartMarky->SetLabel(lman->lookup("textCoStep")); + tableGroup->SetLabel(lman->lookup("textTables")); + tableMode->SetLabel(lman->lookup("textTableMode")); + tableCosys->SetLabel(lman->lookup("textCosys")); + tableStepx->SetLabel(lman->lookup("textStepx")); + tableStepy->SetLabel(lman->lookup("textStepy")); + soundGroup->SetLabel(lman->lookup("prefsSound")); + soundFreq->SetLabel(lman->lookup("soundFrequency")); + soundLatency->SetLabel(lman->lookup("soundLatency")); + soundLoop->SetLabel(lman->lookup("prefsSndLoop")); + commGroup->SetLabel(lman->lookup("textCommunication")); + transferFmt->SetLabel(lman->lookup("textTransferFormats")); + transferMsg->SetLabel(lman->lookup("textTransferParams")); + storageFmt->SetLabel(lman->lookup("textStorageFormats")); + storageMsg->SetLabel(lman->lookup("textStorageParams")); +} + + +void rviewPrefsWindow::OnSize(int w, int h) +{ + if (panel != NULL) + { + int x, y, posx, posy, h, gbox, i, width; + + GetClientSize(&x, &y); + + //need to resize? + if (( prefs_width != x) || ( prefs_height != y)) + { + frameWidth = prefs_width; + frameHeight = prefs_height; + x = prefs_width; + y = prefs_height; + SetClientSize(x, y); + return; + } + + butPanel->SetSize(0, 0, x, y); + i = y - (prefs_bottom + prefs_bheight) / 2 - prefs_border; + h = (x - 3*prefs_bwidth)/3; + butOK->SetSize(h/2, i, prefs_bwidth, prefs_bheight); + butApply->SetSize(prefs_bwidth + (3*h)/2, i, prefs_bwidth, prefs_bheight); + butCancel->SetSize(2*prefs_bwidth + (5*h)/2, i, prefs_bwidth, prefs_bheight); + + y -= prefs_bottom + prefs_border; + + if (y > prefs_pheight) y = prefs_pheight; + + posy = scroll->GetValue(); + // If the window has grown the scrollbar value might be too big + if (posy + y > prefs_pheight + prefs_border) + { + posy = prefs_pheight + prefs_border - y; + if (posy < 0) posy = 0; + scroll->SetValue(posy); + } + posx = x - prefs_border - prefs_scrwidth; + panel->SetSize(0, -posy, posx, y + posy, wxSIZE_ALLOW_MINUS_ONE); + + // It's very important to make the style wxSIZE_ALLOW_MINUS_ONE to keep wxWindows + // from interpreting negative values as defaults. Negative positioning values + // can happen due to scrolling. + + h = y - prefs_border; + scroll->SetSize(posx, prefs_border, prefs_scrwidth, h); + if (h > prefs_pheight) h = prefs_pheight; + scroll->SetViewLength(h); + // Object length hasn't changed. + //scroll->SetObjectLength(prefs_pheight); + scroll->SetPageLength((3*h)/4); + + x -= 3*prefs_border + prefs_scrwidth; + //y += prefs_bottom - prefs_border; + + // Group boxes + posx = prefs_border; + gbox = prefs_border; + miscGroup->SetSize(posx, gbox, x, prefs_grpmsc_height); + gbox += prefs_grpmsc_height + prefs_border; + imgGroup->SetSize(posx, gbox, x, prefs_grpimg_height); + gbox += prefs_grpimg_height + prefs_border; + chartGroup->SetSize(posx, gbox, x, prefs_grpcht_height); + gbox += prefs_grpcht_height + prefs_border; + tableGroup->SetSize(posx, gbox, x, prefs_grptab_height); + gbox += prefs_grptab_height + prefs_border; + soundGroup->SetSize(posx, gbox, x, prefs_grpsnd_height); + gbox += prefs_grpsnd_height + prefs_border; + commGroup->SetSize(posx, gbox, x, prefs_grpcom_height); + + // Misc group + posx = 2*prefs_border; x -= 2*prefs_border; + gbox = prefs_border; + filePath->SetSize(posx, gbox + prefs_theight/2, x, prefs_eheight); + queryPath->SetSize(posx, gbox + (3*prefs_theight)/2, x, prefs_eheight); + queryFont->SetSize(posx, gbox + (5*prefs_theight)/2, x, prefs_eheight); + + maxDWidth->SetSize(posx, gbox + (7*prefs_theight)/2, x/2, prefs_eheight); + maxDHeight->SetSize(posx + x/2, gbox + (7*prefs_theight)/2, x/2, prefs_eheight); + + vffParams->SetSize(posx, gbox + (9*prefs_theight)/2, x, prefs_eheight); + + // Image group + gbox += prefs_grpmsc_height + prefs_border; + i = gbox + prefs_theight/2; + imgDither->SetSize(posx, i, x/3, prefs_chkheight); + ditherBest->SetSize(posx, i + prefs_chkheight, x/3, prefs_chkheight); + rgbSpace->SetSize(posx + x/3, i, prefs_bwidth, prefs_bheight); + cstrap->SetSize(posx + (2*x)/3 + prefs_border, i, prefs_bwidth, prefs_bheight); + i += prefs_theight; + imgMode->SetSize(posx, i, prefs_bwidth, prefs_bheight); + movieMode->SetSize(posx + x/3, i, prefs_bwidth, prefs_bheight); + imgScale->SetSize(posx + (2*x)/3 + x/30, i, x/3 -x/30, prefs_eheight); + i = gbox + (5*prefs_theight)/2 + prefs_border; + renderGroup->SetSize(posx, i, x, prefs_grpren_height); + heightGroup->SetSize(posx, i + prefs_grpren_height + prefs_border, x, prefs_grphgt_height); + orthoGroup->SetSize(posx, i + prefs_grpren_height + prefs_grphgt_height + 2*prefs_border, x, prefs_grport_height); + thumbGroup->SetSize(posx, i + prefs_grpren_height + prefs_grphgt_height + prefs_grport_height + 3*prefs_border, x, prefs_grpthb_height); + // Image renderer subgroup + posx += prefs_border; x -= 2*prefs_border; + i += prefs_theight/2; + imgZpro->SetSize(posx, i, x/3, prefs_eheight); + imgClipz->SetSize(posx + x/3, i, x/3, prefs_eheight); + imgBBox->SetSize(posx + (2*x)/3 + x/40, i, x/3 - x/40, prefs_chkheight); + imgRgbBrightness->SetSize(posx + (2*x)/3 + x/40, i + (2*prefs_theight)/3, x/3 - x/40, prefs_chkheight); + imgVoxForType->SetSize(posx + (2*x)/3 + x/40, i + (4*prefs_theight)/3, x/3 - x/40, prefs_chkheight); + i += prefs_theight; + imgPixThreshLow->SetSize(posx, i, x/3, prefs_eheight); + imgPixThreshHigh->SetSize(posx + x/3, i, x/3, prefs_eheight); + i += prefs_theight; + imgWgtThresh->SetSize(posx, i, x/2, prefs_eheight); + imgWgtQuant->SetSize(posx + x/2, i, x/2, prefs_eheight); + i += prefs_theight; + imgLight->SetSize(posx, i, x/4, prefs_bheight); // must reduce width to x/4! + imgKernSize->SetSize(posx + x/4, i, x/5, prefs_bheight); + imgKernType->SetSize(posx + 2*x/3, i, x/5, prefs_bheight); + i += prefs_theight; + imgLightAngle->SetSize(posx, i, x/3, prefs_eheight); + imgLightAmbient->SetSize(posx + x/3, i, x/3, prefs_eheight); + imgLightGain->SetSize(posx + (2*x)/3, i, x/3, prefs_eheight); + i += prefs_theight; + imgLightScintAngle->SetSize(posx, i, x/3, prefs_eheight); + imgLightDir->SetSize(posx + x/3, i, x/3, prefs_eheight); + imgLightDist->SetSize(posx + (2*x)/3, i, x/3, prefs_eheight); + i += prefs_theight; + imgUseVCol->SetSize(posx, i, x/2, prefs_eheight); + imgVoxColour->SetSize(posx + x/2, i, x/2, prefs_eheight); + // Image heightfield subgroup + i = gbox + 3*prefs_theight + prefs_grpren_height + 2*prefs_border; + imgHeightGrid->SetSize(posx, i, x/2, prefs_eheight); + imgHeightScale->SetSize(posx + x/2, i, x/2, prefs_eheight); + // Orthosection subgroup + i = gbox + 3*prefs_theight + prefs_grpren_height + prefs_grphgt_height + 2*prefs_border; + imgOrthoBBox->SetSize(posx, i, x/3, prefs_eheight); + imgOrthoDragRel->SetSize(posx + x/3, i, x/3, prefs_eheight); + imgOrthoThick->SetSize(posx + (2*x)/3, i, x/3, prefs_eheight); + // Image thumbnail subgroup + i = gbox + 3*prefs_theight + prefs_grpren_height + prefs_grphgt_height + prefs_grport_height + 3*prefs_border; + thumbProjdim->SetSize(posx, i, x/2, prefs_eheight); + thumbProjstep->SetSize(posx + x/2, i, x/2, prefs_eheight); + i += prefs_theight; + thumbWidth->SetSize(posx, i, x/2, prefs_eheight); + thumbCols->SetSize(posx + x/2, i, x/2, prefs_eheight); + posx -= prefs_border; x += 2*prefs_border; + + // Chart group + gbox += prefs_grpimg_height + prefs_border; + i = gbox + prefs_theight/2; + chartMode->SetSize(posx, i, prefs_bwidth, prefs_bheight); + chartCosys->SetSize(posx + x/2, i, x/2, prefs_eheight); + i += prefs_theight; + chartStep->SetSize(posx, i, x/3, prefs_eheight); + chartMarkx->SetSize(posx + x/3, i, x/3, prefs_eheight); + chartMarky->SetSize(posx + (2*x)/3, i, x/3, prefs_eheight); + + // Table group + gbox += prefs_grpcht_height + prefs_border; + i = gbox + prefs_theight/2; + tableMode->SetSize(posx, i, prefs_bwidth, prefs_bheight); + tableCosys->SetSize(posx + x/2, i, x/2, prefs_eheight); + i += prefs_theight; + tableStepx->SetSize(posx, i, x/2, prefs_eheight); + tableStepy->SetSize(posx + x/2, i, x/2, prefs_eheight); + + // Sound group + gbox += prefs_grptab_height + prefs_border; + i = gbox + prefs_theight/2; + h = (x - 2*prefs_border) / 3; + soundFreq->SetSize(posx, i, prefs_bwidth, prefs_bheight); + soundLatency->SetSize(posx + h + prefs_border, i, prefs_bwidth, prefs_bheight); + soundLoop->SetSize(posx + 2*(h + prefs_border), i, h, prefs_bheight); + + // Communications box + gbox += prefs_grpsnd_height + prefs_border; + i = gbox + prefs_theight/2; + h = (x - prefs_border) / 2; + width = (prefs_bwidth > h) ? h : prefs_bwidth; + width -= prefs_border; + transferFmt->SetSize(posx, i, width, prefs_bheight); + storageFmt->SetSize(posx + h + prefs_border, i, width, prefs_bheight); + i += prefs_bheight + 3*prefs_border; + transferMsg->SetSize(posx, i, h, prefs_mheight); + storageMsg->SetSize(posx + h + prefs_border, i, h, prefs_mheight); + i += prefs_mheight; + transferParm->SetSize(posx, i, h, prefs_twheight); + storageParm->SetSize(posx + h + prefs_border, i, h, prefs_twheight); + } +} + + +void rviewPrefsWindow::textWindowToString(DynamicString &str, wxTextWindow *twin) +{ + char *text = twin->GetContents(); + str = text; + delete [] text; +} + +void rviewPrefsWindow::updatePrefs(void) +{ + editPrefs->filePath = filePath->GetValue(); + editPrefs->queryPath = queryPath->GetValue(); + editPrefs->queryFont = queryFont->GetValue(); + editPrefs->maxDWidth = asctoi(maxDWidth->GetValue()); + editPrefs->maxDHeight = asctoi(maxDHeight->GetValue()); + editPrefs->vffParams = vffParams->GetValue(); + editPrefs->imgDither = imgDither->GetValue(); + editPrefs->ditherBest = ditherBest->GetValue(); + editPrefs->rgbSpace = rgbSpace->GetSelection(); + editPrefs->movieMode = movieMode->GetSelection(); + editPrefs->imgBBox = imgBBox->GetValue(); + editPrefs->imgZpro = asctoi(imgZpro->GetValue()); + editPrefs->imgClipz = asctoi(imgClipz->GetValue()); + editPrefs->imgScale = asctof(imgScale->GetValue()); + editPrefs->imgPixThreshLow = asctof(imgPixThreshLow->GetValue()); + editPrefs->imgPixThreshHigh = asctof(imgPixThreshHigh->GetValue()); + editPrefs->imgWgtThresh = asctof(imgWgtThresh->GetValue()); + editPrefs->imgWgtQuant = asctoi(imgWgtQuant->GetValue()); + editPrefs->imgRgbBrightness = imgRgbBrightness->GetValue(); + editPrefs->imgVoxForType = imgVoxForType->GetValue(); + editPrefs->imgLight = imgLight->GetValue(); + editPrefs->imgLightAngle = atof(imgLightAngle->GetValue()); + editPrefs->imgLightAmbient = atof(imgLightAmbient->GetValue()); + editPrefs->imgLightGain = atof(imgLightGain->GetValue()); + editPrefs->imgKernSize = imgKernSize->GetSelection(); + editPrefs->imgKernType = imgKernType->GetSelection(); + editPrefs->imgLightScintAngle = atof(imgLightScintAngle->GetValue()); + editPrefs->imgLightDir = imgLightDir->GetValue(); + editPrefs->imgLightDist = asctoi(imgLightDist->GetValue()); + editPrefs->imgUseVCol = imgUseVCol->GetValue(); + editPrefs->imgVoxColour = asctof(imgVoxColour->GetValue()); + editPrefs->imgHeightGrid = atoi(imgHeightGrid->GetValue()); + editPrefs->imgHeightScale = atof(imgHeightScale->GetValue()); + editPrefs->imgOrthoBBox = imgOrthoBBox->GetValue(); + editPrefs->imgOrthoDragRel = imgOrthoDragRel->GetValue(); + editPrefs->imgOrthoThick = atoi(imgOrthoThick->GetValue()); + editPrefs->chartCosys = chartCosys->GetValue(); + editPrefs->chartStep = atoi(chartStep->GetValue()); + editPrefs->chartMarkx = atoi(chartMarkx->GetValue()); + editPrefs->chartMarky = atof(chartMarky->GetValue()); + editPrefs->tableCosys = tableCosys->GetValue(); + editPrefs->tableStepx = atoi(tableStepx->GetValue()); + editPrefs->tableStepy = atoi(tableStepy->GetValue()); + editPrefs->thumbProjdim = atoi(thumbProjdim->GetValue()); + editPrefs->thumbProjstep = atoi(thumbProjstep->GetValue()); + editPrefs->thumbWidth = atoi(thumbWidth->GetValue()); + editPrefs->thumbCols = atoi(thumbCols->GetValue()); + editPrefs->soundFreq = atoi(soundFrequencyChoices[soundFreq->GetSelection()]); + editPrefs->soundLatency = atoi(soundLatencyChoices[soundLatency->GetSelection()]); + editPrefs->soundLoop = soundLoop->GetValue(); + editPrefs->transferFmt = transferFmt->GetSelection(); + textWindowToString(editPrefs->transferParm, transferParm); + editPrefs->storageFmt = storageFmt->GetSelection(); + textWindowToString(editPrefs->storageParm, storageParm); + + switch (imgMode->GetSelection()) + { + case 1: editPrefs->imgMode = rim_voxel; break; + default: editPrefs->imgMode = rim_surf; break; + } + switch (chartMode->GetSelection()) + { + case 1: editPrefs->chartMode = rcm_line; break; + case 2: editPrefs->chartMode = rcm_spline; break; + default: editPrefs->chartMode = rcm_bar; break; + } + switch (tableMode->GetSelection()) + { + case 1: editPrefs->tableMode = 8; break; + case 2: editPrefs->tableMode = 16; break; + default: editPrefs->tableMode = 10; break; + } + if (csmap != NULL) + { + csmap->getParameters(&(editPrefs->csp)); + } +} + + +void rviewPrefsWindow::updateAndDie(void) +{ + updatePrefs(); + if (myParent != NULL) myParent->closeEditor(editPrefs); + editPrefs = NULL; +} + + +int rviewPrefsWindow::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)butOK) + { + updateAndDie(); + return 1; + } + else if (&obj == (wxObject*)butApply) + { + updatePrefs(); + if (myParent != NULL) myParent->updatePrefs(editPrefs); + return 1; + } + else if (&obj == (wxObject*)butCancel) + { + delete editPrefs; editPrefs = NULL; + if (myParent != NULL) myParent->closeEditor(NULL); + return 1; + } + else if (&obj == (wxObject*)cstrap) + { + if (csmap == NULL) + { + r_Ref<r_GMarray> dummyMdd; + + csmap = new colourspaceMapper(dummyMdd, rbt_none, &(editPrefs->csp), TRUE, NULL); + } + csmap->openEditor(); + } + } + else if (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND) + { + updateAndDie(); + } + else if ((type == wxEVENT_TYPE_SCROLLBAR_COMMAND) && (&obj == (wxObject*)scroll)) + { + int x, y, posy; + + GetClientSize(&x, &y); + posy = scroll->GetValue(); + panel->SetSize(0, -posy, x - prefs_border - prefs_scrwidth, y + posy - prefs_bottom - prefs_border, wxSIZE_ALLOW_MINUS_ONE); + } + + return 0; +} + + +void rviewPrefsWindow::setupVariables(void) +{ + editPrefs = NULL; myParent = NULL; + panel = NULL; csmap = NULL; +} + + +int rviewPrefsWindow::findInChoices(int value, const char **choices, int number) +{ + int i; + int lastval, newval; + + lastval = atoi(choices[0]); + for (i=1; i<number; i++) + { + newval = atoi(choices[i]); + if ((lastval <= value) && (value < newval)) break; + lastval = newval; + } + return i-1; +} + + +int rviewPrefsWindow::userEvent(const user_event &ue) +{ + if ((ue.type == usr_cspace_changed) && (ue.data == (void*)csmap)) + { + csmap->getParameters(&(editPrefs->csp)); + return 1; + } + return 0; +} diff --git a/applications/rview/rviewPrefs.hh b/applications/rview/rviewPrefs.hh new file mode 100644 index 0000000..d1cc181 --- /dev/null +++ b/applications/rview/rviewPrefs.hh @@ -0,0 +1,275 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * Management of rView's preferences. This includes an object encapsulating + * the preferences and providing IO of these preferences (rviewPrefs) and a + * frame class that allows displaying / editing the current preferences + * (rviewPrefsWindow). + * + * COMMENTS: + * none + */ + + + +#ifndef _RVIEW_PREFS_H_ +#define _RVIEW_PREFS_H_ + +// #include "wx_scrol.h" +#include "wx/xrc/xh_scrol.h" + +#include "raslib/mddtypes.hh" + +#include "rviewUtils.hh" +#include "rviewDModes.hh" + + + +// Needed by rviewPrefsWindow. Full definition below. +class rviewPrefs; + + + + +/* + * The window for editing the preferences + */ +class rviewPrefsWindow: public rviewFrame +{ + public: + + rviewPrefsWindow(void); + rviewPrefsWindow(rviewPrefs *Prefs); + ~rviewPrefsWindow(void); + + void setPrefs(rviewPrefs *Prefs); + + void unlinkParent(void); + + void OnSize(int w, int h); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + int userEvent(const user_event &ue); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + + protected: + + void setupVariables(void); + void updatePrefs(void); + void updateAndDie(void); + int findInChoices(int value, const char **choices, int number); + + static rviewChoice *buildFormatMenu(wxPanel *parent, int fmtNum, const char *label); + static void textWindowToString(DynamicString &str, wxTextWindow *twin); + + rviewPrefs *editPrefs; + rviewPrefs *myParent; + + wxPanel *panel, *butPanel; + rviewButton *butOK, *butCancel, *butApply; + // wxGroupBox *miscGroup, *imgGroup, *renderGroup, *thumbGroup, *heightGroup; + // wxGroupBox *chartGroup, *tableGroup, *soundGroup, *commGroup, *orthoGroup; + wxWindowBase *miscGroup, *imgGroup, *renderGroup, *thumbGroup, *heightGroup; + wxWindowBase *chartGroup, *tableGroup, *soundGroup, *commGroup, *orthoGroup; + rviewText *filePath, *queryPath, *queryFont; + rviewText *vffParams; + rviewText *maxDWidth, *maxDHeight; + rviewCheckBox *imgDither, *ditherBest; + rviewChoice *imgMode, *chartMode, *tableMode, *rgbSpace, *movieMode; + rviewCheckBox *imgBBox; + rviewText *imgZpro, *imgClipz, *imgPixThreshLow, *imgPixThreshHigh, *imgWgtThresh, *imgWgtQuant; + rviewText *imgScale; + rviewCheckBox *imgRgbBrightness, *imgVoxForType; + rviewCheckBox *imgLight; + rviewText *imgLightAmbient, *imgLightGain, *imgLightAngle, *imgLightScintAngle; + rviewChoice *imgKernSize, *imgKernType; + rviewCheckBox *imgUseVCol; + rviewText *imgVoxColour; + rviewText *imgLightDir, *imgLightDist; + rviewCheckBox *imgOrthoBBox, *imgOrthoDragRel; + rviewText *imgOrthoThick; + rviewCheckBox *chartCosys; + rviewText *chartStep, *chartMarkx, *chartMarky; + rviewCheckBox *tableCosys; + rviewText *tableStepx, *tableStepy; + rviewText *thumbProjdim, *thumbProjstep, *thumbWidth, *thumbCols; + rviewChoice *soundFreq, *soundLatency; + rviewCheckBox *soundLoop; + rviewText *imgHeightGrid, *imgHeightScale; + rviewChoice *transferFmt, *storageFmt; + wxTextWindow *transferParm, *storageParm; + wxMessage *transferMsg, *storageMsg; + wxScrollBar *scroll; + rviewButton *cstrap; + colourspaceMapper *csmap; + + // constants + static const char *soundLatencyChoices[]; + static const char *soundFrequencyChoices[]; + + // Width and height of preferences window + static const int prefs_width; + static const int prefs_height; + // Borders used in prefs window + static const int prefs_border; + // Space at the bottom for button bar + static const int prefs_bottom; + // Button dimensions + static const int prefs_bwidth; + static const int prefs_bheight; + // Height of a text widget + static const int prefs_theight; + // Height of a checkbox + static const int prefs_chkheight; + // Height of a text widget's writable field + static const int prefs_eheight; + // ScrollBar width + static const int prefs_scrwidth; + // TextWindow height + static const int prefs_twheight; + // Message height + static const int prefs_mheight; + // Group boxes + static const int prefs_grpmsc_height; + static const int prefs_grpimg_height; + static const int prefs_grpren_height; + static const int prefs_grphgt_height; + static const int prefs_grport_height; + static const int prefs_grpthb_height; + static const int prefs_grpcht_height; + static const int prefs_grptab_height; + static const int prefs_grpsnd_height; + static const int prefs_grpcom_height; + // Height of panel + static const int prefs_pheight; +}; + + +/* + * Object holding the preferences and the current setup. + */ +class rviewPrefs +{ + public: + + rviewPrefs(void); + rviewPrefs(const char *file); + rviewPrefs(const rviewPrefs &srcPrefs); + ~rviewPrefs(void); + + int load(const char *file); + int save(const char *file); + int edit(void); + void editorClosed(void); + void closeEditor(rviewPrefs *newPrefs); + void updatePrefs(rviewPrefs *newPrefs); + void markModified(void); + + static void copyPrefs(const rviewPrefs &src, rviewPrefs &dest); + + r_Data_Format getTransferFormat( void ) const; + r_Data_Format getStorageFormat( void ) const; + + DynamicString serverName; + int serverPort; + DynamicString databaseName; + DynamicString userName; + DynamicString lastColl; + DynamicString lastScColl; + DynamicString lastOrthoColl; + DynamicString filePath; + DynamicString queryPath; + DynamicString queryFont; + DynamicString vffParams; + int lastDisplay; + int maxDWidth, maxDHeight; + bool imgDither, ditherBest; + rviewImageMode imgMode; + rviewChartMode chartMode; + int movieMode, rgbSpace; + int tableMode; + bool imgBBox; + unsigned long imgZpro, imgClipz, imgWgtQuant; + double imgPixThreshLow, imgPixThreshHigh, imgWgtThresh; + double imgScale; + bool imgRgbBrightness, imgVoxForType; + bool imgLight; + double imgLightAmbient, imgLightGain, imgLightAngle, imgLightScintAngle; + int imgKernSize, imgKernType; + bool imgUseVCol; + double imgVoxColour; + DynamicString imgLightDir; + int imgLightDist; + int imgHeightGrid; + double imgHeightScale; + bool imgOrthoBBox, imgOrthoDragRel; + int imgOrthoThick; + bool chartCosys; + int chartStep, chartMarkx; + double chartMarky; + bool tableCosys; + int tableStepx, tableStepy; + int thumbProjdim, thumbProjstep, thumbWidth, thumbCols; + int soundFreq, soundLatency, soundLoop; + int transferFmt, storageFmt; + DynamicString transferParm, storageParm; + colourspace_params csp; + + + protected: + + void setupVariables(void); + // used for getting the value of an argument when reading the prefs file. + char *getValue(char *b); + // convert long strings (including newlines) from/to external representation + static char *toExternal(const DynamicString &str); + static void fromExternal(const char *ext, DynamicString &str); + // read a line into the internal buffer; return a pointer to the start if successful + char *readLine(FILE *fp); + + rviewPrefsWindow *pwin; + bool prefsModified; + + char *inbuff; + unsigned long buffSize; + + static const unsigned long buffExtendGranularity; + static const keyword_to_ident_c prefsVarDesc[]; +}; + + + +/* + * Global variables + */ + +extern rviewPrefs *prefs; + +#endif diff --git a/applications/rview/rviewQuery.cpp b/applications/rview/rviewQuery.cpp new file mode 100644 index 0000000..eddbd7f --- /dev/null +++ b/applications/rview/rviewQuery.cpp @@ -0,0 +1,647 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView query shell. Handles editing, loading, saving and execution + * of RasQL queries. Actual database accesses are performed through + * class rView's member functions. + * + * COMMENTS: + * None + */ + + + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + + +#include <string.h> +#include <iostream.h> +#include <ctype.h> + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + +#include "raslib/rmdebug.hh" + +#include "rviewApp.hh" +#include "rviewUtils.hh" +//#include "rviewDb.hh" +#include "rviewQuery.hh" +#include "rviewPrefs.hh" + + + + +const int rviewQuery::query_width = 500; +const int rviewQuery::query_height = 300; +const int rviewQuery::query_border = 8; +const int rviewQuery::query_bottom = 50; +const int rviewQuery::query_bwidth = 80; +const int rviewQuery::query_bheight = 40; + + + + +const char rviewQuery::query_extension[] = ".ql"; +const char rviewQuery::query_firstline[] = "-- rview-Query"; + + + +// The Query window counter +int rviewQuery::queryCounter = 0; + + +// Lookup tables for fonts. These have to be sorted case-sensitively! +const keyword_to_ident_c rviewQuery::fontNameTab[] = { + {wxDECORATIVE, "decorative"}, + {wxDEFAULT, "default"}, + {wxMODERN, "modern"}, + {wxROMAN, "roman"}, + {wxSCRIPT, "script"}, + {wxSWISS, "swiss"}, + {wxTELETYPE, "teletype"} +}; +const keyword_to_ident_c rviewQuery::fontStyleTab[] = { + {wxITALIC, "italic"}, + {wxSLANT, "slant"} +}; + +const keyword_to_ident_c rviewQuery::fontWeightTab[] = { + {wxBOLD, "bold"}, + {wxLIGHT, "light"} +}; + + + + +rviewQuery::rviewQuery(rviewDatabase *db, char *query) : rviewFrame(NULL, NULL, 0, 0, query_width, query_height) +{ + int w, h; + const char *b; + char *d; + char buffer[STRINGSIZE]; + int fontSize, fontName, fontStyle, fontWeight; + const int fontNameSize = sizeof(fontNameTab) / sizeof(keyword_to_ident); + const int fontStyleSize = sizeof(fontStyleTab) / sizeof(keyword_to_ident); + const int fontWeightSize = sizeof(fontWeightTab) / sizeof(keyword_to_ident); + + RMDBGENTER(3, RMDebug::module_applications, "rviewQuery", "rviewQuery()"); + + queryDb = db; + updateDisplay = NULL; + + CreateStatusLine(1); + + mbar = NULL; + for (w=0; w<3; w++) mbarMenus[w] = NULL; + + if (::wxDirExists((char*)(prefs->queryPath.ptr()))) + hotPath = prefs->queryPath; + else + hotPath = "."; + + // Read font from prefs + b = prefs->queryFont; + fontSize = 12; fontName = wxDEFAULT; fontStyle = wxNORMAL; fontWeight = wxNORMAL; + while (*b != 0) + { + while ((*b == ' ') || (*b == ',')) b++; + if (*b == 0) break; + d = buffer; + while ((*b != ' ') && (*b != ',') && (*b != 0)) {*d++ = *b++;} + *d++ = 0; + w = atoi(buffer); + if (w != 0) fontSize = w; + else + { + if ((w = rviewLookupKeyword(buffer, fontNameTab, fontNameSize, FALSE)) >= 0) + fontName = w; + else if ((w = rviewLookupKeyword(buffer, fontStyleTab, fontStyleSize, FALSE)) >= 0) + fontStyle = w; + else if ((w = rviewLookupKeyword(buffer, fontWeightTab, fontWeightSize, FALSE)) >= 0) + fontWeight = w; + else + cerr << "Bad identifier in font string: " << buffer << endl; + } + } + //cout << "size " << fontSize << ", name " << fontName << ", style " << fontStyle << ", weight " << fontWeight << endl; + + font = new wxFont(fontSize, fontName, fontStyle, fontWeight); + //font = ::wxTheFontList->FindOrCreateFont(fontSize, fontName, fontStyle, fontWeight); + //cout << "Font " << font->GetPointSize() << ',' << font->GetFamily() << ',' << font->GetStyle() << endl; + + GetClientSize(&w, &h); + + // Set identifier + qwindowID = queryCounter++; + updateID = -1; // no update object + + w -= 2*query_border; h -= query_bottom; + twin = new wxTextWindow((wxWindow*)this, query_border, query_border, w, h - 2*query_border, wxBORDER | wxNATIVE_IMPL); + + twin->SetFont(font); + + panel = new wxPanel((wxWindow*)this, query_border, h, w, query_bottom); + butClear = new rviewButton(panel); + butExec = new rviewButton(panel); + butUpdt = new rviewButton(panel); + + if (query != NULL) + { + twin->WriteText(query); + } + + buildMenubar(); + + newDBState(rmanClientApp::theApp()->ReadDBState()); + + label(); + + OnSize(w, h); + + Show(TRUE); +} + + +rviewQuery::~rviewQuery(void) +{ + // If an update window is open notify it. + if (updateDisplay != NULL) + { + updateDisplay->noLongerUpdate(); + } +} + + +const char *rviewQuery::getFrameName(void) const +{ + return "rviewQuery"; +} + +rviewFrameType rviewQuery::getFrameType(void) const +{ + return rviewFrameTypeQuery; +} + + +void rviewQuery::OnSize(int w, int h) +{ + int x, y; + + GetClientSize(&x, &y); + if ((frameWidth == x) && (frameHeight == y)) return; + frameWidth = x; frameHeight = y; + + x -= 2*query_border; y -= query_bottom; + twin->SetSize(query_border, query_border, x, y - 2*query_border); + + panel->SetSize(query_border, y, x, query_bottom); + + y = (query_bottom - query_bheight) / 2; + + x = (x - 3*query_bwidth) / 3; + butClear->SetSize(x/2, y, query_bwidth, query_bheight); + butExec->SetSize((3*x)/2 + query_bwidth, y, query_bwidth, query_bheight); + butUpdt->SetSize((5*x)/2 + 2*query_bwidth, y, query_bwidth, query_bheight); + + // doesn't work, unfortunately + /*if (mbar != NULL) + { + int hw, hh; + + mbar->GetSize(&x, &y); + mbarMenus[3]->GetSize(&hw, &hh); + mbarMenus[3]->SetSize(x-hw, (y-hh)/2, hw, hh); + }*/ +} + + +void rviewQuery::OnMenuCommand(int id) +{ + // Load query from hotlist? + if ((id >= MENU_QUERY_HOTLIST) && (id < MENU_QUERY_HOTLIST + hotNumber)) + { + char buffer[STRINGSIZE]; + + sprintf(buffer, "%s"DIR_SEPARATOR"%s%s", hotPath.ptr(), mbar->GetLabel(id), query_extension); + loadQuery(buffer); + return; + } + + switch (id) + { + case MENU_QUERY_FILE_OPEN: + case MENU_QUERY_FILE_SAVE: + { + char *name, *aux; + char *prefDir = (char*)(hotPath.ptr()); + char filter[16]; + + sprintf(filter, "*%s", query_extension); + if (::wxDirExists(prefDir)) name = prefDir; else name = "."; + name = ::wxFileSelector(lman->lookup((id == MENU_QUERY_FILE_OPEN) ? "titleQueryLoad" : "titleQuerySave"), name, NULL, NULL, filter, 0 , this); + if (name != NULL) + { + aux = ::wxFileNameFromPath(name); + if (strlen(aux) > 0) + { + if (id == MENU_QUERY_FILE_OPEN) + { + RMDBGONCE(3, RMDebug::module_applications, "rviewQuery", "loadQuery()"); + loadQuery(name); + } + else + { + RMDBGONCE(3, RMDebug::module_applications, "rviewQuery", "saveQuery()"); + saveQuery(name); + } + hotPath = ::wxPathOnly(name); + prefs->queryPath = hotPath; + prefs->markModified(); + buildMenubar(); + } + } + } + break; + case MENU_QUERY_FILE_EXIT: this->Close(TRUE); break; + case MENU_QUERY_EDIT_CUT: twin->Cut(); break; + case MENU_QUERY_EDIT_COPY: twin->Copy(); break; + case MENU_QUERY_EDIT_PASTE: twin->Paste(); break; + default: break; + } +} + + + +void rviewQuery::buildMenubar(void) +{ + char pattern[STRINGSIZE], leaf[STRINGSIZE]; + char *f; + char *qhots[MENU_QUERY_HOTRANGE]; + int i; + wxMenu *hotList; + + // Do we actually need to rebuild the menu? + if ((mbar != NULL) && (strcmp(hotPath, lastHotPath) == 0)) return; + + // build hotlist menu + hotList = new wxMenu; + sprintf(pattern, "%s"DIR_SEPARATOR"*%s", hotPath.ptr(), query_extension); + + // Sort the hotlist alphabetically + f = ::wxFindFirstFile(pattern); hotNumber = 0; + while (f && (hotNumber < MENU_QUERY_HOTRANGE)) + { + strcpy(leaf, ::wxFileNameFromPath(f)); + f = strstr(leaf, query_extension); + if (f != NULL) *f = '\0'; + if ((qhots[hotNumber] = new char[strlen(leaf) + 1]) == NULL) break; + strcpy(qhots[hotNumber], leaf); + hotNumber++; + f = wxFindNextFile(); + } + + rviewQuicksortStrings(qhots, 0, hotNumber-1); + + for (i=0; i<hotNumber; i++) + { + //cout << "Item " << i << ": " << qhots[i] << endl; + hotList->Append(MENU_QUERY_HOTLIST + i, qhots[i]); + delete [] qhots[i]; + } + + if (mbar == NULL) + { + mbarMenus[0] = new wxMenu; + mbarMenus[0]->Append(MENU_QUERY_FILE_OPEN, ""); + mbarMenus[0]->Append(MENU_QUERY_FILE_SAVE, ""); + mbarMenus[0]->Append(MENU_QUERY_FILE_EXIT, ""); + + mbarMenus[1] = new wxMenu; + mbarMenus[1]->Append(MENU_QUERY_EDIT_CUT, ""); + mbarMenus[1]->Append(MENU_QUERY_EDIT_COPY, ""); + mbarMenus[1]->Append(MENU_QUERY_EDIT_PASTE, ""); + + mbarMenus[2] = hotList; + + + mbar = new wxMenuBar; + sprintf(pattern, "&%s", lman->lookup("menQueryFile")); + mbar->Append(mbarMenus[0], pattern); + sprintf(pattern, "&%s", lman->lookup("menQueryEdit")); + mbar->Append(mbarMenus[1], pattern); + sprintf(pattern, "&%s", lman->lookup("menQueryHotlist")); + mbar->Append(mbarMenus[2], pattern); + + SetMenuBar(mbar); + } + else + { + mbar->Delete(mbarMenus[2], 2); + sprintf(pattern, "&%s", lman->lookup("menQueryHotlist")); + mbar->Append(hotList, pattern); + delete mbarMenus[2]; + mbarMenus[2] = hotList; + } +} + + + +void rviewQuery::label(void) +{ + updateTitle(); + + mbar->SetLabel(MENU_QUERY_FILE_OPEN, lman->lookup("menQueryFileOpen")); + mbar->SetLabel(MENU_QUERY_FILE_SAVE, lman->lookup("menQueryFileSave")); + mbar->SetLabel(MENU_QUERY_FILE_EXIT, lman->lookup("menQueryFileClose")); + mbar->SetHelpString(MENU_QUERY_FILE_OPEN, lman->lookup("helpQueryFileOpen")); + mbar->SetHelpString(MENU_QUERY_FILE_SAVE, lman->lookup("helpQueryFileSave")); + mbar->SetHelpString(MENU_QUERY_FILE_EXIT, lman->lookup("helpQueryFileClose")); + + mbar->SetLabel(MENU_QUERY_EDIT_CUT, lman->lookup("menQueryEditCut")); + mbar->SetLabel(MENU_QUERY_EDIT_COPY, lman->lookup("menQueryEditCopy")); + mbar->SetLabel(MENU_QUERY_EDIT_PASTE, lman->lookup("menQueryEditPaste")); + mbar->SetHelpString(MENU_QUERY_EDIT_CUT, lman->lookup("helpQueryEditCut")); + mbar->SetHelpString(MENU_QUERY_EDIT_COPY, lman->lookup("helpQueryEditCopy")); + mbar->SetHelpString(MENU_QUERY_EDIT_PASTE, lman->lookup("helpQueryEditPaste")); + + mbar->SetLabelTop(0, lman->lookup("menQueryFile")); + mbar->SetLabelTop(1, lman->lookup("menQueryEdit")); + mbar->SetLabelTop(2, lman->lookup("menQueryHotlist")); + + butClear->SetLabel(lman->lookup("textClear")); + butExec->SetLabel(lman->lookup("textExec")); + butUpdt->SetLabel(lman->lookup("textUpdt")); +} + + +void rviewQuery::updateTitle(void) +{ + if (updateID == -1) + { + SetTitle(lman->lookup("titleQuery")); + } + else + { + char buffer[STRINGSIZE]; + sprintf(buffer, "%s q%dd%d", lman->lookup("titleQuery"), qwindowID, updateID); + SetTitle(buffer); + } +} + + +int rviewQuery::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)butClear) + { + twin->Clear(); + return 1; + } + if (&obj == (wxObject*)butExec) + { + int numl = twin->GetNumberOfLines(); + int i, totalLength = 1; + char *b, *query; + + query = twin->GetContents(); + + // Execute query. + i = rmanClientApp::theApp()->executeQuery(query, (updateDisplay == NULL) ? NULL : &updateMddObj); + // Did it fail? + if (i == 0) + { + int line, column; + + // Was it a query error? + if (queryDb->getErrorInfo(line, column) != 0) + { + long offset = twin->XYToPosition(column-1, line-1); + + // query was big enough to hold the entore query, so it's definitely + // big enough to hold one line + twin->GetLineText(line-1, query); + b = query + column-1; + while ((*b != ' ') && (!iscntrl(*b))) b++; + twin->SetSelection(offset, offset + (long)(b - (query + column-1))); + } + } + delete [] query; + // Notify that we did something, not whether it was a success + return 1; + } + if (&obj == (wxObject*)butUpdt) + { + rviewDisplay *newDisplay; + + // Don't set updateDisplay directly. We might have a valid update image and + // issued a cancel from the file selection box. + if ((newDisplay = (rviewDisplay*)rmanClientApp::theApp()->OpenFile(rviewDisplay::display_flag_update, &updateMddObj, FALSE)) != NULL) + { + if (updateDisplay != NULL) + { + updateDisplay->noLongerUpdate(); + } + updateDisplay = newDisplay; + updateDisplay->setQueryWindow(qwindowID); + updateID = updateDisplay->getIdentifier(); + updateTitle(); + return 1; + } + } + } + return 0; +} + + +int rviewQuery::getIdentifier(void) const +{ + return qwindowID; +} + +int rviewQuery::getQueryCounter(void) const +{ + return queryCounter; +} + + + +int rviewQuery::userEvent(const user_event &ue) +{ + if ((ue.type == usr_db_opened) || (ue.type == usr_db_closed)) + { + newDBState((ue.type == usr_db_opened)); + return 1; + } + if ((updateDisplay != NULL) && (ue.type == usr_update_closed)) + { + r_Ref<r_GMarray> *whichMdd = (r_Ref<r_GMarray>*)(ue.data); + if (*whichMdd == updateMddObj) + { + updateDisplay = NULL; updateID = -1; + updateTitle(); + return 1; + } + } + return 0; +} + + +void rviewQuery::newDBState(bool newState) +{ + butExec->Enable(newState); +} + + +bool rviewQuery::loadQuery(char *file) +{ + size_t length; + int idlength; + char *buffer, *b; + FILE *fp; + + if ((fp = fopen(file, "rb")) == NULL) + { + char msg[STRINGSIZE]; + sprintf(msg, "%s %s", lman->lookup("errorFileOpen"), file); + rviewErrorbox::reportError(msg, rviewQuery::getFrameName(), "loadQuery"); + return FALSE; + } + + fseek(fp, 0, SEEK_END); + length = ftell(fp); + if ((buffer = new char[length+1]) == NULL) + { + rviewErrorbox::reportError(lman->lookup("errorMemory"), rviewQuery::getFrameName(), "loadQuery"); + fclose(fp); return FALSE; + } + fseek(fp, 0, SEEK_SET); + + b = buffer; + for (idlength=0; idlength<(int)length; idlength++) + { + int c; + + if ((c = fgetc(fp)) == EOF) break; +#ifdef __VISUALC__ + if (c != '\r') +#endif + *b++ = c; + } + *b++ = '\0'; + + if (idlength < (int)length) + { + char msg[STRINGSIZE]; + sprintf(msg, "%s %s", lman->lookup("errorFileRead"), file); + rviewErrorbox::reportError(msg, rviewQuery::getFrameName(), "loadQuery"); + fclose(fp); + return FALSE; + } + + fclose(fp); + + idlength = strlen("RasDaView-Query"); + if (strncmp(buffer, "RasDaView-Query", idlength) != 0) + { + idlength = strlen(query_firstline); + if (strncmp(buffer,query_firstline, idlength) != 0) idlength = -1; + } + if (idlength < 0) + { + rviewErrorbox::reportError(lman->lookup("errorQueryFile"), rviewQuery::getFrameName(), "loadQuery"); + delete [] buffer; + return FALSE; + } + twin->Clear(); + twin->WriteText(buffer + idlength + 1); + delete [] buffer; + return TRUE; +} + + +bool rviewQuery::saveQuery(char *file) +{ + FILE *fp; + char *buffer, *b, *upper; + + if ((fp = fopen(file, "wb")) == NULL) + { + char msg[STRINGSIZE]; + sprintf(msg, "%s %s", lman->lookup("errorFileOpen"), file); + rviewErrorbox::reportError(msg, rviewQuery::getFrameName(), "saveQuery"); + return FALSE; + } + + fprintf(fp, query_firstline); fputc('\n', fp); + buffer = twin->GetContents(); + b = buffer; upper = buffer + strlen(buffer); + + while (b < upper) + { +#ifdef __VISUALC__ + if (*b != '\r') +#endif + if (fputc(*b, fp) != *b) break; + b++; + } + if (b != upper) + { + char msg[STRINGSIZE]; + sprintf(msg, "%s %s", lman->lookup("errorFileWrite"), file); + rviewErrorbox::reportError(msg, rviewQuery::getFrameName(), "saveQuery"); + fclose(fp); + return FALSE; + } + + fclose(fp); + + delete [] buffer; + + return TRUE; +} diff --git a/applications/rview/rviewQuery.hh b/applications/rview/rviewQuery.hh new file mode 100644 index 0000000..2a18fbc --- /dev/null +++ b/applications/rview/rviewQuery.hh @@ -0,0 +1,128 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView query shell. Handles editing, loading, saving and execution + * of RasQL queries. Actual database accesses are performed through + * class rView's member functions. + * + * COMMENTS: + * None + */ + + + +#ifndef _RVIEW_QUERY_H_ +#define _RVIEW_QUERY_H_ + + +#ifdef __GNUG__ +#pragma interface +#endif + + + +#include "rviewUtils.hh" +#include "rviewDb.hh" + + + + + +class rviewQuery: public rviewFrame +{ + public: + + rviewQuery(rviewDatabase *db, char *query=NULL); + ~rviewQuery(void); + + void OnSize(int w, int h); + void OnMenuCommand(int id); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + + int userEvent(const user_event &ue); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + // Returns the query window ID + int getIdentifier(void) const; + int getQueryCounter(void) const; + + + protected: + + void buildMenubar(void); + bool loadQuery(char *file); + bool saveQuery(char *file); + void newDBState(bool newState); + void updateTitle(void); + + wxTextWindow *twin; + wxMenuBar *mbar; + wxMenu *mbarMenus[3]; + wxPanel *panel; + rviewButton *butClear, *butExec, *butUpdt; + wxFont *font; + int hotNumber; + DynamicString hotPath; + DynamicString lastHotPath; + rviewDatabase *queryDb; + // For update queries + rviewDisplay *updateDisplay; + r_Ref<r_GMarray> updateMddObj; + int qwindowID; + int updateID; + + static const char query_extension[]; + static const char query_firstline[]; + + static const keyword_to_ident_c fontNameTab[]; + static const keyword_to_ident_c fontStyleTab[]; + static const keyword_to_ident_c fontWeightTab[]; + + // constants + // Width and height of query window + static const int query_width; + static const int query_height; + // Borders in query window + static const int query_border; + // Height of control area at the bottom of the query + static const int query_bottom; + // Button dimensions + static const int query_bwidth; + static const int query_bheight; + + + private: + + // Query window counter, realises a unique integer ID for each + // query window. + static int queryCounter; +}; + +#endif diff --git a/applications/rview/rviewSound.cpp b/applications/rview/rviewSound.cpp new file mode 100644 index 0000000..338e1a5 --- /dev/null +++ b/applications/rview/rviewSound.cpp @@ -0,0 +1,1738 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView sound ``viewer'' for playing back MDD as audio samples. + * This file consists of two parts: the highly platform dependent + * SoundPlayer class providing the actual low-level sound playback + * and the general purpose wxWindows class rviewSoundPlayer that + * implements a typical rView display mode window and playback + * controls. + * The SoundPlayer class currently has implementations for Solaris + * and WindowsNT. Supporting other Unix brands only requires + * changes to SoundPlayer::configureDevice, supporting other + * platforms is considerably more complicated (see Unix vs. NT + * implementations). + * + * COMMENTS: + * None + */ + + + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + +#ifndef __HAL_ONLY__ +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + +#endif // HAL + + +/* All platforms */ +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <math.h> + +#include <iostream.h> + +/* Platform class (e.g. Unix) */ +#ifndef __VISUALC__ + +#include <unistd.h> +#include <fcntl.h> + +/* Platform specific */ +#ifdef __sun +#include <multimedia/libaudio.h> +#endif + +#endif + + +#ifndef __HAL_ONLY__ +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + +#include "raslib/rmdebug.hh" +#include "raslib/primitivetype.hh" +#include "raslib/minterval.hh" +//#include "rasodmg/odmgtypes.hh" +#include "rasodmg/marray.hh" +#include "rasodmg/ref.hh" + +#endif // HAL + +#include "rviewSound.hh" +#ifndef __HAL_ONLY__ +#include "rviewTypes.hh" +#include "rviewPrefs.hh" +#include "labelManager.hh" +#endif // HAL + + + + +/* Minimum size of ulaw table */ +const int ULAW_LD_TABLE_MIN=10; +/* log2 of the length of a linear segment in ulaw coding */ +const int ULAW_LD_LINEAR_SIZE=7; + +// general debug messages (should only enable on Unix) +//#define SOUND_PLAYER_DEBUG +// Debug output to file? +//#define SOUND_DEBUG_FILE "Z:\\dehmel\\rview\\slog" + + + +const int rviewSoundPlayer::sound_bwidth = 50; +const int rviewSoundPlayer::sound_bheight = 30; +const int rviewSoundPlayer::sound_sheight = 50; +const int rviewSoundPlayer::sound_twidth = 120; +const int rviewSoundPlayer::sound_theight = 50; +const int rviewSoundPlayer::sound_cwidth = 120; +const int rviewSoundPlayer::sound_cheight = 30; +const int rviewSoundPlayer::sound_ctrly = rviewSoundPlayer::sound_bheight + rviewSoundPlayer::sound_theight + rviewSoundPlayer::sound_sheight + 5*rviewDisplay::display_border; +const int rviewSoundPlayer::sound_width = rviewDisplay::display_width; +const int rviewSoundPlayer::sound_height = rviewDisplay::display_cheight + rviewSoundPlayer::sound_ctrly + 2*rviewDisplay::display_border; +const int rviewSoundPlayer::sound_latencies = 11; + + + + +/* Global variables and functions */ +static soundPlayer *activePlayer = NULL; +static int inCallback = 0; + +#ifdef SOUND_DEBUG_FILE +static FILE *debugfd=NULL; +#endif + + +#ifdef __VISUALC__ +VOID CALLBACK handle_sound_timer(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime) +{ +#ifdef SOUND_PLAYER_DEBUG + cout << "timer called, time " << dwTime << endl; +#endif + + if ((activePlayer != NULL) && (inCallback == 0)) + { + inCallback = 1; + activePlayer->writeSamples(dwTime); + inCallback = 0; + } +} + +#else + +static void handle_sound_timer(int signal) +{ +#ifdef SOUND_PLAYER_DEBUG + cout << "timer called " << signal << ", player " << (void*)activePlayer << ", inCallback " << inCallback << endl; +#endif + + if ((activePlayer != NULL) && (inCallback == 0)) + { + inCallback = 1; + activePlayer->writeSamples(); + inCallback = 0; + } +} +#endif + + + + +/* + * soundPlayer: low-level sound driver, hardware abstraction layer + */ + +soundPlayer::soundPlayer(void) +{ + setupVariables(); +} + +soundPlayer::soundPlayer(int frq, int ch, FILE *fp, rviewSoundFormat fmt, int lat) +{ + setupVariables(); + newSample(frq, ch, fp, fmt, lat); +} + +soundPlayer::soundPlayer(int frq, int ch, const signed char *data, int len, rviewSoundFormat fmt, int lat) +{ + setupVariables(); + newSample(frq, ch, data, len, fmt, lat); +} + +soundPlayer::soundPlayer(int frq, int ch, const unsigned char *data, int len, rviewSoundFormat fmt, int lat) +{ + setupVariables(); + newSample(frq, ch, data, len, fmt, lat); +} + +soundPlayer::soundPlayer(int frq, int ch, const short *data, int len, rviewSoundFormat fmt, int lat) +{ + setupVariables(); + newSample(frq, ch, data, len, fmt, lat); +} + +soundPlayer::~soundPlayer(void) +{ + playbackStop(); + if (buffer != NULL) delete [] buffer; + if (convBuff != NULL) delete [] convBuff; + if (LinToUlaw != NULL) delete [] LinToUlaw; + if (UlawToLin != NULL) delete [] UlawToLin; +} + +void soundPlayer::setupVariables(void) +{ + buffer = NULL; convBuff = NULL; inData = NULL; sampleFile = NULL; suspendedPlayer = NULL; + LinToUlaw = NULL; UlawToLin = NULL; timerActive = FALSE; loopMode = 0; +#ifdef __VISUALC__ + waveOut = (HWAVEOUT)0; timerID = 0; + for (int i=0; i<RVIEW_SND_BUFFERS; i++) waveHdrs[i].lpData = NULL; +#else + audioDevice = -1; +#endif + +#ifdef SOUND_DEBUG_FILE + if (debugfd == NULL) debugfd = fopen(SOUND_DEBUG_FILE, "w"); +#endif +} + +void soundPlayer::playbackStop(void) +{ +#ifdef SOUND_PLAYER_DEBUG + cout << "soundPlayer::playbackStop" << endl; +#endif + + stopTimer(); + +#ifdef __VISUALC__ + if (waveOut != (HWAVEOUT)0) + { + // Reset is CRITICALLY important here! Otherwise playback won't work again + // until you quit and restart your app. + waveOutReset(waveOut); waveOutClose(waveOut); waveOut = (HWAVEOUT)0; + } + freeWaveHeaders(); +#else + if (audioDevice != -1) + { + close(audioDevice); audioDevice = -1; + } +#endif + inData = NULL; sampleFile = NULL; +} + +int soundPlayer::newSample(int frq, int ch, FILE *fp, rviewSoundFormat fmt, int lat) +{ + if (configureDevice(frq, ch, -1, fmt, lat) != 0) return -1; + sampleFile = fp; + return 0; +} + +int soundPlayer::newSample(int frq, int ch, const signed char *data, int len, rviewSoundFormat fmt, int lat) +{ + if (configureDevice(frq, ch, len, fmt, lat) != 0) return -1; + inData = (char*)data; + return 0; +} + +int soundPlayer::newSample(int frq, int ch, const unsigned char *data, int len, rviewSoundFormat fmt, int lat) +{ + if (configureDevice(frq, ch, len, fmt, lat) != 0) return -1; + inData = (char*)data; + return 0; +} + +int soundPlayer::newSample(int frq, int ch, const short *data, int len, rviewSoundFormat fmt, int lat) +{ + if (configureDevice(frq, ch, len, fmt, lat) != 0) return -1; + inData = (char*)data; + return 0; +} + +int soundPlayer::playbackActive(void) +{ + if ((sampleFile != NULL) || (inData != NULL)) return dataOffset; + + return -1; +} + +int soundPlayer::playbackGetOffset(void) +{ + if ((sampleFile == NULL) && (inData == NULL)) return -1; + return dataOffset; +} + +void soundPlayer::ensureUlawTable(int ulawsize) +{ + int i, j, shift, range, sign, base, vsh; + unsigned char val; + unsigned char *u; + + if (LinToUlaw != NULL) + { + if (ldUlawSize >= ulawsize) return; + delete [] LinToUlaw; + } + if (ulawsize < ULAW_LD_TABLE_MIN) ldUlawSize = ULAW_LD_TABLE_MIN; else ldUlawSize = ulawsize; + LinToUlaw = new unsigned char[(1<<ldUlawSize)]; + + range = 255 << (ULAW_LD_LINEAR_SIZE + ldUlawSize - 16); + u = LinToUlaw + (1<<(ldUlawSize-1)) - range; + + for (i=-8; i<8; i++) + { + if (i < 0) + { + shift = -1-i; sign = -1; base = 127 - 16*shift - 15; + } + else + { + shift = i; sign = 1; base = 255 - 16*shift; + } + vsh = ULAW_LD_LINEAR_SIZE + ldUlawSize + shift - 20; + if (vsh < 0) + { + vsh = -vsh; + for (j=0; j<(1<<(ULAW_LD_LINEAR_SIZE + ldUlawSize + shift - 16)); j++) + { + val = j << vsh; + val = base - sign * val; + *u++ = val; + } + } + else + { + for (j=0; j<(1<<(ULAW_LD_LINEAR_SIZE + ldUlawSize + shift - 16)); j++) + { + val = j >> vsh; + val = base - sign * val; + *u++ = val; + } + } + } + i = (1<<(ldUlawSize-1)) - range; + memset(LinToUlaw, LinToUlaw[i], i-1); + memset(LinToUlaw + (1<<ldUlawSize) - i, LinToUlaw[(1<<ldUlawSize) - i - 1], i); + + /*for (i=0; i<(1<<(ldUlawSize-4)); i++) + { + printf("%4x: ", i*16); + for (j=0; j<16; j++) + { + printf("%2x ", LinToUlaw[16*i + j]); + } + printf("\n"); + }*/ +} + +void soundPlayer::ensureLinearTable(void) +{ + int i, j; + short *data; + + if (UlawToLin != NULL) return; + + UlawToLin = new short[256]; + data = UlawToLin; + + for (i=0; i<128; i++) + { + j = ((127 - i) >> 4); + *data++ = -((((1 << j) - 1) << ULAW_LD_LINEAR_SIZE) + ((16-(i&15)) << (j + ULAW_LD_LINEAR_SIZE - 4))); + } + for (i=128; i<256; i++) + { + j = ((255 - i) >> 4); + *data++ = ((((1 << j) -1) << ULAW_LD_LINEAR_SIZE) + ((15-(i&15)) << (j + ULAW_LD_LINEAR_SIZE - 4))); + } + + /*for (i=0; i<32; i++) + { + printf("%4x: ", i*8); + for (j=0; j<8; j++) + { + printf("%6d ", UlawToLin[8*i + j]); + } + printf("\n"); + }*/ +} + +char *soundPlayer::ensureConvBuff(int size) +{ + if (convBuff == NULL) + { + convBuff = new char [size]; cbuffSize = size; + } + else + { + if (cbuffSize < size) + { + delete [] convBuff; convBuff = new char [size]; cbuffSize = size; + } + } + return convBuff; +} + +char *soundPlayer::ensureSampleBuff(int size) +{ + if (buffer == NULL) + { + buffer = new char[size]; buffSize = size; + } + else + { + if (buffSize < size) + { + delete [] buffer; buffer = new char[size]; buffSize = size; + } + } + return buffer; +} + +const char *soundPlayer::ensureSamplesForDevice(const char *source, int len) +{ + int needSize; + int needSamples; + int i; + +#ifdef SOUND_PLAYER_DEBUG + cout << "soundPlayer::ensureSamplesForDevice" << endl; +#endif + + if (format == devFormat) return source; + + needSamples = len * channels; needSize = len * devSampSize; + + ensureConvBuff(needSize); + + // Device is 8 bit signed linear (mean at 0) + if (devFormat == rsf_lin8) + { + signed char *dest = (signed char*)convBuff; + + if (format == rsf_ulin8) + { + const unsigned char *src = (const unsigned char*)source; + + for (i=0; i<needSamples; i++) dest[i] = (signed char)(src[i] - 0x80); + } + else if (format == rsf_ulaw8) + { + const unsigned char *src = (const unsigned char*)source; + + ensureLinearTable(); + for (i=0; i<needSamples; i++) dest[i] = (signed char)(UlawToLin[src[i]] >> 8); + } + else if (format == rsf_lin16) + { + const short *src = (const short *)source; + + for (i=0; i<needSamples; i++) dest[i] = (signed char)(src[i] >> 8); + } + } + // Device is 8 bit unsigned linear (mean at 0x80) + else if (devFormat == rsf_ulin8) + { + unsigned char *dest = (unsigned char*)convBuff; + + if (format == rsf_lin8) + { + const signed char *src = (const signed char*)source; + + for (i=0; i<needSamples; i++) dest[i] = (unsigned char)(src[i]) + 0x80; + } + else if (format == rsf_ulaw8) + { + const unsigned char *src = (const unsigned char*)source; + + ensureLinearTable(); + for (i=0; i<needSamples; i++) dest[i] = (unsigned char)((UlawToLin[src[i]] >> 8) + 0x80); + } + else if (format == rsf_lin16) + { + const short *src = (const short*)source; + + for (i=0; i<needSamples; i++) dest[i] = (short)((src[i] >> 8) + 0x80); + } + } + // Device is 8 bit ulaw + else if (devFormat == rsf_ulaw8) + { + unsigned char *dest = (unsigned char*)convBuff; + + if (format == rsf_lin8) + { + const signed char *src = (const signed char*)source; + short val; + + ensureUlawTable(8); + + for (i=0; i<needSamples; i++) + { + val = (src[i] << 8); + dest[i] = LinToUlaw[(1<<(ldUlawSize-1)) + (val >> (16 - ldUlawSize))]; + } + } + else if (format == rsf_ulin8) + { + const unsigned char *src = (const unsigned char*)source; + short val; + + ensureUlawTable(8); + + for (i=0; i<needSamples; i++) + { + val = (src[i] << 8) - 0x8000; + dest[i] = LinToUlaw[(1<<(ldUlawSize-1)) + (val >> (16 - ldUlawSize))]; + } + } + else if (format == rsf_lin16) + { + const short *src = (const short*)source; + short val; + + ensureUlawTable(14); + + for (i=0; i<needSamples; i++) + { + val = src[i]; + dest[i] = LinToUlaw[(1<<(ldUlawSize-1)) + (val >> (16 - ldUlawSize))]; + } + } + } + // Device is 16 bit linear + else if (devFormat == rsf_lin16) + { + short *dest = (short*)convBuff; + + if (format == rsf_lin8) + { + const signed char *src = (const signed char*)source; + + for (i=0; i<needSamples; i++) dest[i] = (short)(src[i] << 8); + } + else if (format == rsf_ulin8) + { + const unsigned char *src = (const unsigned char*)source; + + for (i=0; i<needSamples; i++) dest[i] = (short)((src[i] << 8) - 0x8000); + } + else if (format == rsf_ulaw8) + { + const unsigned char *src = (const unsigned char *)source; + + ensureLinearTable(); + for (i=0; i<needSamples; i++) dest[i] = (short)(UlawToLin[src[i]]); + } + } + + return convBuff; +} + +const char *soundPlayer::ensureSamples(int &num) +{ + const char *result; + char *b; + + if (sampleFile != NULL) + { + ensureSampleBuff(num * sampleSize); +#ifdef SOUND_PLAYER_DEBUG + cout << "read " << num << " samples from " << (void*)sampleFile << endl; +#endif + if (loopMode == 0) + { + num = fread(buffer, sampleSize, num, sampleFile); + dataOffset += num; + } + else + { + int total, dsize; + + b = buffer; total = num; + while (total > 0) + { + dsize = fread(b, sampleSize, total, sampleFile); + dataOffset += dsize; + if (dsize < total) + { + fseek(sampleFile, 0, SEEK_SET); dataOffset = 0; + } + b += dsize * sampleSize; total -= dsize; + } + } + if (handleOutOfData(num) != 0) + { + sampleFile = NULL; return NULL; + } + result = ensureSamplesForDevice(buffer, num); + } + else if (inData != NULL) + { + if (loopMode == 0) + { + if (num + dataOffset > inLength) num = inLength - dataOffset; + result = (char*)inData + dataOffset * sampleSize; + dataOffset += num; + } + else + { + int total, dsize; + + ensureSampleBuff(num * sampleSize); + b = buffer; total = num; + while (total > 0) + { + dsize = inLength - dataOffset; + if (dsize > total) dsize = total; + memcpy(b, inData + dataOffset * sampleSize, dsize * sampleSize); + dataOffset += dsize; if (dataOffset >= inLength) dataOffset = 0; + b += dsize * sampleSize; total -= dsize; + } + result = buffer; + } + if (handleOutOfData(num) != 0) + { + inData = NULL; return NULL; + } + result = ensureSamplesForDevice(result, num); + } + else + { + playbackStop(); result = NULL; num = 0; + } + + return result; +} + +int soundPlayer::handleOutOfData(int dataSize) +{ + // If there is data, do nothing. + if (dataSize > 0) return 0; + +#ifdef __VISUALC__ + // On Windows we have to make sure both buffers are played to the end. + // Since the new one is all empty that leaves only the current one, + // therefore delay by 1 buffer. + if (emptyBuffers < 1) + { + emptyBuffers++; return 0; + } +#endif + // On Unix we can stop immediately + playbackStop(); + return 1; +} + +int soundPlayer::playbackSetPosition(int position) +{ + int oldPosition = -1; + + oldPosition = dataOffset; + + if (position >= 0) + { + if (sampleFile != NULL) + { + fseek(sampleFile, position, SEEK_SET); + dataOffset = (int)ftell(sampleFile); + } + else + { + dataOffset = (position < inLength) ? position : inLength-1; + } + } + return oldPosition; +} + +int soundPlayer::playbackLoopMode(int lpMode) +{ + int oldLoop = loopMode; + + loopMode = lpMode; + + return oldLoop; +} + + +#ifdef __VISUALC__ +void soundPlayer::freeWaveHeaders(void) +{ + int i; + + for (i=0; i<RVIEW_SND_BUFFERS; i++) + { + if (waveHdrs[i].lpData != NULL) + { + waveOutUnprepareHeader(waveOut, waveHdrs + i, sizeof(WAVEHDR)); + delete [] waveHdrs[i].lpData; waveHdrs[i].lpData = NULL; + } + } +} +#endif + +int soundPlayer::configureDevice(int frq, int ch, int len, rviewSoundFormat fmt, int lat) +{ + // only allow 1 active player at a time. + if ((activePlayer != NULL) && (activePlayer != this)) + { + cerr << "Another player is already active" << endl; + return -1; + } + + playbackStop(); + + frequency = frq; latency = lat; channels = ch; format = fmt; inLength = len; + samplesWritten = 0; dataOffset = 0; + samplesWriteahead = (frequency * latency) / 1000; + +#ifdef __VISUALC__ + devFormat = rsf_lin16; +#else +#ifdef __sun + if ((audioDevice = open("/dev/audio", O_WRONLY)) != -1) + { + Audio_hdr head; + + if (fmt == rsf_ulaw8) + { + head.encoding = AUDIO_ENCODING_ULAW; + devFormat = rsf_ulaw8; + head.bytes_per_unit = 1; + } + else + { + head.encoding = AUDIO_ENCODING_LINEAR; + devFormat = rsf_lin16; + head.bytes_per_unit = 2; + } + head.sample_rate = frequency; + head.channels = channels; + head.samples_per_unit = 1; + head.data_size = AUDIO_UNKNOWN_SIZE; + head.endian = AUDIO_ENDIAN_BIG; + audio_set_play_config(audioDevice, &head); + } +#endif + if (audioDevice == -1) + { + cerr << "Unable to initialize audio device!" << endl; + return -1; + } +#endif + + switch (format) + { + case rsf_lin8: + case rsf_ulin8: + case rsf_ulaw8: sampleSize = 1; break; + case rsf_lin16: sampleSize = 2; break; + default: sampleSize = 0; break; + } + sampleSize *= channels; + switch (devFormat) + { + case rsf_lin8: + case rsf_ulin8: + case rsf_ulaw8: devSampSize = 1; break; + case rsf_lin16: devSampSize = 2; break; + default: devSampSize = 0; + } + devSampSize *= channels; + +#ifdef SOUND_PLAYER_DEBUG + cout << "sampleSize " << sampleSize << ", devSampSize " << devSampSize << ", writeahead " << samplesWriteahead << endl; +#endif + +#ifdef __VISUALC__ + waveFmt.wFormatTag = WAVE_FORMAT_PCM; + waveFmt.nChannels = channels; + waveFmt.nSamplesPerSec = frequency; + waveFmt.nAvgBytesPerSec = 2*channels*frequency; + waveFmt.nBlockAlign = 2*channels; + waveFmt.wBitsPerSample = 16*channels; + waveFmt.cbSize = 0; + + int i; + + if ((i = waveOutOpen(&waveOut, WAVE_MAPPER, &waveFmt, 0, 0, WAVE_FORMAT_QUERY)) != MMSYSERR_NOERROR) + { + cerr << "Unable to initialize audio device! (" << i << ")" << endl; + return -1; + } + waveOutOpen(&waveOut, WAVE_MAPPER, &waveFmt, 0, 0, CALLBACK_NULL | WAVE_FORMAT_DIRECT); + + freeWaveHeaders(); + + for (i=0; i<RVIEW_SND_BUFFERS; i++) + { + waveHdrs[i].lpData = (LPSTR)(new char[samplesWriteahead * devSampSize]); + waveHdrs[i].dwBufferLength = (DWORD)(samplesWriteahead * devSampSize); + //waveOutPrepareHeader(waveOut, waveHdrs + i, sizeof(WAVEHDR)); + // Init flags to "done" for writeSamples + waveHdrs[i].dwFlags = WHDR_DONE; + } + currentHeader = 0; emptyBuffers = 0; + +#endif + + return startTimer(); +} + + +void soundPlayer::playbackSuspend(void) +{ + stopTimer(0); +#ifdef __VISUALC__ + // NT!!! If you issue these calls playback gets _seriously_ screwed up later on. + //waveOutPause(waveOut); +#endif +} + + +void soundPlayer::playbackResume(void) +{ + samplesWritten = 0; +#ifdef __VISUALC__ + //waveOutRestart(waveOut); +#endif + startTimer(0); +} + + +#ifdef __VISUALC__ +// VISUALC +int soundPlayer::setTimerInterval(unsigned int ti) +{ + if (ti == 0) + { + if (timerID != 0) + { + if (!KillTimer(NULL, timerID)) return 1; + timerID = 0; + } + } + else + { + // ti is in us! + if (timerID == 0) + { + timerID = SetTimer(NULL, 1, (UINT)ti/1000, (TIMERPROC)handle_sound_timer); + if (timerID == 0) return 1; + } + } + + timerActive = (ti != 0); + + return 0; +} + +#else +// UNIX only +int soundPlayer::setTimerInterval(unsigned int ti) +{ + struct itimerval value; + struct sigaction act; + int res; + + act.sa_handler = handle_sound_timer; + act.sa_flags = SA_RESTART; + sigaction(SIGALRM, &act, &oact); + value.it_interval.tv_sec = 0; + value.it_interval.tv_usec = ti; + value.it_value.tv_sec = 0; + value.it_value.tv_usec = ti; + res = setitimer(ITIMER_REAL, &value, &ovalue); + + timerActive = (ti != 0); + + return res; +} +#endif + + +int soundPlayer::startTimer(int ap) +{ + unsigned int period; + +#ifdef SOUND_PLAYER_DEBUG + cout << "soundPlayer::startTimer" << endl; +#endif + + if (timerActive) return -1; + + if (ap != 0) + { + if ((activePlayer != this) && (activePlayer != NULL)) + { + activePlayer->playbackSuspend(); + suspendedPlayer = activePlayer; + } + } + activePlayer = this; +#ifdef __VISUALC__ + lastSyncTime = GetTickCount(); +#else + struct timezone tzp; + gettimeofday(&lastSyncTime, &tzp); +#endif + + // period in us + period = (unsigned int)(1000 * latency * RVIEW_SND_RELPERIOD); + if (setTimerInterval(period) != 0) + { + cerr << "Unable to set up timer!" << endl; + return -1; + } + +#ifdef SOUND_PLAYER_DEBUG + cout << "started timer with period " << period << endl; +#endif + + return 0; +} + +int soundPlayer::stopTimer(int ap) +{ + if (!timerActive) return -1; + + setTimerInterval(0); + timerActive = FALSE; + + activePlayer = NULL; + if (ap != 0) + { + if (suspendedPlayer != NULL) + { + activePlayer = suspendedPlayer; suspendedPlayer = NULL; + activePlayer->playbackResume(); + } + } + return 0; +} + +#ifdef __VISUALC__ +void soundPlayer::writeSamples(DWORD systime) +#else +void soundPlayer::writeSamples(void) +#endif +{ + int samplesNeeded; +#ifdef __VISUALC__ + samplesNeeded = ((systime - lastSyncTime) * frequency) / 1000; +#else + struct timeval tp; + struct timezone tzp; + float usecs; + + gettimeofday(&tp, &tzp); + usecs = (float)(tp.tv_usec - lastSyncTime.tv_usec); + samplesNeeded = (tp.tv_sec - lastSyncTime.tv_sec) * frequency + (int)((usecs * frequency) / 1e6); +#endif + +#ifdef SOUND_PLAYER_DEBUG + cout << "soundPlayer::writeSamples" << endl; +#endif + +#ifdef SOUND_DEBUG_FILE + fprintf(debugfd, "%d: written: %d, needed: %d, deltawa: %d\n", systime, samplesWritten, samplesNeeded, samplesWritten - samplesNeeded - samplesWriteahead); +#endif + + if ((samplesWritten - samplesNeeded) >= samplesWriteahead) + { +#ifdef SOUND_PLAYER_DEBUG + cout << "soundPlayer: stalled" << endl; +#endif + samplesWritten = (samplesWritten - samplesNeeded); +#ifdef __VISUALC__ + lastSyncTime = systime; +#else + lastSyncTime.tv_sec = tp.tv_sec; lastSyncTime.tv_usec = tp.tv_usec; +#endif + +#ifdef SOUND_DEBUG_FILE + fprintf(debugfd, "stalled\n"); +#endif + } + else + { + int num; + +#ifdef SOUND_PLAYER_DEBUG + cout << "written " << samplesWritten << ", needed " << samplesNeeded << endl; +#endif + + // Underflow? + if ((samplesWritten - samplesNeeded) < 0) + { + samplesWritten = samplesNeeded; +#ifdef SOUND_DEBUG_FILE + fprintf(debugfd, "underflow\n"); +#endif + } + //num = samplesWritten - samplesNeeded; + num = samplesWriteahead; + if (num != 0) + { + const char *newSamples; + +#ifdef SOUND_PLAYER_DEBUG + cout << "samples " << num << ", device " << endl; +#endif + if ((newSamples = ensureSamples(num)) != NULL) + { +#ifdef __VISUALC__ + int bsize = waveHdrs[currentHeader].dwBufferLength; + +#ifdef SOUND_DEBUG_FILE + fprintf(debugfd, "need %d, buff %d, flags %x\n", num, bsize, waveHdrs[currentHeader].dwFlags); +#endif + // If the buffer is still being used we do nothing. + if ((waveHdrs[currentHeader].dwFlags & (WHDR_INQUEUE | WHDR_DONE)) != WHDR_DONE) return; +#ifdef SOUND_DEBUG_FILE + fprintf(debugfd, "fill Buffer %d\n", currentHeader); +#endif + //waveOutUnprepareHeader(waveOut, waveHdrs + currentHeader, sizeof(WAVEHDR)); + // must prepare the header here or no playback. + //waveHdrs[currentHeader].dwFlags = 0; + waveOutPrepareHeader(waveOut, waveHdrs + currentHeader, sizeof(WAVEHDR)); + memcpy(waveHdrs[currentHeader].lpData, newSamples, num * devSampSize); + // Fill rest of buffer with the correct 0 value + if (num * devSampSize != bsize) + { + int fillValue = 0; + + memset(waveHdrs[currentHeader].lpData + num * devSampSize, fillValue, bsize - num * devSampSize); + } + waveOutWrite(waveOut, waveHdrs + currentHeader, sizeof(WAVEHDR)); + currentHeader++; if (currentHeader >= RVIEW_SND_BUFFERS) currentHeader = 0; + samplesWritten += samplesWriteahead; +#else + write(audioDevice, newSamples, num * devSampSize); + samplesWritten += num; +#endif + } +#ifdef SOUND_PLAYER_DEBUG + cout << "soundPlayer: wrote " << num << " samples" << endl; +#endif + } + } +} + + + + + +#ifndef __HAL_ONLY__ + +// Descriptor for sound formats +const rviewSoundPlayer::format_desc rviewSoundPlayer::soundFormatDesc[] = { + {"soundFmtLin8", rsf_lin8, 1}, + {"soundFmtUlin8", rsf_ulin8, 1}, + {"soundFmtUlaw8", rsf_ulaw8, 1}, + {"soundFmtLin16", rsf_lin16, 2}, + {NULL, rsf_none, 0} +}; + + +/* + * rviewSoundPlayer: sound player widget, platform independent. + */ + +rviewSoundPlayer::rviewSoundPlayer(mdd_frame *mf, unsigned int flags) : rviewDisplay(mf, sound_ctrly, flags) +{ + int w, h; + char buffer[STRINGSIZE]; + char **latStr, **fmtStr; + int fnumLin8, fnumUlaw8, fnumLin16; + char *b; + int i; + + RMDBGENTER(3, RMDebug::module_applications, "rviewSoundPlayer", "rviewSoundPlayer()"); + +#if 0 + { + dimMDD = 1; interv = r_Minterval(1); + //interv << r_Sinterval(r_Range(0), r_Range(41744)); + //#define SAMPLE_NAME "death.sam" + //interv << r_Sinterval(r_Range(0), r_Range(79842)); + //#define SAMPLE_NAME "cyber.sam" + interv << r_Sinterval(r_Range(0), r_Range(77071)); + #define SAMPLE_NAME "willy.sam" + r_Ref<r_Marray<r_Octet> > mddPtr = new r_Marray<r_Octet>(interv); + mddPtr->set_type_by_name("OctetString"); + sprintf(buffer, "marray <octet, [0:%d]>", interv[0].high()); + mddPtr->set_type_structure(buffer); + char *data = mddPtr->get_array(); +#ifdef __VISUALC__ + FILE *fp = fopen("Z:\\dehmel\\rview\\samples\\" SAMPLE_NAME, "rb"); +#else + FILE *fp = fopen("samples" DIR_SEPARATOR SAMPLE_NAME, "rb"); +#endif + if (fp != NULL) + { + fread(data, 1, interv[0].high() - interv[0].low() + 1, fp); fclose(fp); + } + else + { + memset(data, 0x80, interv[0].high() - interv[0].low() + 1); + } + mddObj = (r_Ref<r_GMarray>)mddPtr; baseType = rbt_uchar; + } +#endif + + sampleBuffer = NULL; lastOffset = 0; paused = FALSE; + typeLength = mddObj->get_type_length(); playbackOn = FALSE; + + // Defaults + frequency = prefs->soundFreq; latency = prefs->soundLatency; loopMode = prefs->soundLoop; + + GetClientSize(&w, &h); + + toStart = new rviewButton(ctrlPanel, "<<"); + toEnd = new rviewButton(ctrlPanel, ">>"); + pbPause = new rviewButton(ctrlPanel, "||"); + pbStart = new rviewButton(ctrlPanel, ">"); + pbStop = new rviewButton(ctrlPanel, "[]"); + pbLoop = new rviewButton(ctrlPanel); + + slider = new rviewSlider(ctrlPanel, 0, 0, 1000, 10, lman->lookup("soundPlayTime")); + frqWidget = new rviewText(ctrlPanel, frequency); + + fnumLin8 = 0; fnumUlaw8 = 0; fnumLin16 = 0; + for (i=0; soundFormatDesc[i].labelName != NULL; i++) + { + switch (soundFormatDesc[i].fmt) + { + case rsf_lin8: fnumLin8 = i; break; + case rsf_ulaw8: fnumUlaw8 = i; break; + case rsf_lin16: fnumLin16 = i; break; + default: break; + } + } + fmtStr = new char *[i]; + for (i=0; soundFormatDesc[i].labelName != NULL; i++) + fmtStr[i] = lman->lookup(soundFormatDesc[i].labelName); + fmtWidget = new rviewChoice(ctrlPanel, i, fmtStr, lman->lookup("soundFormat")); + delete [] fmtStr; + + latencies = new int[sound_latencies]; + for (i=0; i<sound_latencies; i++) latencies[i] = 50 * (i+1); + latStr = new char *[sound_latencies]; + b = buffer; + for (i=0; i<sound_latencies; i++) + { + latStr[i] = b; + b += 1 + sprintf(b, "%dms", latencies[i]); + } + latWidget = new rviewChoice(ctrlPanel, i, latStr, lman->lookup("soundLatency")); + delete [] latStr; + for (i=0; i<sound_latencies-1; i++) + { + if ((latencies[i] <= latency) && (latencies[i+1] > latency)) break; + } + latWidget->SetSelection(i); + + b = projString; + b += sprintf(b, "*:*"); + for (i=1; i<dimMDD; i++) + { + b += sprintf(b, ", %ld", interv[i].low()); + } + project->SetValue(projString); + + // Interpret 2D data as multiple channels + setModeDimension((dimMDD == 1) ? 1 : 2); + + setLoopMode(loopMode); + + // Mustn't create errorbox before all widgets are created. + switch (baseType) + { + case rbt_char: currentFormat = fnumUlaw8; break; + case rbt_uchar: currentFormat = fnumLin8; break; + case rbt_short: currentFormat = fnumLin16; break; + default: + { + rviewErrorbox::reportError(lman->lookup("errorSoundFormat"), rviewSoundPlayer::getFrameName(), "rviewSoundFormat"); + currentFormat = -1; + objectInitializedOK = FALSE; + } + break; + } + if (currentFormat >= 0) fmtWidget->SetSelection(currentFormat); + + RMDBGEXIT(3, RMDebug::module_applications, "rviewSoundPlayer", "rviewSoundPlayer()"); +} + + +int rviewSoundPlayer::openViewer(void) +{ + + RMDBGONCE(3, RMDebug::module_applications, "rviewSoundPlayer", "openViewer()"); + + if (currentFormat < 0) + { + rviewErrorbox::reportError(lman->lookup("errorSoundFormat"), rviewSoundPlayer::getFrameName(), "openViewer"); + objectInitializedOK = FALSE; + return -1; + } + + if (rviewDisplay::openViewer() == 0) + { + int w, h; + + GetClientSize(&w, &h); + + label(); + + frameWidth=-1; + frameHeight=-1; + + OnSize(w, h); + OnSize(w, h); + + Show(TRUE); + + return 0; + } + return -1; +} + + +rviewSoundPlayer::~rviewSoundPlayer(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewSoundPlayer", "~rviewSoundPlayer()"); + closeViewer(); + if (latencies) + { + delete [] latencies; + latencies=0; + } + if (sampleBuffer) + { + delete [] sampleBuffer; + sampleBuffer=0; + } +} + + +const char *rviewSoundPlayer::getFrameName(void) const +{ + return "rviewSoundPlayer"; +} + +rviewFrameType rviewSoundPlayer::getFrameType(void) const +{ + return rviewFrameTypeSound; +} + +int rviewSoundPlayer::getViewerType(void) const +{ + return RVIEW_RESDISP_SOUND; +} + + +bool rviewSoundPlayer::setLoopMode(bool lm) +{ + bool oldMode = loopMode; + + loopMode = lm; + if (loopMode) + { + pbLoop->SetLabel("<-->"); + } + else + { + pbLoop->SetLabel("--->"); + } + player.playbackLoopMode((loopMode) ? 1 : 0); + + return oldMode; +} + + +void rviewSoundPlayer::setSlider(int offset) +{ + slider->SetValue((offset + frequency - 1) / frequency); +} + +void rviewSoundPlayer::label(void) +{ + SetTitle(lman->lookup("titleSound")); + slider->SetLabel(lman->lookup("soundPlayTime")); + frqWidget->SetLabel(lman->lookup("soundFrequency")); + latWidget->SetLabel(lman->lookup("soundLatency")); + + rviewDisplay::label(); +} + +int rviewSoundPlayer::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + RMDBGONCE(3, RMDebug::module_applications, "rviewSoundPlayer", "process(...)"); + + if (rviewDisplay::process(obj, evt) != 0) + { + return 1; + } + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)pbStart) + { + startPlayback(); + return 1; + } + else if (&obj == (wxObject*)pbStop) + { + stopPlayback(); + return 1; + } + else if (&obj == (wxObject*)toStart) + { + player.playbackSetPosition(0); + setSlider(0); + return 1; + } + else if (&obj == (wxObject*)toEnd) + { + player.playbackSetPosition(sampleLength); + setSlider(sampleLength); + return 1; + } + else if (&obj == (wxObject*)pbPause) + { + if (paused) + { + player.playbackResume(); paused = FALSE; + } + else + { + player.playbackSuspend(); paused = TRUE; + } + return 1; + } + else if (&obj == (wxObject*)pbLoop) + { + setLoopMode(!loopMode); + return 1; + } + } + + if (type == wxEVENT_TYPE_SLIDER_COMMAND) + { + if (&obj == (wxObject*)slider) + { + int soff, poff; + + if ((poff = player.playbackGetOffset()) >= 0) + { + soff = slider->GetValue(); + if (soff != poff/frequency) + { + player.playbackSetPosition(soff * frequency); + } + } + return 1; + } + } + + if (type == wxEVENT_TYPE_CHOICE_COMMAND) + { + if (&obj == (wxObject*)fmtWidget) + { + int newFmt; + + newFmt = fmtWidget->GetSelection(); + if ((soundFormatDesc[newFmt].sampleSize != baseSize) && (currentFormat >= 0)) + { + fmtWidget->SetSelection(currentFormat); + } + else + { + currentFormat = newFmt; + } + return 1; + } + } + + return 0; +} + + +void rviewSoundPlayer::OnSize(int w, int h) +{ + int x, y, posx, posy, d; + + RMDBGONCE(3, RMDebug::module_applications, "rviewSoundPlayer", "OnSize( " << w << ", " << h << " )"); + + rviewDisplay::OnSize(w, h); + + GetClientSize(&x, &y); + + //need to resize? + if (( sound_width != x) || ( sound_height != y)) + { + frameWidth = sound_width; + frameHeight = sound_height; + x = sound_width; + y = sound_height; + SetClientSize(x, y); + return; + } + + + d = (x - 7*display_border) / 6; + posx = display_border; posy = display_border + display_cheight; + toStart->SetSize(posx, posy, d, sound_bheight); + posx += d + display_border; + pbPause->SetSize(posx, posy, d, sound_bheight); + posx += d + display_border; + pbStart->SetSize(posx, posy, d, sound_bheight); + posx += d + display_border; + pbStop->SetSize(posx, posy, d, sound_bheight); + posx += d + display_border; + toEnd->SetSize(posx, posy, d, sound_bheight); + posx += d + display_border; + pbLoop->SetSize(posx, posy, d, sound_bheight); + posx = display_border; posy += sound_bheight + display_border; + d = (x - 4*display_border) / 3; + frqWidget->SetSize(posx, posy, d, sound_theight); + fmtWidget->SetSize(posx + d + display_border, posy, 6*d/9, sound_cheight); + latWidget->SetSize(posx + 2*(d + display_border), posy, 5*d/9, sound_cheight); + posy += sound_theight + 2*display_border; + slider->SetSize(display_border, posy, x - 2*display_border, sound_theight); +} + + + +int rviewSoundPlayer::newProjection(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewSoundPlayer", "newProjection()"); + + if (playbackOn) + { + player.playbackStop(); + if (buildSample() != 0) return -1; + if (newSample() != 0) return -1; // implicitly does playbackResume() + } + else + { + return startPlayback(); + } + return 0; +} + + +void rviewSoundPlayer::prepareToDie(void) +{ + //cout << "rviewSoundPlayer::prepareToDie" << endl; + stopPlayback(); + ::wxYield(); +} + + +int rviewSoundPlayer::newSample(void) +{ + int fmt; + int status=0; + + RMDBGONCE(3, RMDebug::module_applications, "rviewSoundPlayer", "newSample()"); + + frequency = atoi(frqWidget->GetValue()); + latency = latencies[latWidget->GetSelection()]; + + fmt = fmtWidget->GetSelection(); + switch (fmt) + { + case 0: + status = player.newSample(frequency, channels, (signed char*)sampleBuffer, sampleLength, rsf_lin8, latency); + break; + case 1: + status = player.newSample(frequency, channels, (unsigned char *)sampleBuffer, sampleLength, rsf_ulin8, latency); + break; + case 2: + status = player.newSample(frequency, channels, (unsigned char *)sampleBuffer, sampleLength, rsf_ulin8, latency); + break; + case 3: + status = player.newSample(frequency, channels, (short *)sampleBuffer, sampleLength, rsf_lin16, latency); + break; + default: + { + rviewErrorbox::reportError(lman->lookup("errorSoundFormat"), rviewSoundPlayer::getFrameName(), "newSample"); + } + return -1; + } + + if (status != 0) + { + rviewErrorbox::reportError(lman->lookup("errorSoundDevice"), rviewSoundPlayer::getFrameName(), "newSample"); + return -1; + } + + setSlider(0); + slider->SetRange(0, (sampleLength + frequency - 1) / frequency); + + return 0; +} + + +int rviewSoundPlayer::buildSample(void) +{ + int stepx, stepy; + union {char *c; short *s;} src, dest, srcBase; + r_Ref<r_Marray<r_Char> > mddPtr = (r_Ref<r_Marray<r_Char> >)mddObj; + char *base; + int i; + + RMDBGONCE(3, RMDebug::module_applications, "rviewSoundPlayer", "buildSample()"); + + strcpy(projString, project->GetValue()); + if (rviewParseProjection(getVirtualDomain(), pt1, pt2, projString, &freeDims) != dimMDD) + { + rviewErrorbox::reportError(lman->lookup("errorProjection"), rviewSoundPlayer::getFrameName(), "buildSample"); + return -1; + } + + dim1 = dimMDD; dim2 = dimMDD; + for (dim1=0; dim1<dimMDD; dim1++) if ((freeDims & (1<<dim1)) != 0) break; + for (dim2=dim1+1; dim2<dimMDD; dim2++) if ((freeDims & (1<<dim2)) != 0) break; + + sampleLength = pt2[dim1] - pt1[dim1] + 1; + + if (sampleBuffer != NULL) + { + delete [] sampleBuffer; sampleBuffer = NULL; + } + + base = mddObj->get_array(); + src.c = base + typeLength * ((char*)(&((*mddPtr)[pt1])) - base); + + r_Point paux(pt1); + paux[dim1]++; + stepx = (int)(&((*mddPtr)[paux]) - &((*mddPtr)[pt1])); + + // One channel or multiple ones? + if (dim2 >= dimMDD) + { + channels = 1; + sampleBuffer = (void*)(new char[sampleLength * typeLength]); + dest.c = (char*)sampleBuffer; + switch (typeLength) + { + case 1: + { + for (i=0; i<sampleLength; i++, src.c += stepx) + { + *dest.c++ = *src.c; + } + } + break; + case 2: + { + for (i=0; i<sampleLength; i++, src.s += stepx) + { + *dest.s++ = *src.s; + } + } + break; + default: + break; + } + } + else + { + int j; + + paux[dim1] = pt1[dim1]; paux[dim2]++; + stepy = (int)(&((*mddPtr)[paux]) - &((*mddPtr)[pt1])); + + channels = pt2[dim2] - pt1[dim2] + 1; + sampleBuffer = (void*)(new char[sampleLength * typeLength * channels]); + dest.c = (char*)sampleBuffer; + srcBase.c = src.c; + + // Multiple channels ==> create channel-interleaved sample data. + switch (typeLength) + { + case 1: + { + for (i=0; i<sampleLength; i++, srcBase.c += stepx) + { + for (j=0, src.c = srcBase.c; j<channels; j++, src.c += stepy) + { + *dest.c++ = *src.c; + } + } + } + break; + case 2: + { + for (i=0; i<sampleLength; i++, srcBase.c += stepx) + { + for (j=0, src.s = srcBase.s; j<channels; j++, src.s += stepy) + { + *dest.s++ = *src.s; + } + } + } + break; + default: + break; + } + } + return 0; +} + + +int rviewSoundPlayer::startPlayback(void) +{ + int offset; + + RMDBGENTER(3, RMDebug::module_applications, "rviewSoundPlayer", "startPlayback()"); + + // no reentrancy + if (playbackOn) return -1; + + if (sampleBuffer == NULL) + { + if (buildSample() != 0) return -1; + } + + newSample(); + + playbackOn = TRUE; paused = FALSE; + + lastOffset = 0; + while ((offset = player.playbackActive()) >= 0) + { + if (abs(offset - lastOffset) >= frequency) + { + slider->SetValue(offset / frequency); + lastOffset = offset; + } + ::wxYield(); + if (!playbackOn) break; + } + if (offset < 0) + { + setSlider(sampleLength); + } + + playbackOn = FALSE; + + return 0; +} + + +int rviewSoundPlayer::stopPlayback(void) +{ + player.playbackStop(); + playbackOn = FALSE; + + return 0; +} + + + + + +#else + +// Testbed +int main(int argc, char *argv[]) +{ + FILE *infile; + soundPlayer *sp; +#ifdef __VISUALC__ + char *lumpname = "samples\\death.sam"; +#else + char *lumpname = "samples/death.sam"; +#endif + int offset, lastOffset; + int frequency; + + if (argc > 1) + { + lumpname = argv[1]; + } + + if ((infile = fopen(lumpname, "rb")) == NULL) + { + cerr << "Couldn't open file " << lumpname << endl; + exit(-1); + } + + cout << "create new player" << endl; + + frequency = 11025; + sp = new soundPlayer(frequency, 1, infile, rsf_ulin8, 500); + + lastOffset = -frequency; + while ((offset = sp->playbackActive()) >= 0) + { + if (abs(offset - lastOffset) >= frequency) + { + printf("Time %ds\r", offset / frequency); fflush(stdout); + lastOffset = offset; + } + } + printf("\n", offset); + + cout << "delete player" << endl; + + delete sp; + + fclose(infile); + + return 0; +} + +#endif // HAL diff --git a/applications/rview/rviewSound.hh b/applications/rview/rviewSound.hh new file mode 100644 index 0000000..e6a156a --- /dev/null +++ b/applications/rview/rviewSound.hh @@ -0,0 +1,274 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView sound ``viewer'' for playing back MDD as audio samples. + * This file consists of two parts: the highly platform dependent + * SoundPlayer class providing the actual low-level sound playback + * and the general purpose wxWindows class rviewSoundPlayer that + * implements a typical rView display mode window and playback + * controls. + * The SoundPlayer class currently has implementations for Solaris + * and WindowsNT. Supporting other Unix brands only requires + * changes to SoundPlayer::configureDevice, supporting other + * platforms is considerably more complicated (see Unix vs. NT + * implementations). + * + * COMMENTS: + * None + */ + + + +#ifndef _RVIEW_SOUND_H_ +#define _RVIEW_SOUND_H_ + + +#include <stdio.h> +#include <signal.h> + +#ifdef __VISUALC__ +#include <windows.h> +#endif + + +#ifdef __HAL_ONLY__ +#ifndef __VISUALC__ +#include "bool.h" +#endif +typedef bool bool; +#ifndef TRUE +#define TRUE true +#define FALSE false +#endif +#else +#include "rviewUtils.hh" +#include "rviewDisplay.hh" +#endif // HAL + + + +/* Default latency of sound playback */ +#define RVIEW_SND_LATENCY 100 +/* Timer interval relative to sample buffer runtime */ +#define RVIEW_SND_RELPERIOD 0.5 + +/* Number of sample buffers (NT only) */ +#define RVIEW_SND_BUFFERS 3 + + +enum rviewSoundFormat { + rsf_none, + rsf_lin8, + rsf_ulin8, + rsf_ulaw8, + rsf_lin16 +}; + + + +/* + * soundPlayer: hardware abstraction layer + */ + +class soundPlayer +{ + public: + + // Base constructor + soundPlayer(void); + // Constructor for input from file + soundPlayer(int frq, int ch, FILE *fp, rviewSoundFormat fmt, int lat=RVIEW_SND_LATENCY); + // Constructor for 8bit linear (default) + soundPlayer(int frq, int ch, const signed char *data, int len, rviewSoundFormat fmt=rsf_lin8, int lat=RVIEW_SND_LATENCY); + // Constructor for 8bit ulaw (default) + soundPlayer(int frq, int ch, const unsigned char *data, int len, rviewSoundFormat fmt=rsf_ulaw8, int lat=RVIEW_SND_LATENCY); + // Constructor for 16bit linear (default) + soundPlayer(int frq, int ch, const short *data, int len, rviewSoundFormat fmt=rsf_lin16, int lat=RVIEW_SND_LATENCY); + + ~soundPlayer(void); + + // For changing during playback + int newSample(int frq, int ch, FILE *fp, rviewSoundFormat fmt, int lat=RVIEW_SND_LATENCY); + int newSample(int frq, int ch, const signed char *data, int len, rviewSoundFormat=rsf_lin8, int lat=RVIEW_SND_LATENCY); + int newSample(int frq, int ch, const unsigned char *data, int len, rviewSoundFormat=rsf_ulaw8, int lat=RVIEW_SND_LATENCY); + int newSample(int frq, int ch, const short *data, int len, rviewSoundFormat=rsf_lin16, int lat=RVIEW_SND_LATENCY); + + int playbackGetOffset(void); + int playbackActive(void); + void playbackSuspend(void); + void playbackResume(void); + void playbackStop(void); + int playbackSetPosition(int position); + int playbackLoopMode(int lpMode); + +#ifdef __VISUALC__ + void writeSamples(DWORD systime); +#else + void writeSamples(void); +#endif + + + protected: + + void setupVariables(void); + const char *ensureSamplesForDevice(const char *source, int len); + const char *ensureSamples(int &num); + int configureDevice(int frq, int ch, int len, rviewSoundFormat fmt, int lat); + void ensureUlawTable(int ulawsize); + void ensureLinearTable(void); + char *ensureConvBuff(int size); + char *ensureSampleBuff(int size); + + int setTimerInterval(unsigned int ti); + int startTimer(int ap=1); + int stopTimer(int ap=1); + int handleOutOfData(int dataSize); + + rviewSoundFormat format; + rviewSoundFormat devFormat; + int sampleSize, devSampSize; + FILE *sampleFile; + int dataOffset, inLength; + int frequency, channels, latency, samplesWriteahead; + int buffSize, cbuffSize; + char *buffer, *convBuff; + const char *inData; + unsigned char *LinToUlaw; + short *UlawToLin; + int ldUlawSize; + int samplesWritten; + soundPlayer *suspendedPlayer; + int loopMode; + bool timerActive; + + // Unix specifics +#ifdef __VISUALC__ + void freeWaveHeaders(void); + + HWAVEOUT waveOut; + WAVEFORMATEX waveFmt; + WAVEHDR waveHdrs[RVIEW_SND_BUFFERS]; + UINT timerID; + DWORD lastSyncTime; + int currentHeader; + int emptyBuffers; +#else + int audioDevice; + struct timeval lastSyncTime; + struct sigaction oact; + struct itimerval ovalue; +#endif +}; + + + +#ifndef __HAL_ONLY__ + +/* + * rviewSoundPlayer: sound player widget + */ + +class rviewSoundPlayer: public rviewDisplay +{ + public: + + rviewSoundPlayer(mdd_frame *mf, unsigned int flags=0); + ~rviewSoundPlayer(void); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + virtual int openViewer(void); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + virtual int getViewerType(void) const; + + void OnSize(int w, int h); + + int newProjection(void); + void prepareToDie(void); + + typedef struct format_desc { + const char *labelName; + rviewSoundFormat fmt; + int sampleSize; + } format_desc; + + // constants + // Sound player window button size + static const int sound_bwidth; + static const int sound_bheight; + // Sound player window slider height + static const int sound_sheight; + // Sound player window text size + static const int sound_twidth; + static const int sound_theight; + // Sount player choice size + static const int sound_cwidth; + static const int sound_cheight; + // Sound player window ctrl area size + static const int sound_ctrly; + // Sound player frame size + static const int sound_width; + static const int sound_height; + // Number of latency entries + static const int sound_latencies; + + + protected: + + int buildSample(void); + int newSample(void); + int startPlayback(void); + int stopPlayback(void); + void setSlider(int offset); + bool setLoopMode(bool lm); + + soundPlayer player; + rviewButton *toStart, *toEnd; + rviewButton *pbPause, *pbStart, *pbStop, *pbLoop; + rviewSlider *slider; + rviewText *frqWidget; + rviewChoice *fmtWidget, *latWidget; + int frequency, channels, latency; + int lastOffset; + int *latencies; + void *sampleBuffer; + int sampleLength; + bool paused; + bool playbackOn; + bool loopMode; + int dim1, dim2; + int typeLength; + int currentFormat; + unsigned int freeDims; + + static const format_desc soundFormatDesc[]; +}; + +#endif // HAL + +#endif diff --git a/applications/rview/rviewStrings.cpp b/applications/rview/rviewStrings.cpp new file mode 100644 index 0000000..bd66bd4 --- /dev/null +++ b/applications/rview/rviewStrings.cpp @@ -0,0 +1,216 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView string viewer. Can display 1D MDD objects as strings + * + * COMMENTS: + * None + */ + + + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + + +#include <string.h> +#include <iostream.h> +#include <ctype.h> + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + + +#include "raslib/rmdebug.hh" +#include "raslib/primitivetype.hh" +#include "raslib/structuretype.hh" + + +#include "rviewTypes.hh" + +#include "labelManager.hh" + +#include "rviewUtils.hh" +#include "rviewDModes.hh" +#include "rviewPrefs.hh" + + +const int rviewStringViewer::strview_msgheight = 30; +const int rviewStringViewer::strview_minwidth = 100; +const int rviewStringViewer::strview_minheight = 64; +const int rviewStringViewer::strview_ctrly = 32; +const int rviewStringViewer::strview_totaly = rviewStringViewer::strview_ctrly + rviewDisplay::display_cheight; + + + +rviewStringViewer::rviewStringViewer(mdd_frame *mf, unsigned int flags) : + rviewDisplay(mf, strview_ctrly, flags) +{ + RMDBGONCE( 3, RMDebug::module_applications, "rviewStringViewer", "rviewStringViewer()" ); + + msgString = new wxMessage(ctrlPanel, ""); + + // init projection string -- this viewer works for 1D only! + strcpy(projString, "*:*"); + project->SetValue(projString); + + setModeDimension(1); + + setMinimumViewerSize(strview_minwidth, strview_minheight); +} + + +int rviewStringViewer::openViewer(void) +{ + RMDBGONCE( 3, RMDebug::module_applications, "rviewStringViewer", "openViewer()" ); + + if (dimMDD == 1) + { + if (baseSize == 1) + { + if (rviewDisplay::openViewer() == 0) + { + int w, h; + + newProjection(); + + label(); + frameWidth = -1; + frameHeight = -1; + + GetClientSize(&w, &h); + + SetSize(w, strview_totaly); + OnSize(w, h); + + Show(TRUE); + + return 0; + } + } + else + { + rviewErrorbox::reportError(lman->lookup("errorBaseType"), getFrameName(), "openViewer()"); + } + } + else + { + rviewErrorbox::reportError(lman->lookup("errorModeDim"), getFrameName(), "openViewer()"); + } + return -1; +} + + +const char *rviewStringViewer::getFrameName(void) const +{ + return "rviewStringViewer"; +} + +rviewFrameType rviewStringViewer::getFrameType(void) const +{ + return rviewFrameTypeStringViewer; +} + +int rviewStringViewer::getViewerType(void) const +{ + return RVIEW_RESDISP_STRVIEW; +} + + +rviewStringViewer::~rviewStringViewer(void) +{ + RMDBGONCE( 3, RMDebug::module_applications, "rviewStringViewer", "~rviewStringViewer()" ); + closeViewer(); +} + + +void rviewStringViewer::OnSize(int w, int h) +{ + int x, y; + + GetClientSize(&x, &y); + + msgString->SetSize(display_border, display_border + display_cheight, x - 2*display_border, strview_msgheight); + + rviewDisplay::OnSize(w, h); +} + + +int rviewStringViewer::newProjection(void) +{ + unsigned int len, i; + const char *b; + char *newMsg; + + mapIndex = r_Point(dimMDD); + if (rviewParseProjection(getVirtualDomain(), pt1, pt2, projString, &freeDims, &mapIndex) != dimMDD) + { + rviewErrorbox::reportError(lman->lookup("errorProjection"), getFrameName(), "newProjection"); + return -1; + } + if ((freeDims & 1) == 0) + { + rviewErrorbox::reportError(lman->lookup("errorProjectFree"), getFrameName(), "newProjection"); + return -1; + } + len = pt2[0] - pt1[0] + 1; + newMsg = new char[len + 1]; + b = mddObj->get_array() + pt1[0]; + // make sure the message is printable + for (i=0; i<len; i++) + { + if (isprint(b[i])) + newMsg[i] = b[i]; + else + newMsg[i] = ' '; + } + newMsg[i] = '\0'; + //cout << "MSG: " << newMsg << endl; + msgString->SetLabel(newMsg); + delete [] newMsg; + + return 0; +} diff --git a/applications/rview/rviewTable.cpp b/applications/rview/rviewTable.cpp new file mode 100644 index 0000000..bd208ce --- /dev/null +++ b/applications/rview/rviewTable.cpp @@ -0,0 +1,770 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView table viewer. Can display MDD objects of any dimension and base type (!) + * as tables. Optional modes are decimal, octal and hex number bases. + * + * COMMENTS: + * None + */ + + + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + + +#include <string.h> +#include <iostream.h> + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + + +#include "raslib/rmdebug.hh" +#include "raslib/primitivetype.hh" +#include "raslib/structuretype.hh" + + +#include "rviewTypes.hh" + +#include "labelManager.hh" + +#include "rviewUtils.hh" +#include "rviewDModes.hh" +#include "rviewPrefs.hh" + + +const int textCanvas::txcanv_cospace = 8; +const int textCanvas::txcanv_colspace = 16; +const int textCanvas::txcanv_border = rviewDisplay::display_border; + +const int rviewTable::table_twidth = 64; +const int rviewTable::table_theight = 50; +const int rviewTable::table_cwidth = 100; +const int rviewTable::table_cheight = 50; +const int rviewTable::table_minwidth = 100; +const int rviewTable::table_minheight = 100; +const int rviewTable::table_ctrly = 64; +const int rviewTable::table_totaly = rviewDisplay::display_cheight + rviewTable::table_ctrly; + + + + +textCanvas::textCanvas(wxWindow *parent, int x, int y, int w, int h, long style) : wxCanvas(parent, x, y, w, h, style) +{ + wxColour fc(0x10, 0x10, 0x10); + wxColour bc(0xf0, 0xf0, 0xf0); + + fore.SetStyle(wxSOLID); + fore.SetColour(fc); + back.SetStyle(wxSOLID); + back.SetColour(bc); + // Don't use wxDEFAULT! Doesn't work on Sun! */ + font = new wxFont(12, wxROMAN, wxNORMAL, wxNORMAL); + SetBackground(&back); + SetTextBackground(&bc); + SetTextForeground(&fc); + pen.SetStyle(wxSOLID); + pen.SetColour(fc); + + scrollX = 0; scrollY = 0; +} + + +textCanvas::~textCanvas(void) +{ + SetBackground(NULL); SetTextBackground(NULL); SetTextForeground(NULL); pen.SetColour(0, 0, 0); + delete font; +} + + +void textCanvas::setData(mdd_frame *mf, rviewBaseType bt, unsigned int bs) +{ + mddObj = mf->mdd; + dimMDD = (int)(mddObj->spatial_domain().dimension()); + baseType = bt; + baseSize = bs; +} + + +void textCanvas::setStep(int sx, int sy) +{ + stepx = sx; stepy = sy; +} + + +void textCanvas::setProjection(r_Point &p1, r_Point &p2, unsigned int fd, r_Point *mapIndex) +{ + pt1 = p1; pt2 = p2; freeDims = fd; + for (dim1=0; dim1<dimMDD; dim1++) if ((freeDims & (1<<dim1)) != 0) break; + for (dim2=dim1+1; dim2<dimMDD; dim2++) if ((freeDims & (1<<dim2)) != 0) break; + + if (dim1 >= dimMDD) + dim1 = -1; + else if (mapIndex != NULL) + dim1 = (*mapIndex)[dim1]; + if (dim2 >= dimMDD) + dim2 = -1; + else if (mapIndex != NULL) + dim2 = (*mapIndex)[dim2]; +} + + +void textCanvas::setCoSys(bool cs, int &cl, int &ct) +{ + cosys = cs; + + if (cosys) + { + char buffer[STRINGSIZE]; + float twidth, theight; + + SetFont(font); + if (dim2 < 0) + { + coleft = txcanv_cospace; + sprintf(buffer, "%ld", pt1[dim1]); + GetTextExtent(buffer, &twidth, &theight); + } + else + { + sprintf(buffer, "%ld", (abs(pt1[dim2]) > abs(pt2[dim2])) ? pt1[dim2] :pt2[dim2]); + GetTextExtent(buffer, &twidth, &theight); + coleft = (int)twidth + txcanv_cospace; + } + cotop = (int)theight + txcanv_cospace; + SetFont(NULL); + } + else + { + coleft = 0; cotop = 0; + } + cl = coleft; ct = cotop; +} + + + +void textCanvas::OnPaint(void) +{ + wxUpdateIterator upd(this); + wxRect rect; + int w, h, x, y; + r_Point prun = pt1; + wxCanvasDC *cdc; + r_Range startOffX, endOffX, startOffY, endOffY; + float posx, posy; + char textbuff[STRINGSIZE]; + bool redrawAll = FALSE; + + //cout << "textCanvas::OnPaint()" << endl; + + if (dim1 < 0) return; + + GetClientSize(&w, &h); + + cdc = GetDC(); + cdc->BeginDrawing(); + cdc->SetMapMode(MM_TEXT); + cdc->SetBrush(&fore); + cdc->SetFont(font); + cdc->SetLogicalFunction(wxCOPY); + + w = GetScrollPos(wxHORIZONTAL); h = GetScrollPos(wxVERTICAL); + x = rviewDisplay::display_scrstep * w; + y = rviewDisplay::display_scrstep * h; + + // In case of scrolling we have to plot the whole thing. There's a bug in the + // current version of wxWindows that means you don't get any rectangles returned + // that were covered by other windows and are now visible. + if ((w != scrollX) || (h != scrollY)) + { + redrawAll = TRUE; + GetClientSize(&rect.width, &rect.height); + rect.x = 0; rect.y = 0; + scrollX = w; scrollY = h; + } + + // Use for, not while, because of continue in loop body + for (; upd ; upd++) + { + if (!redrawAll) upd.GetRect(&rect); + + // cdc->SetClippingRegion(rect.x, rect.y, rect.width, rect.height); + // Calculate the range to plot. + startOffX = (x + rect.x - txcanv_border - coleft) / stepx; + if (startOffX < 0) startOffX = 0; + if (pt1[dim1] + startOffX > pt2[dim1]) continue; + endOffX = (x + rect.x + rect.width - txcanv_border - coleft + stepx - 1) / stepx; + if (endOffX > pt2[dim1] - pt1[dim1]) endOffX = pt2[dim1] - pt1[dim1]; + if (dim2 < 0) + { + startOffY = 0; endOffY = 0; + } + else + { + startOffY = (y + rect.y - txcanv_border - cotop) / stepy; + if (startOffY < 0) startOffY = 0; + if (pt1[dim2] + startOffY > pt2[dim2]) continue; + endOffY = (y + rect.y + rect.height - txcanv_border - cotop + stepy - 1) / stepy; + if (endOffY > pt2[dim2] - pt1[dim2]) endOffY = pt2[dim2] - pt1[dim2]; + } + + //cout << "table: " << rect.x << ':' << rect.y << ':' << rect.width << ':' << rect.height << " -- " << startOffX << ':' << endOffX << ':' << startOffY << ':' << endOffY << endl; + + posy = (float)(startOffY * stepy + txcanv_border + cotop); + r_Ref<r_Marray<r_Char> > mddPtr = (r_Ref<r_Marray<r_Char> >)mddObj; + r_Char *srcBase; + const r_Type *tp; + + if ((tp = mddPtr->get_base_type_schema()) == NULL) + { + cerr << "No schema information available" << endl; // FIXME + return; + } + + srcBase = (r_Char*)(mddPtr->get_array()); + if (dim2 < 0) + { + posx = (float)(startOffX * stepx + txcanv_border + coleft); + for (prun[dim1]=pt1[dim1]+startOffX; prun[dim1]<=pt1[dim1]+endOffX; prun[dim1]++, posx+=stepx) + { + long offset; + + offset = ((&((*mddPtr)[prun])) - srcBase) * baseSize; + rviewPrintTypedCell(tp, textbuff, (char*)(srcBase + offset), numberBase); + cdc->DrawText(textbuff, posx, posy); + } + } + else + { + for (prun[dim2]=pt1[dim2]+startOffY; prun[dim2]<=pt1[dim2]+endOffY; prun[dim2]++, posy+=stepy) + { + posx = (float)(startOffX * stepx + txcanv_border + coleft); + for (prun[dim1]=pt1[dim1]+startOffX; prun[dim1]<=pt1[dim1]+endOffX; prun[dim1]++, posx+=stepx) + { + long offset; + + offset = ((&((*mddPtr)[prun])) - srcBase) * baseSize; + rviewPrintTypedCell(tp, textbuff, (char*)(srcBase + offset), numberBase); + cdc->DrawText(textbuff, posx, posy); + } + } + } + + // Draw coordinate system if necessary + if (cosys) + { + int i; + + // Mustn't plot entire lines in one go; the lengths seem to be broken into 16 bit quantities + // internally so you could get overflows. Instead plot just what's visible. + if (startOffX == 0) + { + cdc->DrawLine(txcanv_border, cotop, txcanv_border + coleft, cotop); + } + if (startOffY == 0) + { + cdc->DrawLine(txcanv_border + coleft + startOffX * stepx, cotop, txcanv_border + coleft + (endOffX+1)*stepx, cotop); + } + if (dim2 >= 0) + { + if (startOffX == 0) + { + cdc->DrawLine(coleft, txcanv_border + cotop + startOffY * stepy, coleft, txcanv_border + cotop + (endOffY+1) * stepy); + } + if (startOffY == 0) + { + cdc->DrawLine(coleft, txcanv_border, coleft, txcanv_border + cotop); + } + posx = txcanv_cospace/2; + posy = startOffY * stepy + txcanv_border + cotop; + for (i=startOffY; i<=endOffY; i++, posy+=stepy) + { + sprintf(textbuff, "%ld", pt1[dim2] + i); + cdc->DrawText(textbuff, posx, posy); + } + } + + posx = startOffX * stepx + txcanv_border + coleft; + posy = txcanv_cospace/2; + for (i=startOffX; i<=endOffX; i++, posx+=stepx) + { + sprintf(textbuff, "%ld", pt1[dim1] + i); + cdc->DrawText(textbuff, posx, posy); + } + } + } + cdc->SetBrush(NULL); + cdc->SetFont(NULL); + cdc->EndDrawing(); +} + + +void textCanvas::CalcTextExtent(char *b, float &width, float &height) +{ + SetFont(font); + GetTextExtent(b, &width, &height); + SetFont(NULL); +} + + +void textCanvas::EstimateCellSize(int &width, int &height) +{ + char buffer[STRINGSIZE]; + float twidth, theight; + + sprintf(buffer, " ,"); + switch (numberBase) + { + case 8: sprintf(buffer+2, "%o", 255); break; + case 16: sprintf(buffer+2, "%x", 255); break; + default: sprintf(buffer+2, "%d", 255); break; + } + CalcTextExtent(buffer, twidth, theight); + width = (int)(baseSize * twidth + txcanv_colspace); + height = (int)(1.5 * theight); +} + + + +void textCanvas::setNumberBase(int newBase) +{ + numberBase = newBase; +} + + + + + + + +/* + * Class to display tables + */ + +const char *rviewTable::view_StepSize = "stepSize"; +const char *rviewTable::view_ScrollPos = "scrollPos"; +const char *rviewTable::view_CoSys = "coordSys"; +const char *rviewTable::view_NumBase = "numberBase"; + +rviewTable::rviewTable(mdd_frame *mf, unsigned int flags) : rviewDisplay(mf, table_ctrly, flags) +{ + int w, h, i; + char *b; + + RMDBGONCE(3, RMDebug::module_applications, "rviewTable", "rviewTable()"); + + // Mode defaults, move to prefs later on + stepx = prefs->tableStepx; stepy = prefs->tableStepy; + cosys = prefs->tableCosys; numberBase = prefs->tableMode; + + GetClientSize(&w, &h); + w -= 2*display_cnvborder; h -= 2*display_cnvborder + table_totaly; + canvas = new textCanvas((wxWindow*)this, display_cnvborder, display_cnvborder + table_totaly, w, h); + canvas->setData(mf, baseType, baseSize); + + // Need to init these before calling EstimateCellSize() + sxText = NULL; syText = NULL; + // Try to make a good guess about the default table width + canvas->setNumberBase(numberBase); + EstimateCellSize(stepx, stepy); + + sxText = new rviewText(ctrlPanel, stepx); + syText = new rviewText(ctrlPanel, stepy); + csBox = new rviewCheckBox(ctrlPanel); + csBox->SetValue(cosys); + + // Init projection string + b = projString; + b += sprintf(b, "*:*"); + if (dimMDD>1) + { + b += sprintf(b, ", *:*"); + } + for (i=2; i<dimMDD; i++) + { + b += sprintf(b, ", %ld", interv[i].low()); + } + project->SetValue(projString); + + scrollx = -1; scrolly = -1; + newProjection(); + + setModeDimension((dimMDD == 1) ? 1 : 2); + + setMinimumViewerSize(table_minwidth, table_minheight); +} + + +int rviewTable::openViewer(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewTable", "openViewer()"); + + if (rviewDisplay::openViewer() == 0) + { + int w, h; + wxMenu *men; + char buffer[STRINGSIZE]; + + GetClientSize(&w, &h); + + men = new wxMenu; + men->Append(MENU_TABLE_MODE_DECIMAL, "", NULL, TRUE); + men->Append(MENU_TABLE_MODE_OCTAL, "", NULL, TRUE); + men->Append(MENU_TABLE_MODE_HEX, "", NULL, TRUE); + sprintf(buffer, "&%s\n", lman->lookup("menTabMode")); + mBar->Append(men, buffer); + + checkModeMenu(); + + label(); + + frameWidth=-1; + frameHeight=-1; + + OnSize(w, h); + OnSize(w, h); + + Show(TRUE); + + return 0; + } + return -1; +} + + +void rviewTable::checkModeMenu(void) +{ + switch (numberBase) + { + case 8: lastMode = MENU_TABLE_MODE_OCTAL; break; + case 16: lastMode = MENU_TABLE_MODE_HEX; break; + default: lastMode = MENU_TABLE_MODE_DECIMAL; break; + } + mBar->Check(lastMode, TRUE); +} + + +const char *rviewTable::getFrameName(void) const +{ + return "rviewTable"; +} + +rviewFrameType rviewTable::getFrameType(void) const +{ + return rviewFrameTypeTable; +} + +int rviewTable::getViewerType(void) const +{ + return RVIEW_RESDISP_TABLE; +} + + +void rviewTable::EstimateCellSize(int &width, int &height) +{ + int w, h; + char buffer[STRINGSIZE]; + + canvas->EstimateCellSize(w, h); + // If prefs specify defaults (<= 0), update width/height, otherwise leave old values. + if (prefs->tableStepx <= 0) + { + width = w; + if (sxText != NULL) {sprintf(buffer, "%d", w); sxText->SetValue(buffer);} + } + if (prefs->tableStepy <= 0) + { + height = h; + if (syText != NULL) {sprintf(buffer, "%d", h); syText->SetValue(buffer);} + } +} + + + +rviewTable::~rviewTable(void) +{ + RMDBGONCE(3, RMDebug::module_applications, "rviewTable", "~rviewTable()"); + closeViewer(); +} + + + +void rviewTable::label(void) +{ + setDisplayTitle(lman->lookup("titleTable")); + + sxText->SetLabel(lman->lookup("textStepx")); + syText->SetLabel(lman->lookup("textStepy")); + csBox->SetLabel(lman->lookup("textCosys")); + + mBar->SetLabelTop(fixedNumberOfMenus, lman->lookup("menTabMode")); + mBar->SetLabel(MENU_TABLE_MODE_DECIMAL, lman->lookup("menTabModeDec")); + mBar->SetLabel(MENU_TABLE_MODE_OCTAL, lman->lookup("menTabModeOct")); + mBar->SetLabel(MENU_TABLE_MODE_HEX, lman->lookup("menTabModeHex")); + + rviewDisplay::label(); +} + + + +void rviewTable::OnSize(int w, int h) +{ + int x, y, i, j; + + GetClientSize(&x, &y); + x -= 2*display_border; + i = x - 2*table_twidth - table_cwidth; + j = 2*display_border + display_cheight; + sxText->SetSize(display_border + i/6, j, table_twidth, table_theight); + syText->SetSize(display_border + (3*i)/6 + table_twidth, j, table_twidth, table_theight); + csBox->SetSize(display_border + (5*i)/6 + 2*table_twidth, j, table_cwidth, table_cheight); + y -= 2*display_border + table_totaly; + + canvas->SetSize(display_border, display_border + table_totaly, x, y); + + rviewDisplay::OnSize(w, h); +} + + + +void rviewTable::OnMenuCommand(int id) +{ + int newBase; + + switch (id) + { + case MENU_TABLE_MODE_DECIMAL: newBase = 10; break; + case MENU_TABLE_MODE_OCTAL: newBase = 8; break; + case MENU_TABLE_MODE_HEX: newBase = 16; break; + default: newBase = -1; break; + } + if (newBase > 0) + { + mBar->Check(lastMode, FALSE); + numberBase = newBase; + checkModeMenu(); + canvas->setNumberBase(numberBase); + EstimateCellSize(stepx, stepy); + newTableSize(); + } + else + { + rviewDisplay::OnMenuCommand(id); + } +} + + + +int rviewTable::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + int i, j; + + if (((&obj == (wxObject*)sxText) || (&obj == (wxObject*)syText)) && (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND)) + { + i = atoi(sxText->GetValue()); + j = atoi(syText->GetValue()); + if ((i > 0) && (j > 0)) + { + stepx = i; stepy = j; + newTableSize(); + return 1; + } + } + + if ((&obj == (wxObject*)csBox) && (type == wxEVENT_TYPE_CHECKBOX_COMMAND)) + { + cosys = csBox->GetValue(); + newProjection(); + return 1; + } + + if (rviewDisplay::process(obj, evt) != 0) + { + return 1; + } + + return 0; +} + + + +int rviewTable::newProjection(void) +{ + int dim1, dim2; + + mapIndex = r_Point(dimMDD); + if (rviewParseProjection(getVirtualDomain(), pt1, pt2, projString, &freeDims, &mapIndex) != dimMDD) + { + rviewErrorbox::reportError(lman->lookup("errorProjection"), rviewTable::getFrameName(), "newProjection"); + return -1; + } + + for (dim1=0; dim1<dimMDD; dim1++) if ((freeDims & (1<<dim1)) != 0) break; + for (dim2=dim1+1; dim2<dimMDD; dim2++) if ((freeDims & (1<<dim2)) != 0) break; + if (dim1 >= dimMDD) + { + rviewErrorbox::reportError(lman->lookup("errorProjectFree"), rviewTable::getFrameName(), "newProjection"); + return -1; + } + dim1 = mapIndex[dim1]; + canvas->setProjection(pt1, pt2, freeDims, &mapIndex); + fieldsx = pt2[dim1] - pt1[dim1] + 1; + if (dim2 >= dimMDD) + { + fieldsy = 1; + } + else + { + dim2 = mapIndex[dim2]; + fieldsy = pt2[dim2] - pt1[dim2] + 1; + } + + //EstimateCellSize(&stepx, &stepy); + + newTableSize(); + + return 0; +} + + + +void rviewTable::newTableSize(void) +{ + int cl, ct; + int newstepx, newstepy; + + canvas->setStep(stepx, stepy); + canvas->setCoSys(cosys, cl, ct); + + if (scrollx >= 0) + scrollx = canvas->GetScrollPos(wxHORIZONTAL); + else + scrollx = 0; + + if (scrolly >= 0) + scrolly = canvas->GetScrollPos(wxVERTICAL); + else + scrolly = 0; + + newstepx = (int)((fieldsx*stepx + 2*display_border + cl + display_scrstep - 1) / display_scrstep); + newstepy = (int)((fieldsy*stepy + 2*display_border + ct + display_scrstep - 1) / display_scrstep); + + canvas->SetScrollbars(display_scrstep, display_scrstep, newstepx, newstepy, display_pgstep, display_pgstep, scrollx, scrolly); +} + + +int rviewTable::saveView(FILE *fp) +{ + int status = rviewDisplay::saveView(fp); + + long lvals[2]; + lvals[0] = (long)stepx; lvals[1] = (long)stepy; + writeViewParam(fp, view_StepSize, 2, lvals); + lvals[0] = (long)(canvas->GetScrollPos(wxHORIZONTAL)); + lvals[1] = (long)(canvas->GetScrollPos(wxVERTICAL)); + writeViewParam(fp, view_ScrollPos, 2, lvals); + writeViewParam(fp, view_CoSys, (long)cosys); + writeViewParam(fp, view_NumBase, (long)numberBase); + + return status; +} + + +int rviewTable::readView(const char *key, const char *value) +{ + int status = rviewDisplay::readView(key, value); + + if (status == 0) + { + if (strcmp(key, view_StepSize) == 0) + { + long lvals[2]; + if (readVector(value, 2, lvals) == 0) + { + stepx = (int)lvals[0]; stepy = (int)lvals[1]; + } + return 1; + } + else if (strcmp(key, view_ScrollPos) == 0) + { + long lvals[2]; + if (readVector(value, 2, lvals) == 0) + { + scrollx = (int)lvals[0]; scrolly = (int)lvals[1]; + } + return 1; + } + else if (strcmp(key, view_CoSys) == 0) + { + cosys = (bool)atoi(value); + return 1; + } + else if (strcmp(key, view_NumBase) == 0) + { + numberBase = atoi(value); + return 1; + } + return 0; + } + return status; +} + + +void rviewTable::loadViewFinished(void) +{ + sxText->SetValue(stepx); + syText->SetValue(stepy); + csBox->SetValue(cosys); + + mBar->Check(lastMode, FALSE); + checkModeMenu(); + + canvas->SetScrollPos(wxHORIZONTAL, scrollx); + canvas->SetScrollPos(wxVERTICAL, scrolly); +} diff --git a/applications/rview/rviewThumb.cpp b/applications/rview/rviewThumb.cpp new file mode 100644 index 0000000..0688ed2 --- /dev/null +++ b/applications/rview/rviewThumb.cpp @@ -0,0 +1,1183 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView thumbnail viewer class. Unlike the regular MDD viewers (e.g. + * rviewImage) this is not derived from rviewDisplay because it can + * display any number of MDD objects in contrast to regular viewers + * which manage exactly one object. + * + * COMMENTS: + * None + */ + + + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + + +#include <string.h> +#include <iostream.h> + + +#include "wx_pixmap.h" + + +#include "labelManager.hh" + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + + + +#include "rviewPrefs.hh" +#include "rviewTypes.hh" +#include "rviewUtils.hh" +#include "rviewThumb.hh" + +#include "raslib/rmdebug.hh" + +#include "rasodmg/ref.hh" +#include "rasodmg/marray.hh" + + + + +const int rviewThumb::thumb_width = 450; +const int rviewThumb::thumb_height = 300; +const int rviewThumb::thumb_imgwidth = 100; +const int rviewThumb::thumb_perline = 4; +const int rviewThumb::thumb_space = 32; +const int rviewThumb::thumb_border = 8; +const int rviewThumb::thumb_scrstep = 8; +const int rviewThumb::thumb_pgstep = 8; +const int rviewThumb::thumb_chkwidth = 100; +const int rviewThumb::thumb_chkheight = 30; +const int rviewThumb::thumb_minwidth = 10; +const int rviewThumb::thumb_maxwidth = 2000; +const int rviewThumb::thumb_mincols = 1; +const int rviewThumb::thumb_maxcols = 16; +const int rviewThumb::thumb_cheight = 64; +const int rviewThumb::thumb_twidth = 80; +const int rviewThumb::thumb_theight = 50; +const int rviewThumb::thumb_prjwidth = 60; + + + + + + +/* + * The thumbnail canvas class + */ +thumbCanvas::thumbCanvas(rviewThumb *par, int x, int y, int width, int height) : wxCanvas((wxWindow*)par, x, y, width, height, 0) +{ + parent = par; + brush.SetStyle(wxSOLID); + brush.SetColour((char)0xc0, (char)0xc0, (char)0xc0); + SetBackground(&brush); + font = new wxFont(12, wxROMAN, wxNORMAL, wxNORMAL); +} + + +thumbCanvas::~thumbCanvas(void) +{ + SetBackground(NULL); + delete font; +} + + +void thumbCanvas::OnPaint(void) +{ + int thumbs, thumbsperline; + int gridX, gridY; + wxPixmap *thumbnail; + wxUpdateIterator upd(this); + //wxRect rect; + int startx, starty, endx, endy, w, h, posx, posy; + int scrollx, scrolly; + int maxy, runx, runy; + + //cout << "thumbCanvas::OnPaint" << endl; + + parent->getThumbInfo(thumbs, thumbsperline); + parent->getGridInfo(gridX, gridY); + + if ((thumbs <= 0) || (thumbsperline <= 0) || (gridX <= 0) || (gridY <= 0)) return; + + GetClientSize(&w, &h); + + scrollx = rviewThumb::thumb_scrstep * GetScrollPos(wxHORIZONTAL); + scrolly = rviewThumb::thumb_scrstep * GetScrollPos(wxVERTICAL); + + startx = (scrollx / gridX); endx = ((scrollx + w + gridX - 1) / gridX); + starty = (scrolly / gridY); endy = ((scrolly + h + gridY - 1) / gridY); + posx = rviewThumb::thumb_space / 2 + gridX * startx; + posy = rviewThumb::thumb_space / 2 + gridY * starty; + + maxy = (thumbs + thumbsperline - 1) / thumbsperline; + + if ((startx >= thumbsperline) || (endx < 0)) return; + if ((starty >= maxy) || (endy < 0)) return; + + if (startx < 0) startx = 0; + if (endx >= thumbsperline) endx = thumbsperline-1; + + if (starty < 0) starty = 0; + if (endy >= maxy) endy = maxy - 1; + + BeginDrawing(); + + SetFont(font); + + while (upd) + { + char caption[STRINGSIZE]; + + runx = posx; + for (w = startx; w <= endx; w++, runx += gridX) + { + runy = posy; + for (h = starty; h <= endy; h++, runy += gridY) + { + if ((thumbnail = parent->getPixmapNumber(h * thumbsperline + w, caption)) != NULL) + { + float tw, th; + + /*cout << "Pixmap " << w << ' ' << h << ": " << (void*)thumbnail << ", at " << runx << ' ' << runy + << ", width " << thumbnail->getWidth() << ", height " << thumbnail->getHeight() + << ", depth " << thumbnail->getDepth() << ", ddepth " << thumbnail->getModeDepth() << endl;*/ + + // Pixmaps have the canvas coordinate system, text has the _scrolled_ canvas cosys! + thumbnail->plotPixmap(runx - scrollx, runy - scrolly); + GetTextExtent(caption, &tw, &th); + tw = ((float)(thumbnail->getWidth()) - tw) / 2; + th = thumbnail->getHeight(); + DrawText(caption, runx + tw, runy + th); + } + } + } + upd++; + } + + SetFont(NULL); + + EndDrawing(); +} + + +void thumbCanvas::updateDisplay(void) +{ + if (parent->IsShown()) + { + int cw, ch; + + GetClientSize(&cw, &ch); + SetClippingRegion(0, 0, cw, ch); + OnPaint(); + DestroyClippingRegion(); + } +} + + + +/* + * The rviewThumb class for displaying thumbnails of images + */ +rviewThumb::rviewThumb(void) : rviewFrame(NULL, "", 0, 0, thumb_width, thumb_height) +{ + int x, y, tw, py; + wxMenu *menu; + wxMenu *setup; + wxMenu *submenu; + char buffer[STRINGSIZE]; + + thumbsperline = 4; if (prefs->thumbCols > 0) thumbsperline = prefs->thumbCols; + projstep = 1; if (prefs->thumbProjstep > 0) projstep = prefs->thumbProjstep; + doValToCspace = (prefs->rgbSpace != 0); doFullRangeCspace = (prefs->rgbSpace == 2); + dimproj = prefs->thumbProjdim; + imgWidth = 100; if (prefs->thumbWidth > 0) imgWidth = prefs->thumbWidth; + if (imgWidth < thumb_minwidth) imgWidth = thumb_minwidth; + if (imgWidth > thumb_maxwidth) imgWidth = thumb_maxwidth; + + thumbs = 0; numPixmaps = 0; maxHeight = -1; + listHead = NULL; csmap = NULL; canDoCspace = FALSE; + gridX = 0; gridY = 0; + + menu = new wxMenu; + menu->Append(MENU_THUMB_DATA_CLOSE, ""); + submenu = new wxMenu; + submenu->Append(MENU_THUMB_CSPACE_ON, "", NULL, TRUE); + submenu->Append(MENU_THUMB_CSPACE_FULL, "", NULL, TRUE); + submenu->Append(MENU_THUMB_CSPACE_EDIT, ""); + setup = new wxMenu; + setup->Append(MENU_THUMB_SETUP_CSPACE, "", submenu, NULL); + + mbar = new wxMenuBar; + sprintf(buffer, "&%s", lman->lookup("menThumbData")); + mbar->Append(menu, buffer); + sprintf(buffer, "&%s", lman->lookup("menThumbSetup")); + mbar->Append(setup, buffer); + mbar->Check(MENU_THUMB_CSPACE_ON, doValToCspace); + mbar->Check(MENU_THUMB_CSPACE_FULL, doFullRangeCspace); + configureCspace(FALSE); + + GetClientSize(&x, &y); + + panel = new wxPanel((wxWindow*)this, 0, thumb_border, x, thumb_cheight); + panel->SetLabelPosition(wxVERTICAL); + + x -= 2*thumb_border; y -= 2*thumb_border + thumb_cheight; + canvas = new thumbCanvas(this, thumb_border, thumb_border + thumb_cheight, x, y); + + tw = (x - 4*thumb_border - 2*thumb_prjwidth) / 3; + py = thumb_border; + projString[0] = '\0'; + project = new rviewText(panel); + thumbProj = new rviewText(panel, dimproj); + thumbStep = new rviewText(panel, projstep); + thumbWidth = new rviewText(panel, imgWidth); + thumbCols = new rviewText(panel, thumbsperline); + + SetMenuBar(mbar); + + label(); + + frameWidth=-1; + frameHeight=-1; + + OnSize(x, y); + OnSize(x, y); + + + Show(TRUE); +} + + +rviewThumb::~rviewThumb(void) +{ + if (thumbs != 0) + { + int i; + rviewThumbList *tlst = listHead; + + for (i=0; i<thumbs; i++) + { + deletePixmapChain(tlst); + + //cout << "delete " << (void*)tlst << endl; + listHead = tlst; + tlst = tlst->next; delete listHead; + } + } + if (csmap != NULL) delete csmap; +} + + +void rviewThumb::configureCspace(bool mode) +{ + mbar->Enable(MENU_THUMB_SETUP_CSPACE, mode); + mbar->Enable(MENU_THUMB_CSPACE_ON, mode); + mbar->Enable(MENU_THUMB_CSPACE_FULL, mode); + mbar->Enable(MENU_THUMB_CSPACE_EDIT, mode); +} + + + +// Deletes all the pixmaps for one MDD object +void rviewThumb::deletePixmapChain(rviewThumbList *tlst) +{ + if (tlst->pixmaps != NULL) + { + rviewThumbPixList *tplst, *tplast; + + tplst = tlst->pixmaps; + while (tplst != NULL) + { + //cout << " delete " << (void*)tplst << endl; + tplast = tplst; tplst = tplst->next; + if (tplast->pixmap != NULL) delete tplast->pixmap; + delete tplast; + numPixmaps--; + } + tlst->numPix = 0; + tlst->pixmaps = NULL; + } +} + + +// Creates a pixmap chain for a given MDD object +int rviewThumb::pixmapsFromMDD(rviewThumbList *tlst) +{ + rviewThumbPixList *newPixmap, *lastPixmap; + int i, projval; + bool pixLoop; + + //cout << "pixmapsFromMDD" << endl; + + // delete old pixmaps + deletePixmapChain(tlst); + + lastPixmap = NULL; + + if (dimproj >= 0) + { + projval = pt1[dimproj]; + } + + // Create all thumbnails for this object. This is 1 for 2D objects, any number for 3+D objects + do + { + pixLoop = FALSE; + newPixmap = new rviewThumbPixList; + newPixmap->next = NULL; + newPixmap->dimproj = dimproj; newPixmap->projval = projval; + + if (lastPixmap == NULL) + { + tlst->pixmaps = newPixmap; + } + else + { + lastPixmap->next = newPixmap; + } + lastPixmap = newPixmap; + + try + { + if ((newPixmap->pixmap = buildThumbnail(tlst->mdd, tlst->baseType, newPixmap->dimproj, newPixmap->projval)) == NULL) + { + cerr << lman->lookup("errorProjThumb") << endl; + return 0; + } + } + catch (r_Error &errObj) + { + cerr << errObj.what() << endl; + return 0; + } + + (tlst->numPix)++; numPixmaps++; + + i = newPixmap->pixmap->getHeight(); + if (i > maxHeight) + { + maxHeight = i; + } + + if (dimproj >= 0) + { + projval += projstep; + if (projval <= pt2[dimproj]) pixLoop = TRUE; + } + + } + while (pixLoop); + + return 1; +} + + + +int rviewThumb::addMDD(r_Ref<r_GMarray> &newMdd) +{ + rviewThumbList *newItem; + int i; + r_Object *mo; + bool oldCstate; + + RMDBGONCE(3, RMDebug::module_applications, "rviewThumb", "addMDD(...)"); + + if ((newItem = new rviewThumbList) == NULL) return 0; + + oldCstate = canDoCspace; + + // Init object-dependent internal data? + if (projString[0] == '\0') + { + // No ==> init to a decent default: + initForObject(newMdd); + } + mo = (r_Object*)(&(*newMdd)); + newItem->mdd = newMdd; newItem->baseType = rviewGetBasetype(mo); + newItem->numPix = 0; newItem->next = NULL; newItem->pixmaps = NULL; + + if (pixmapsFromMDD(newItem) == 0) + { + deletePixmapChain(newItem); + delete newItem; return 0; + } + + // If it was true before it's true now. + if (oldCstate) + canDoCspace = TRUE; + else + { + // Otherwise try the transition FALSE -> TRUE + if (canDoCspace) + { + // Enabling the parent automatically enables the children too, so reset them... + mbar->Enable(MENU_THUMB_SETUP_CSPACE, TRUE); + mbar->Enable(MENU_THUMB_CSPACE_FULL, doValToCspace); + mbar->Enable(MENU_THUMB_CSPACE_EDIT, doValToCspace); + } + } + + if (thumbs == 0) + { + listHead = newItem; + } + else + { + rviewThumbList *tlst = listHead; + + for (i=1; i<thumbs; i++) tlst = tlst->next; + + tlst->next = newItem; + } + + thumbs++; + + updateCanvasSize(); + + return 1; +} + + +int rviewThumb::deleteMDD(r_Ref<r_GMarray> &obsMdd) +{ + rviewThumbList *last, *tlst; + int i; + + RMDBGONCE(3, RMDebug::module_applications, "rviewThumb", "deleteMDD(...)"); + + last = NULL; tlst = listHead; + + for (i=0; i<thumbs; i++) + { + if (tlst->mdd == obsMdd) break; + last = tlst; tlst = tlst->next; + } + if (i < thumbs) + { + deletePixmapChain(tlst); + + if (last == NULL) + { + listHead = tlst->next; + } + else + { + last->next = tlst->next; + } + delete tlst; + thumbs--; + + // Removing a thumbnail means a new layout and new bounding boxes + maxHeight = 0; tlst = listHead; + for (i=0; i<thumbs; i++) + { + int ph; + rviewThumbPixList *tplst = tlst->pixmaps; + + while (tplst != NULL) + { + ph = tplst->pixmap->getHeight(); + if (ph > maxHeight) maxHeight = ph; + tplst = tplst->next; + } + tlst = tlst->next; + } + // Close thumbnail viewer when the last thumbnail has been deleted + if (thumbs <= 0) + { + this->Close(TRUE); + } + else + { + updateCanvasSize(); + } + return 1; + } + return 0; +} + + +void rviewThumb::setLayout(int width, int npl) +{ + imgWidth = width; thumbsperline = npl; + updateCanvasSize(); +} + + +void rviewThumb::label(void) +{ + SetTitle(lman->lookup("titleThumb")); + mbar->SetLabelTop(0, lman->lookup("menThumbData")); + mbar->SetLabel(MENU_THUMB_DATA_CLOSE, lman->lookup("menThumbDataClose")); + mbar->SetLabelTop(1, lman->lookup("menThumbSetup")); + mbar->SetLabel(MENU_THUMB_SETUP_CSPACE, lman->lookup("menCspaceTitle")); + mbar->SetLabel(MENU_THUMB_CSPACE_ON, lman->lookup("menCspaceOn")); + mbar->SetLabel(MENU_THUMB_CSPACE_FULL, lman->lookup("menCspaceFull")); + mbar->SetLabel(MENU_THUMB_CSPACE_EDIT, lman->lookup("menCspaceEdit")); + + project->SetLabel(lman->lookup("textProjString")); + thumbProj->SetLabel(lman->lookup("textThumbProjDim")); + thumbStep->SetLabel(lman->lookup("textThumbProjStep")); + thumbWidth->SetLabel(lman->lookup("textThumbWidth")); + thumbCols->SetLabel(lman->lookup("textThumbColumns")); +} + + +int rviewThumb::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND) + { + char buffer[STRINGSIZE]; + int newVal; + + if (&obj == (wxObject*)thumbWidth) + { + newVal = atoi(thumbWidth->GetValue()); + if ((newVal < thumb_minwidth) || (newVal > thumb_maxwidth)) + { + sprintf(buffer, "%d", imgWidth); + thumbWidth->SetValue(buffer); + } + else + { + newThumbWidth(newVal); + } + return 1; + } + if (&obj == (wxObject*)thumbCols) + { + newVal = atoi(thumbCols->GetValue()); + if ((newVal < thumb_mincols) || (newVal > thumb_maxcols)) + { + sprintf(buffer, "%d", thumbsperline); + thumbCols->SetValue(buffer); + } + else + { + thumbsperline = newVal; + updateCanvasSize(); + } + return 1; + } + if (&obj == (wxObject*)project) + { + if (thumbs > 0) + { + unsigned int oldProj; + int oldFrom, oldTo; + + oldProj = dimproj; + if (dimproj >= 0) + { + oldFrom = pt1[dimproj]; oldTo = pt2[dimproj]; + } + strcpy(projString, project->GetValue()); + if (parseProjection(listHead->mdd) != 0) + { + bool fromScratch = TRUE; + + // Determine whether we have to rebuild everything from scratch or not + if ((int)oldProj == dimproj) + { + if (dimproj >= 0) + { + if ((pt1[dimproj] == oldFrom) && (pt2[dimproj] == oldTo)) fromScratch = TRUE; + } + } + rebuildThumbnails(fromScratch); + } + } + return 1; + } + if (&obj == (wxObject*)thumbStep) + { + int oldStep = projstep; + + projstep = atoi(thumbStep->GetValue()); + if (projstep <= 0) + { + projstep = 1; + thumbProj->SetValue("1"); + } + if ((dimMDD > 2) && (oldStep != projstep)) rebuildThumbnails(TRUE); + + return 1; + } + if (&obj == (wxObject*)thumbProj) + { + if ((thumbs > 0) && (dimMDD > 2)) + { + if (parseProjection(listHead->mdd) != 0) + { + rebuildThumbnails(TRUE); + } + } + return 1; + } + } + return 0; +} + + +const char *rviewThumb::getFrameName(void) const +{ + return "rviewThumb"; +} + +rviewFrameType rviewThumb::getFrameType(void) const +{ + return rviewFrameTypeThumb; +} + + +void rviewThumb::OnSize(int w, int h) +{ + int x, y, tw, py, px; + + GetClientSize(&x, &y); + if((x < thumb_width) || (y < thumb_height)) + { + frameWidth = x; + frameHeight = y; + SetClientSize(thumb_width, thumb_height); + return; + } + + panel->SetSize(0, thumb_border, x, thumb_cheight); + x -= 2*thumb_border; y -= 2*thumb_border + thumb_cheight; + canvas->SetSize(thumb_border, thumb_border + thumb_cheight, x, y); + + tw = (x - 2*thumb_prjwidth - 2*thumb_twidth - 6*thumb_border); + py = thumb_border; + px = thumb_border; + project->SetSize(px, py, tw , thumb_theight); + px +=thumb_border + tw; + thumbProj->SetSize(px, py, thumb_prjwidth, thumb_theight); + px +=thumb_prjwidth + thumb_border; + thumbStep->SetSize(px, py, thumb_prjwidth, thumb_theight); + px +=thumb_prjwidth + thumb_border; + thumbWidth->SetSize(px, py, thumb_twidth, thumb_theight); + px +=thumb_twidth + thumb_border; + thumbCols->SetSize(px, py, thumb_twidth, thumb_theight); +} + + +void rviewThumb::OnMenuCommand(int id) +{ + switch (id) + { + case MENU_THUMB_DATA_CLOSE: + this->Close(TRUE); break; + case MENU_THUMB_CSPACE_EDIT: + csmap->openEditor(); break; + case MENU_THUMB_CSPACE_ON: + { + rviewThumbList *tlst = listHead; + int i; + + // Don't allow switching off cspace mapping if at least one object is float/double. + for (i=0; i<thumbs; i++) + { + if ((tlst->baseType == rbt_float) || (tlst->baseType == rbt_double)) break; + tlst = tlst->next; + } + if (i < thumbs) + { + mbar->Check(MENU_THUMB_CSPACE_ON, TRUE); + } + else + { + doValToCspace = mbar->Checked(MENU_THUMB_CSPACE_ON); + mbar->Enable(MENU_THUMB_CSPACE_FULL, doValToCspace); + mbar->Enable(MENU_THUMB_CSPACE_EDIT, doValToCspace); + rebuildThumbnails(FALSE); + } + } + break; + case MENU_THUMB_CSPACE_FULL: + doFullRangeCspace = mbar->Checked(MENU_THUMB_CSPACE_FULL); + if (csmap != NULL) + { + csmap->processRange((doFullRangeCspace) ? CSPACE_RANGE_FULL : CSPACE_RANGE_ACTUAL); + } + rebuildThumbnails(FALSE); + break; + default: break; + } +} + + +int rviewThumb::userEvent(const user_event &ue) +{ + if (ue.type == usr_mdd_dying) + { + return deleteMDD(*((r_Ref<r_GMarray>*)(ue.data))); + } + if (ue.type == usr_cspace_changed) + { + if (ue.data == (void*)csmap) + { + rebuildThumbnails(FALSE); + return 1; + } + } + return 0; +} + + +// Return the pixmap with number no and write a textual description into +// caption which has to be at least STRINGSIZE bytes large. +wxPixmap *rviewThumb::getPixmapNumber(int no, char *caption) +{ + rviewThumbList *tlst = listHead; + int major, minor; + wxPixmap *retPix = NULL; + + if (no >= numPixmaps) return NULL; + + // major = number of MDD object, minor = number of pixmap for this object + major = 0; minor = 0; + + while ((tlst != NULL) && (no > 0)) + { + minor = 0; + if (no < tlst->numPix) + { + rviewThumbPixList *tplst = tlst->pixmaps; + while ((tplst != NULL) && (no > 0)) + { + tplst = tplst->next; minor++; no--; + } + if (tplst == NULL) + { + retPix = NULL; + break; + } + else + { + //cout << "found " << (void*)(tplst->pixmap) << endl; + retPix = tplst->pixmap; + break; + } + } + else + { + no -= tlst->numPix; tlst = tlst->next; major++; + } + } + if ((retPix == NULL) && (tlst != NULL)) + { + if ((no == 0) && (tlst->numPix > 0)) + { + retPix = tlst->pixmaps->pixmap; + } + } + + if (retPix != NULL) + { + sprintf(caption, "MDD %d [%d]", major, minor*projstep); + } + + return retPix; +} + + +void rviewThumb::getThumbInfo(int &num, int &npl) +{ + num = numPixmaps; npl = thumbsperline; +} + + +void rviewThumb::getGridInfo(int &gx, int &gy) +{ + gx = gridX; gy = gridY; +} + + +void rviewThumb::updateCanvasSize(void) +{ + int stepx, stepy; + int scrollx, scrolly; + + RMDBGONCE(3, RMDebug::module_applications, "rviewThumb", "updateCanvasSize()"); + + gridX = imgWidth + thumb_space; + gridY = maxHeight + thumb_space; + + scrollx = canvas->GetScrollPos(wxHORIZONTAL); scrolly = canvas->GetScrollPos(wxVERTICAL); + + stepx = (gridX * thumbsperline + thumb_scrstep - 1) / thumb_scrstep; + if ((numPixmaps == 0) || (thumbsperline == 0)) + { + stepy = 1; + canvas->EnableScrolling(TRUE, FALSE); + canvas->SetScrollRange(wxVERTICAL, 0); + } + else + { + stepy = (((numPixmaps + thumbsperline - 1) / thumbsperline) * gridY + thumb_scrstep - 1) / thumb_scrstep; + canvas->EnableScrolling(TRUE, TRUE); + } + canvas->SetScrollbars(thumb_scrstep, thumb_scrstep, stepx, stepy, thumb_pgstep, thumb_pgstep, scrollx, scrolly); +} + + + +// Init internal variables (e.g. projection-related) according to a sample object +void rviewThumb::initForObject(r_Ref<r_GMarray> &mddObj) +{ + char *data; + int i; + r_Minterval interv; + + interv = mddObj->spatial_domain(); + dimMDD = interv.dimension(); + // freeDims is a bitfield specifying which dimensions are free + if (dimMDD <= 2) + { + freeDims = 3; + } + else + { + if ((dimproj == -1) || (dimproj >= dimMDD)) freeDims = 3; + else + { + if (dimproj == 0) freeDims = 6; + else if (dimproj == 1) freeDims = 5; + else freeDims = 3; + } + } + pt1 = r_Point(dimMDD); pt2 = r_Point(dimMDD); + data = projString; + for (i=0; i<dimMDD; i++) + { + pt1[i] = interv[i].low(); + if ((freeDims & (1<<i)) == 0) + { + data += sprintf(data, "%d, ", interv[i].low()); + pt2[i] = interv[i].low(); + } + else + { + data += sprintf(data, "*:*, "); + pt2[i] = interv[i].high(); + } + } + data[-2] = 0; // cut the final ", " + project->SetValue(projString); + parseProjection(mddObj); +} + + +// Parse the projection string and set up all internal variables that depend on it. +int rviewThumb::parseProjection(r_Ref<r_GMarray> &mddObj) +{ + int x, h; + + mapIndex = r_Point(dimMDD); + if (rviewParseProjection(mddObj->spatial_domain(), pt1, pt2, projString, &freeDims, &mapIndex) != dimMDD) + { + cerr << lman->lookup("errorProjection") << endl; + rviewErrorbox eb(lman->lookup("errorProjection")); eb.activate(); + return 0; + } + + dimproj = -1; dim1 = -1; dim2 = -1; + for (x=0; x<dimMDD; x++) + { + if ((freeDims & (1<<x)) != 0) + { + if (dim1 < 0) + dim1 = x; + else if (dim2 < 0) + dim2 = x; + else if (dimproj < 0) + dimproj = x; + else + break; + } + } + if (x < dimMDD) + { + cerr << lman->lookup("errorProjThumb") << endl; + return 0; + } + //cout << "parseProj: dim1 " << dim1 << ", dim2 " << dim2 << ", dimproj " << dimproj << endl; + // are there 3 free dimensions to begin with? + if (dimproj >= 0) + { + // user-specified projection value overrides default (last free one) + h = atoi(thumbProj->GetValue()); + // Now swap around the dimensions as required + if (h == dim1) + { + dim1 = dim2; dim2 = dimproj; dimproj = h; + } + else if (h == dim2) + { + dim2 = dimproj; dimproj = h; + } + // otherwise just keep the default dimproj value + } + dim1 = mapIndex[dim1]; dim2 = mapIndex[dim2]; + + // Also read the other variables that can change the appearance of the thumbnails + h = atoi(thumbWidth->GetValue()); + if ((h >= thumb_minwidth) && (h <= thumb_maxwidth)) + { + imgWidth = h; + } + h = atoi(thumbStep->GetValue()); + if (h > 0) + { + projstep = h; + } + + return 1; +} + + + + +wxPixmap *rviewThumb::buildThumbnail(r_Ref<r_GMarray> &mddObj, rviewBaseType baseType, int dimproject, int projval) +{ + char *data; + wxPixmap *pixmap; + wxColour palette[2]; + int baseSize; + int x; + r_Minterval thisInterv; + int objdim; + int srcWidth; + r_Point ptlow, pthigh; + + RMDBGENTER(3, RMDebug::module_applications, "rviewThumb", "buildThumbnail(...)"); + + //cout << "thumbnail " << dimproject << ", " << projval << endl; + + thisInterv = mddObj->spatial_domain(); + objdim = thisInterv.dimension(); + + // Only data with at least 2 dimensions is allowed + if (objdim < 2) + return NULL; + + // Check if all the thumbnails have the same number of dimensions + if (dimMDD != objdim) + { + cerr << "Warning: inconsistent number of dimensions in collection!" << endl; + } + + // Make sure the points we use are within the spatial domain of this object, in case + // the objects in this collection don't have a uniform spatial domain. + ptlow = r_Point(dimMDD); pthigh = r_Point(dimMDD); + for (x=0; x<dimMDD; x++) + { + if (pt1[x] >= thisInterv[x].low()) ptlow[x] = pt1[x]; else ptlow[x] = thisInterv[x].low(); + if (pt2[x] <= thisInterv[x].high()) pthigh[x] = pt2[x]; else pthigh[x] = thisInterv[x].high(); + } + //cout << "Corners " << pt1 << ", " << pt2 << endl; + //cout << "low " << ptlow << ", high " << pthigh << endl; + + baseSize = (int)(mddObj->get_type_length()); + + if ((baseType == rbt_float) || (baseType == rbt_double)) + { + doValToCspace = TRUE; + mbar->Check(MENU_THUMB_CSPACE_ON, TRUE); + configureCspace(TRUE); + } + + rviewFlatProjEnv penv; + + penv.mddPtr = mddObj.ptr(); + penv.pt1 = ptlow; penv.pt2 = pthigh; + penv.dim1 = dim1; penv.dim2 = dim2; + penv.bt = baseType; + penv.doCspace = doValToCspace; + + if (dimproject >= 0) + { + penv.pt1[dimproject] = projval; penv.pt2[dimproject] = projval; + } + srcWidth = (int)(pthigh[dim1] - ptlow[dim1] + 1); + //cout << "srcWidth " << srcWidth << endl; + if (srcWidth < 1) srcWidth = 1; + penv.scale = ((double)imgWidth) / srcWidth; + //cout << "dimproject " << dimproject << ", projval " << projval + // << ", scale " << penv.scale << endl; + //cout << "penv " << penv.pt1 << ", " << penv.pt2 << endl; + + if (rviewPrepareFlatProjection(penv) != 0) return NULL; + + if (rviewImageTypes[baseType] == RVIEW_IMGTYPE_MONO) + { + palette[0] = wxColour(0,0,0); palette[1] = wxColour(255,255,255); + } + + // Colourspace mapping possible for this object? + if (rviewCheckInitCspace(baseType, NULL, mddObj) != 0) canDoCspace = TRUE; + + if (doValToCspace) + { + penv.cspaceState = rviewCheckInitCspace(baseType, &csmap, mddObj, doFullRangeCspace, NULL, penv.width, &penv.pitch, &penv.depth, &penv.pad); + if (csmap != NULL) mbar->Enable(MENU_THUMB_CSPACE_EDIT, TRUE); + } + else + penv.cspaceState = 0; + + penv.csmap = csmap; + + /*if (penv.pitch * penv.height >= 16*1024*1024) + return NULL;*/ + + if ((data = (char*)malloc(penv.pitch * penv.height)) == NULL) + return NULL; + + if (rviewPerformFlatProjection(penv, data) != 0) + { + free(data); return NULL; + } + + pixmap = new wxPixmap((wxWindow*)canvas, penv.width, penv.height, penv.depth, penv.pad, data, rviewImage::getPixmapFlags(), (rviewImageTypes[baseType] == RVIEW_IMGTYPE_MONO) ? palette : NULL); + + RMDBGEXIT(3, RMDebug::module_applications, "rviewThumb", "buildThumbnail(...) Pixmap " << (void*)pixmap ); + + return pixmap; +} + + + + +void rviewThumb::newThumbWidth(int newWidth) +{ + if (newWidth == imgWidth) return; + imgWidth = newWidth; + + rebuildThumbnails(FALSE); +} + + + +void rviewThumb::rebuildThumbnails(bool fromScratch) +{ + int i, j; + rviewThumbList *tlst; + rviewThumbPixList *tplst; + + //cout << "rebuildThumbnails " << fromScratch << endl; + + tlst = listHead; + + if (fromScratch) + { + // Delete all thumbnail pixmaps, including the chain structure + for (i=0; i<thumbs; i++, tlst = tlst->next) + { + deletePixmapChain(tlst); + } + maxHeight = 0; + for (i=0, tlst=listHead; i<thumbs; i++, tlst=tlst->next) + { + if (pixmapsFromMDD(tlst) == 0) + { + cerr << lman->lookup("errorProjThumb") << endl; + } + } + } + else + { + // 1) Delete all the thumbnail pixmaps, but leave the chain intact! + for (i=0; i<thumbs; i++, tlst = tlst->next) + { + tplst = tlst->pixmaps; + while (tplst != NULL) + { + if (tplst->pixmap != NULL) delete tplst->pixmap; + tplst->pixmap = NULL; tplst = tplst->next; + } + } + // 2) Create new ones. Doing this in two passes is better for the memory allocation + for (i=0, tlst=listHead; i<thumbs; i++, tlst = tlst->next) + { + tplst = tlst->pixmaps; + while (tplst != NULL) + { + if ((tplst->pixmap = buildThumbnail(tlst->mdd, tlst->baseType, tplst->dimproj, tplst->projval)) == NULL) + { + cerr << lman->lookup("errorProjThumb") << endl; + } + tplst = tplst->next; + } + } + } + + // Now adapt the maximum height + maxHeight = 0; tlst = listHead; + for (i=0; i<thumbs; i++, tlst=tlst->next) + { + tplst = tlst->pixmaps; + while (tplst != NULL) + { + if (tplst->pixmap != NULL) + { + j = tplst->pixmap->getHeight(); + if (j > maxHeight) maxHeight = j; + } + tplst = tplst->next; + } + } + + updateCanvasSize(); +} diff --git a/applications/rview/rviewThumb.hh b/applications/rview/rviewThumb.hh new file mode 100644 index 0000000..dc30c24 --- /dev/null +++ b/applications/rview/rviewThumb.hh @@ -0,0 +1,203 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * + * rView thumbnail viewer class. Unlike the regular MDD viewers (e.g. + * rviewImage) this is not derived from rviewDisplay because it can + * display any number of MDD objects in contrast to regular viewers + * which manage exactly one object. + * + * COMMENTS: + * None + */ + + + +#ifndef _RVIEW_THUMB_H_ +#define _RVIEW_THUMB_H_ + + + + +#include "rasodmg/ref.hh" +#include "rasodmg/marray.hh" +#include "rasodmg/gmarray.hh" + +#include "labelManager.hh" +#include "rviewUtils.hh" +#include "rviewDisplay.hh" +#include "rviewDModes.hh" + + + + +class wxPixmap; +class rviewThumb; + + +// List holding all the pixmap items for one mddObject. +// This chain is 1 item long for 2D data, but can be any length +// for higher dimensions +typedef struct rviewThumbPixList { + wxPixmap *pixmap; + int dimproj, projval; + rviewThumbPixList *next; +} rviewThumbPixList; + +// List holding mdd/pixmap items +typedef struct rviewThumbList { + r_Ref<r_GMarray> mdd; + rviewBaseType baseType; + int numPix; + rviewThumbPixList *pixmaps; + rviewThumbList *next; +} rviewThumbList; + + + + +/* + * The canvas displaying the thumbnails + */ +class thumbCanvas: public wxCanvas +{ + public: + + thumbCanvas(rviewThumb *par, int x, int y, int width, int height); + ~thumbCanvas(void); + + void OnPaint(void); + void updateDisplay(void); + + + protected: + + wxBrush brush; + rviewThumb *parent; +}; + + + +/* + * A window containing small versions of images + */ +class rviewThumb: public rviewFrame +{ + public: + + rviewThumb(void); + ~rviewThumb(void); + + int addMDD(r_Ref<r_GMarray> &newMdd); + int deleteMDD(r_Ref<r_GMarray> &obsMdd); + void setLayout(int width, int npl); + void newThumbWidth(int newWidth); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + void OnSize(int w, int h); + void OnMenuCommand(int id); + + int userEvent(const user_event &ue); + + // Used by canvas to get data + wxPixmap *getPixmapNumber(int no, char *caption); + void getThumbInfo(int &num, int &npl); + void getGridInfo(int &gx, int &gy); + + // constants + // Initial dimensions of window + static const int thumb_width; + static const int thumb_height; + // Default width of thumbnail images + static const int thumb_imgwidth; + // Default number of thumbnails per line + static const int thumb_perline; + // Space between thumbnails + static const int thumb_space; + // Borders used in thumbnail window + static const int thumb_border; + // Scrolling values + static const int thumb_scrstep; + static const int thumb_pgstep; + // Dimensions of checkboxs + static const int thumb_chkwidth; + static const int thumb_chkheight; + // Minimum / maximum width of thumbnails + static const int thumb_minwidth; + static const int thumb_maxwidth; + // Minimum / maximum number of thumbnails per line + static const int thumb_mincols; + static const int thumb_maxcols; + // Height of control panel at the top + static const int thumb_cheight; + // Height of text items + static const int thumb_twidth; + static const int thumb_theight; + // Width of projDim / step widgets + static const int thumb_prjwidth; + + + protected: + + void deletePixmapChain(rviewThumbList *tlst); + int pixmapsFromMDD(rviewThumbList *tlst); + wxPixmap *buildThumbnail(r_Ref<r_GMarray> &mddObj, rviewBaseType baseType, int dimproject, int projval); + void updateCanvasSize(void); + void rebuildThumbnails(bool fromScratch); + void initForObject(r_Ref<r_GMarray> &mddObj); + int parseProjection(r_Ref<r_GMarray> &mddObj); + void configureCspace(bool mode); + + char projString[STRINGSIZE]; + r_Point pt1, pt2, mapIndex; + int thumbs, thumbsperline, numPixmaps; + int maxHeight; + int gridX, gridY; + int imgWidth; + int dimMDD; // should be constant for all objects! + int dim1, dim2; // dimensions to iterate over + int dimproj; // projection dim for 3+D objects + int projstep; // stepping value in dimproj + unsigned int freeDims; + rviewThumbList *listHead; + thumbCanvas *canvas; + wxMenuBar *mbar; + wxPanel *panel; + rviewText *thumbWidth; + rviewText *thumbCols; + rviewText *project; + rviewText *thumbProj, *thumbStep; + wxFont *font; + colourspaceMapper *csmap; + bool doValToCspace; + bool doFullRangeCspace; + bool canDoCspace; +}; + +#endif diff --git a/applications/rview/rviewTypeMan.cpp b/applications/rview/rviewTypeMan.cpp new file mode 100644 index 0000000..9a0af2c --- /dev/null +++ b/applications/rview/rviewTypeMan.cpp @@ -0,0 +1,564 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * COMMENTS: + * None + */ + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + + + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + +#include "rviewTypeMan.hh" +#include "raslib/primitivetype.hh" +#include "raslib/structuretype.hh" + + +// use extremely large default values, otherwise the window doesn't open +// correctly on Windows. +const int rviewTypeMan::tman_width = 1000; +const int rviewTypeMan::tman_height = 2000; +const int rviewTypeMan::tman_border = 8; +const int rviewTypeMan::tman_basewidth = 100; +const int rviewTypeMan::tman_cheight = 30; +const int rviewTypeMan::tman_bheight = 30; +const int rviewTypeMan::tman_bwidth = 50; + +// Type keywords +const char rviewTypeMan::structName[] = "struct"; +const char rviewTypeMan::marrayName[] = "marray"; + +// String constants for type names +const char rviewTypeMan::typeBool[] = "bool"; +const char rviewTypeMan::typeChar[] = "char"; +const char rviewTypeMan::typeOctet[] = "octet"; +const char rviewTypeMan::typeShort[] = "short"; +const char rviewTypeMan::typeUShort[] = "ushort"; +const char rviewTypeMan::typeLong[] = "long"; +const char rviewTypeMan::typeULong[] = "ulong"; +const char rviewTypeMan::typeFloat[] = "float"; +const char rviewTypeMan::typeDouble[] = "double"; + + + +void rviewTypeMan::initShare(rviewFrame *parentWindow) +{ + numStruct = 0; numMembers = 0; typeDepth = 0; + structures = NULL; members = NULL; offsets = NULL; primtypes = NULL; + typeNames = NULL; + myType = NULL; panel = NULL; + parent = parentWindow; +} + + +rviewTypeMan::rviewTypeMan(rviewFrame *parentWindow) : rviewFrame(NULL, lman->lookup("titleTypeMan"), 0, 0, tman_width, tman_height) +{ + initShare(parentWindow); +} + + +rviewTypeMan::rviewTypeMan(rviewFrame *parentWindow, const r_Type *type) : rviewFrame(NULL, lman->lookup("titleTypeMan"), 0, 0, tman_width, tman_height) +{ + initShare(parentWindow); + setType(type); +} + + +rviewTypeMan::~rviewTypeMan(void) +{ + user_event usr; + + usr.type = usr_child_closed; usr.data = (void*)this; + if (parent != NULL) parent->userEvent(usr); + + clearData(); +} + + +void rviewTypeMan::unlinkParent(void) +{ + parent = NULL; +} + + +void rviewTypeMan::clearData(void) +{ + if (structures != NULL) {delete [] structures; structures = NULL;} + if (members != NULL) {delete [] members; members = NULL;} + if (offsets != NULL) {delete [] offsets; offsets = NULL;} + if (primtypes != NULL) {delete [] primtypes; primtypes = NULL;} + if (typeNames != NULL) {delete [] typeNames; typeNames = NULL;} + + if (myType != NULL) {delete myType; myType = NULL;} + + if (panel != NULL) {delete panel; panel = NULL;} + + initShare(parent); +} + + +// The name can't be read from cloned r_Types (not copied). Bug? +void rviewTypeMan::parsePrimitiveType(const r_Primitive_Type *tp, const char *name, unsigned int &numm, unsigned int offset, wxRect *bbox) +{ + if (members != NULL) + { + char buffer[STRINGSIZE]; + const char *tname = NULL; + + switch (tp->type_id()) + { + case r_Primitive_Type::BOOL: tname = typeBool; break; + case r_Primitive_Type::CHAR: tname = typeChar; break; + case r_Primitive_Type::OCTET: tname = typeOctet; break; + case r_Primitive_Type::SHORT: tname = typeShort; break; + case r_Primitive_Type::USHORT: tname = typeUShort; break; + case r_Primitive_Type::LONG: tname = typeLong; break; + case r_Primitive_Type::ULONG: tname = typeULong; break; + case r_Primitive_Type::FLOAT: tname = typeFloat; break; + case r_Primitive_Type::DOUBLE: tname = typeDouble; break; + default: break; + } + typeNames[numm] = tname; + sprintf(buffer, "%s (%s)", name, (tname == NULL) ? "" : tname); + members[numm]->SetLabel(buffer); + members[numm]->SetSize(bbox->x, bbox->y, bbox->width, bbox->height); + offsets[numm] = offset; + primtypes[numm] = tp->type_id(); + } + numm++; +} + + +void rviewTypeMan::parseStructType(const r_Structure_Type *tp, unsigned int &nums, unsigned int &numm, unsigned int depth, unsigned int offset, wxRect *bbox) +{ + wxRect pos; + unsigned int thisStruct = nums++; + + if (bbox != NULL) + { + pos.x = bbox->x + tman_border; + pos.width = bbox->width - 2*tman_border; + pos.y = bbox->y + tman_border; + pos.height = tman_cheight; + } + + if (depth > typeDepth) typeDepth = depth; + + r_Structure_Type::attribute_iterator iter(tp->defines_attribute_begin()); + while (iter != tp->defines_attribute_end()) + { + r_Type *newType; + unsigned int off = offset + (*iter).offset(); + + //(*iter).print_status(cout); cout << " --- " << (*iter).name() << endl; + newType = (*iter).type_of().clone(); + if (newType->isStructType()) + { + parseStructType((const r_Structure_Type*)newType, nums, numm, depth+1, off, &pos); + } + else + { + parsePrimitiveType((const r_Primitive_Type*)newType, (*iter).name(), numm, off, &pos); + pos.y += pos.height; + } + delete newType; + iter++; + } + if (structures != NULL) + { + structures[thisStruct]->SetSize(bbox->x, bbox->y, bbox->width, pos.y - bbox->y + tman_border/2); + bbox->y = pos.y + tman_border; + } +} + + +void rviewTypeMan::setType(const r_Type *type) +{ + unsigned int nums, numm, i; + wxRect bbox; + + clearData(); + panel = new wxPanel(this, -1, -1, -1, -1); + myType = type->clone(); + + if (myType->isStructType()) + parseStructType((r_Structure_Type*)type, numStruct, numMembers, 1); + else + parsePrimitiveType((r_Primitive_Type*)type, "", numMembers); + + if (numStruct != 0) + { + structures = new wxGroupBox*[numStruct]; + for (i=0; i<numStruct; i++) structures[i] = new wxGroupBox(panel, "", -1, -1, -1, -1); + } + members = new rviewCheckBox*[numMembers]; + for (i=0; i<numMembers; i++) + { + members[i] = new rviewCheckBox(panel); + } + offsets = new unsigned int[numMembers]; + primtypes = new unsigned char[numMembers]; + typeNames = new const char*[numMembers]; + baseTypeLength = ((r_Base_Type*)myType)->size(); + + nums = 0; numm = 0; + bbox.x = tman_border; bbox.y = tman_border; + bbox.width = tman_basewidth + 2*tman_border * typeDepth; + bbox.height = numMembers * tman_cheight + 2*tman_border * numStruct; + if (myType->isStructType()) + parseStructType((r_Structure_Type*)type, nums, numm, 1, 0, &bbox); + else + parsePrimitiveType((r_Primitive_Type*)type, "", numm, 0, &bbox); + + closeBut = new rviewButton(panel); + convertBut = new rviewButton(panel); + + label(); + + bbox.width += 2*tman_border; bbox.height += 2*tman_border; + closeBut->SetSize(tman_border, bbox.height, tman_bwidth, tman_bheight); + convertBut->SetSize(bbox.width - tman_border - tman_bwidth, bbox.height, tman_bwidth, tman_bheight); + bbox.height += tman_bheight + tman_border + rview_window_extra_height; + + SetSize(0, 0, bbox.width, bbox.height); + panel->SetSize(0, 0, bbox.width, bbox.height); + + //for (i=0; i<numMembers; i++) cout << (int)(offsets[i]) << ' '; cout << endl; + + Show(TRUE); +} + + +void rviewTypeMan::label(void) +{ + SetTitle(lman->lookup("titleTypeMan")); + closeBut->SetLabel(lman->lookup("textClose")); + convertBut->SetLabel(lman->lookup("textConvert")); +} + + +int rviewTypeMan::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + if (&obj == (wxObject*)closeBut) + { + Close(TRUE); + return 1; + } + else if (&obj == (wxObject*)convertBut) + { + user_event usr; + + usr.type = usr_typeman_convert; usr.data = (void*)this; + if (parent != NULL) parent->userEvent(usr); + return 1; + } + } + return 0; +} + + +void rviewTypeMan::OnSize(int w, int h) +{ + if (panel != NULL) + { + panel->SetSize(0, 0, w, h); + } +} + + +/* + * Warning: this is in fact quit low-level. Assumes alignment -- handle with care! + */ +int rviewTypeMan::convert(r_Ref<r_GMarray> &src, r_Ref<r_GMarray> &dest) +{ + unsigned int i, newMembers=0; + + //cout << "rviewTypeMan::convert()" << endl; + + for (i=0; i<numMembers; i++) + { + if (members[i]->GetValue()) newMembers++; + } + if (newMembers == 0) + { + cerr << "No types selected; ignored." << endl; + return -1; + } + const r_Type *srcBaseType = src->get_base_type_schema(); + if (srcBaseType == NULL) + { + cerr << "No base type information available for object; ignored." << endl; + return -1; + } + if (((r_Base_Type*)srcBaseType)->size() != baseTypeLength) + { + cerr << "Base type incompatible; ignored." << endl; + return -1; + } + + unsigned int *newOff = new unsigned int[newMembers]; + unsigned int *srcIndex = new unsigned int[newMembers]; + unsigned int j; + + for (i=0, j=0; i<numMembers; i++) + { + if (members[i]->GetValue()) srcIndex[j++] = i; + } + //cout << "src index OK" << endl; + + unsigned int off; + int needsAlign=1; + + for (i=0, off=0; i<newMembers; i++) + { + int increment = 1; + + // align certain types + switch (primtypes[srcIndex[i]]) + { + case r_Primitive_Type::SHORT: + case r_Primitive_Type::USHORT: + off = (off+1) & ~1; increment = 2; if (needsAlign < 2) needsAlign = 2; + break; + case r_Primitive_Type::LONG: + case r_Primitive_Type::ULONG: + case r_Primitive_Type::FLOAT: + off = (off+3) & ~3; increment = 4; if (needsAlign < 4) needsAlign = 4; + break; + case r_Primitive_Type::DOUBLE: + off = (off+3) & ~3; increment = 8; if (needsAlign < 4) needsAlign = 4; + break; + default: break; + } + newOff[i] = off; off += increment; + } + // needsAlign is now the most coarse alignment needed within the type. The start + // address of the type itself must also be aligned to this coarseness (e.g. + // struct {long, char}) + unsigned int length = (off + (needsAlign-1)) & ~(needsAlign-1); + //cout << "off = " << off << ", length = " << length << endl; + + r_Minterval interv = src->spatial_domain(); + +#ifdef __VISUALC__ + // Need this rather roundabout syntax for Visual C + dest = (r_Ref<r_GMarray>&)(new r_GMarray); +#else + dest = new r_GMarray; +#endif + + int dim = interv.dimension(); + long cells = 1; + for (i=0; i<(unsigned int)dim; i++) + { + cells *= (interv[i].high() - interv[i].low() + 1); + } + // claim a new array and initialize it, if necessary + char *newArray = new char[cells * length]; + if (needsAlign != 1) memset(newArray, 0, cells*length); + + const char *srcArray = src->get_array(); + dest->set_array(newArray); + dest->set_type_length(length); + dest->set_spatial_domain(interv); + dest->set_array_size(cells*length); + + unsigned int srcLength = src->get_type_length(); + long count; + + //cout << "start copying " << newMembers << " members..." << endl; + for (i=0; i<newMembers; i++) + { + //cout << "index " << srcIndex[i] << endl; + switch (primtypes[srcIndex[i]]) + { + case r_Primitive_Type::BOOL: + case r_Primitive_Type::CHAR: + case r_Primitive_Type::OCTET: + { + const char *s = srcArray + offsets[srcIndex[i]]; + char *d = newArray + newOff[i]; + + for (count=0; count<cells; count++) + { + *d = *s; + s += srcLength; d += length; + } + } + break; + case r_Primitive_Type::SHORT: + case r_Primitive_Type::USHORT: + { + const short *s = (const short*)(srcArray + offsets[srcIndex[i]]); + short *d = (short*)(newArray + newOff[i]); + + for (count=0; count<cells; count++) + { + *d = *s; + s = (const short*)(((char*)s) + srcLength); + d = (short*)(((char*)d) + length); + } + } + break; + case r_Primitive_Type::LONG: + case r_Primitive_Type::ULONG: + case r_Primitive_Type::FLOAT: + { + const long *s = (const long*)(srcArray + offsets[srcIndex[i]]); + long *d = (long*)(newArray + newOff[i]); + + for (count=0; count<cells; count++) + { + *d = *s; + s = (const long*)(((char*)s) + srcLength); + d = (long*)(((char*)d) + length); + } + } + break; + case r_Primitive_Type::DOUBLE: + { + const double *s = (const double*)(srcArray + offsets[srcIndex[i]]); + double *d = (double*)(newArray + newOff[i]); + + for (count=0; count<cells; count++) + { + *d = *s; + s = (const double*)(((char*)s) + srcLength); + d = (double*)(((char*)d) + length); + } + } + break; + default: + break; + } + } + + delete [] newOff; + + //cout << "build type..." << endl; + int strLength = 0; + for (i=0; i<newMembers; i++) + { + strLength += strlen(typeNames[srcIndex[i]]) + 2; + } + // in case it's a structure type we also need space for "struct { ... }" + if (newMembers > 1) strLength += strlen(structName) + 5; + // standard wrapper "marray < ... , dim >" + strLength += strlen(marrayName) + 5 + 5; + + char *typeString = new char[strLength]; + char *b = typeString; + b += sprintf(b, "%s < ", marrayName); + if (newMembers > 1) b += sprintf(b, "%s { ", structName); + for (i=0; i<newMembers; i++) + { + b += sprintf(b, "%s, ", typeNames[srcIndex[i]]); + } + if (newMembers > 1) + strcpy(b-2, " }"); + else + b -= 2; + sprintf(b, ", %d >", dim); + + //cout << "TYPE: " << typeString << endl; + dest->set_type_structure(typeString); + + //cout << "Checkpoint" << endl; + + // Query for the base type the first time only! + if (strlen(baseTypeName) == 0) + { + // find the base type name... + rviewBaseType baseType = rbt_none; + if ((newMembers == 3) && (length == 3)) baseType = rbt_rgb; + if (newMembers == 1) + { + switch (primtypes[srcIndex[0]]) + { + case r_Primitive_Type::BOOL: baseType = rbt_bool; break; + case r_Primitive_Type::CHAR: baseType = rbt_char; break; + case r_Primitive_Type::OCTET: baseType = rbt_uchar; break; + case r_Primitive_Type::SHORT: baseType = rbt_short; break; + case r_Primitive_Type::USHORT: baseType = rbt_ushort; break; + case r_Primitive_Type::LONG: baseType = rbt_long; break; + case r_Primitive_Type::ULONG: baseType = rbt_ulong; break; + case r_Primitive_Type::FLOAT: baseType = rbt_float; break; + case r_Primitive_Type::DOUBLE: baseType = rbt_double; break; + default: break; + } + } + if ((baseType != rbt_none) && (dim < MAXIMUM_DIMENSIONS)) + { + baseTypeName = rviewTypeNames[baseType][dim-1]; + } + else + { + const char *prompt = lman->lookup("promptEnterType"); + char *msg = new char[strlen(prompt) + strlen(typeString) + 2]; + sprintf(msg, "%s %s", prompt, typeString); + baseTypeName = ::wxGetTextFromUser(msg, lman->lookup("titleEnterType")); + delete [] msg; + } + } + dest->set_type_by_name(baseTypeName); + //cout << "Base type name = " << baseTypeName << endl; + + /*dest->print_status(); + const r_Type *btype = dest->get_base_type_schema(); + if (btype == NULL) cout << "no base type schema" << endl; + btype = r_Type::get_any_type(dest->get_type_structure()); + if (btype == NULL) cout << "string can't be parsed?!?" << endl;*/ + + delete [] typeString; + delete [] srcIndex; + + return 0; +} diff --git a/applications/rview/rviewTypeMan.hh b/applications/rview/rviewTypeMan.hh new file mode 100644 index 0000000..ef12499 --- /dev/null +++ b/applications/rview/rviewTypeMan.hh @@ -0,0 +1,106 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * COMMENTS: + * None + */ + +#ifndef _RVIEW_TYPE_MAN_H_ +#define _RVIEW_TYPE_MAN_H_ + + +#ifdef __GNUG__ +#pragma interface +#endif + + +#include "rviewUtils.hh" +#include "rviewDisplay.hh" + + + +class rviewTypeMan: public rviewFrame +{ + public: + + rviewTypeMan(rviewFrame *parentWindow); + rviewTypeMan(rviewFrame *parentWindow, const r_Type *type); + ~rviewTypeMan(void); + + void unlinkParent(void); + + void setType(const r_Type *type); + void label(void); + int process(wxObject &obj, wxEvent &evt); + void OnSize(int w, int h); + int convert(r_Ref<r_GMarray> &src, r_Ref<r_GMarray> &dest); + + + protected: + + void clearData(void); + + wxPanel *panel; + wxGroupBox **structures; + rviewCheckBox **members; + unsigned int *offsets; + unsigned char *primtypes; + unsigned int numStruct, numMembers, typeDepth; + unsigned int baseTypeLength; + r_Type *myType; + rviewFrame *parent; + rviewButton *closeBut, *convertBut; + const char **typeNames; + DynamicString baseTypeName; + + + private: + + void initShare(rviewFrame *parentWindow); + void parseStructType(const r_Structure_Type *tp, unsigned int &nums, unsigned int &numm, unsigned int depth, unsigned int offset=0, wxRect *bbox=NULL); + void parsePrimitiveType(const r_Primitive_Type *tp, const char *name, unsigned int &numm, unsigned int offset=0, wxRect *bbox=NULL); + + static const char structName[]; + static const char marrayName[]; + static const char typeBool[]; + static const char typeChar[]; + static const char typeOctet[]; + static const char typeShort[]; + static const char typeUShort[]; + static const char typeLong[]; + static const char typeULong[]; + static const char typeFloat[]; + static const char typeDouble[]; + + // constants + static const int tman_width; + static const int tman_height; + static const int tman_border; + static const int tman_basewidth; + static const int tman_cheight; + static const int tman_bheight; + static const int tman_bwidth; +}; + +#endif diff --git a/applications/rview/rviewTypes.hh b/applications/rview/rviewTypes.hh new file mode 100644 index 0000000..20830f7 --- /dev/null +++ b/applications/rview/rviewTypes.hh @@ -0,0 +1,81 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * COMMENTS: + * This file is created automatically by the rasdl command. + * DO NOT EDIT + */ + + +#ifndef _RVTYPES_HH_ +#define _RVTYPES_HH_ + +//------------------------------------------------------------ +// Includes +//------------------------------------------------------------ + +#if (defined(EARLY_TEMPLATE) && !defined(__EXECUTABLE__)) +#define __EXECUTABLE__ +#define __UNDEF_EXEC_TYPES__ +#endif + +#include "rasodmg/ref.hh" +#include "rasodmg/transaction.hh" +#include "rasodmg/database.hh" +#include "rasodmg/set.hh" +#include "rasodmg/iterator.hh" +#include "rasodmg/marray.hh" +#include "raslib/odmgtypes.hh" + +#if (defined(EARLY_TEMPLATE) && defined(__UNDEF_EXEC_TYPES__)) +#undef __EXECUTABLE__ +#undef __UNDEF_EXEC_TYPES__ +#endif + +/*[2,33]*//* TYPEDEF ------------------------- GreyImage */ +typedef r_Marray<r_Char/*[*:*,*:*]*/> GreyImage; + +/*[3,24]*//* TYPEDEF ------------------------- GreySet */ +typedef r_Set<r_Ref<GreyImage> > GreySet; + +/*[6,36]*//* TYPEDEF ------------------------- BoolImage */ +typedef r_Marray<r_Boolean/*[*:*,*:*]*/> BoolImage; + +/*[7,24]*//* TYPEDEF ------------------------- BoolSet */ +typedef r_Set<r_Ref<BoolImage> > BoolSet; + +/*[10,1]*//* STRUCT -------------------------- RGBPixel */ +struct RGBPixel { + r_Char red; + r_Char green; + r_Char blue; +}; +/*[11,37]*//* TYPEDEF ------------------------- RGBImage */ +typedef r_Marray<RGBPixel/*[*:*,*:*]*/> RGBImage; + +/*[12,23]*//* TYPEDEF ------------------------- RGBSet */ +typedef r_Set<r_Ref<RGBImage> > RGBSet; + +#endif + diff --git a/applications/rview/rviewUtils.cpp b/applications/rview/rviewUtils.cpp new file mode 100644 index 0000000..762ee10 --- /dev/null +++ b/applications/rview/rviewUtils.cpp @@ -0,0 +1,3630 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** +* PURPOSE: +* +* Central definitions of shared objects and constants, global tool functions. +* +* - All menu codes used in the program (MENU_...) +* - Global tool functions for handling collections, base types, projection +* strings, X events, ... . See ``Global functions''. +* - rviewFrame class which must be the base class of all frames used in rView. +* - rviewFrameMgr class for handling all of rView's frames (dispatching events +* etc) +* - rviewMultiline class for displaying several lines of non-editable text. +* - rviewDialog class which is the base class for all rView's dialog windows. +* - rviewErrorbox class, derived from rviewDialog. +* - rviewProgress class, derived from rviewDialog. +* - rviewResults class for displaying database results and dispatching object +* viewers. Relies on code provided by rviewMDD for scaling/resampling/endian +* conversions. +* - rviewAbout class for displaying information about rView itself. +* - rviewStringSet class for displaying a set of strings in a scrollable +* list box. +* +* COMMENTS: +* none +*/ + + +// Was file included via header? Then mask out all non-template code +#if (!defined(EARLY_TEMPLATE) || !defined(__EXECUTABLE__)) + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + +// #include "wb_timer.h" -- PB 2006-jan-01 + + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <iostream.h> + + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + + +#include "raslib/rmdebug.hh" +#include "raslib/primitivetype.hh" +#include "raslib/structuretype.hh" + +#include "rviewUtils.hh" +#include "rviewMDD.hh" +#include "rviewPrefs.hh" +#include "rviewColMap.hh" +#include "rviewDModes.hh" +#include "rviewOSection.hh" +#include "rviewTypes.hh" +#include "rviewThumb.hh" +#include "rviewSound.hh" +#include "rviewTypeMan.hh" +#include "labelManager.hh" + + +#ifdef __VISUALC__ +const int rview_window_extra_height = 24; +const int rview_choice_sub_width = 0; +#else +const int rview_window_extra_height = 0; +const int rview_choice_sub_width = 32; +#endif + + + + + + +/* + * Global data + */ + +// Base type to string translation tables + +#if (MAXIMUM_DIMENSIONS != 4) +#error "Adapt tables for MAXIMUM DIMENSIONS!" +#endif + +// String names of base types +char *rviewBaseTypes[] = { + "none", + "Boolean", + "Char", + "Octet", + "Short", + "UShort", + "Long", + "ULong", + "RGBPixel", + "Float", + "Double" +}; + +// String names of objects of baseType x and dimension y (-> rviewBaseType) +char *rviewTypeNames[][MAXIMUM_DIMENSIONS] = { + {"?", "?", "?", "?"}, + {"BoolString", "BoolImage", "BoolCube", "BoolCube4"}, + {"GreyString", "GreyImage", "GreyCube", "GreyCube4"}, + {"OctetString", "OctetImage", "OctetCube", "OctetCube4"}, + {"ShortString", "ShortImage", "ShortCube", "ShortCube4"}, + {"UShortString", "UShortImage", "UShortCube", "UShortCube4"}, + {"LongString", "LongImage", "LongCube", "LongCube4"}, + {"ULongString", "ULongImage", "ULongCube", "ULongCube4"}, + {"RGBString", "RGBImage", "RGBCube", "RGBCube4"}, + {"FloatString", "FloatImage", "FloatCube", "FloatCube4"}, + {"DoubleString", "DoubleImage" "DoubleCube", "DoubleCube4"} +}; + +// The same for sets +char *rviewSetNames[][MAXIMUM_DIMENSIONS] = { + {"?", "?", "?", "?"}, + {"BoolSet1", "BoolSet", "BoolSet3", "BoolSet4"}, + {"GreySet1", "GreySet", "GreySet3", "GreySet4"}, + {"OctetSet1", "OctetSet", "OctetSet3", "OctetSet4"}, + {"ShortSet1", "ShortSet", "ShortSet3", "ShortSet4"}, + {"UShortSet1", "UShortSet", "UShortSet3", "UShortSet4"}, + {"LongSet1", "LongSet", "LongSet3", "LongSet4"}, + {"ULongSet1", "ULongSet", "ULongSet3", "ULongSet4"}, + {"RGBSet1", "RGBSet", "RGBSet3", "RGBSet4"}, + {"FloatSet1", "FloatSet", "FloatSet3", "FloatSet4"}, + {"DoubleSet1", "DoubleSet", "DoubleSet3", "DoubleSet4"} +}; + + + +unsigned char lowerCaseTable[256]; + + + +// Various support classes +rviewFrameMgr *frameManager = NULL; + +labelManager *lman = NULL; + + + + + + +/* + * GLOBAL functions + */ + +// Frees all memory allocated by a collection descriptor. +void rviewDeleteCollection(collection_desc *coll) +{ + if (coll != NULL) + { + int i; + collection_desc *ptr = coll; + + if (coll->collName != NULL) delete [] coll->collName; + if (coll->collType != NULL) delete [] coll->collType; + if (coll->collInfo != NULL) delete [] coll->collInfo; + + for (i=0; i<coll->number; i++) + { + if (coll->mddObjs != NULL) + { + if (!coll->mddObjs[i].mdd.is_null()) + { + coll->mddObjs[i].mdd.destroy(); + } + } + else if (coll->strObjs != NULL) + { + if (coll->strObjs[i] != NULL) + { + delete [] coll->strObjs[i]; + } + } + } + if (coll->mddObjs != NULL) delete [] coll->mddObjs; + if (coll->strObjs != NULL) delete [] coll->strObjs; + + delete coll; + } +} + + + +/* + * Support function for rviewParseProjection + */ +static const char *rviewParseIndexMapping(const char *s, int idx, int dims, r_Point *mapIndex) +{ + const char *b, *d; + r_Range value; + + b = s+1; + if (mapIndex == NULL) + { + cerr << "Mode doesn't support reordering of dimensions." << endl; + } + + value = (r_Range)strtol(b, (char**)&d, 0); + if (b == d) return NULL; + + if ((value < 0) || (value >= (r_Range)dims)) + { + cerr << "Bad dimension index " << value << endl; + value = (r_Range)idx; + } + if (mapIndex != NULL) (*mapIndex)[idx] = value; + + while ((*d == ' ') || (*d == '\t')) d++; + if (*d == ']') return d+1; + return NULL; +} + + +/* + * Translate a projection string (now RasDaMan style, i.e. things + * like *:* are also allowed) into two diagonal r_Points. Returns + * number of dimension of projection string for success, 0 for an error. + */ + +int rviewParseProjection(const r_Minterval &interv, r_Point &pt1, r_Point &pt2, const char *projString, unsigned int *freeDims, r_Point *mapIndex) +{ + int dims, i; + const char *b, *d; + r_Range value; + + if (freeDims != NULL) *freeDims = 0; + dims = interv.dimension(); + pt1 = r_Point(dims); pt2 = r_Point(dims); + + b = projString; i = 0; + while (*b != '\0') + { + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == ',') b++; + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == '\0') break; + if (i >= dims) return 0; + if (*b == '*') + { + pt1[i] = interv[i].low(); pt2[i] = interv[i].high(); b++; + } + else + { + value = (r_Range)strtol(b, (char**)&d, 0); + // no valid number found? + if (b == d) return 0; + b = d; + if ((value < interv[i].low()) || (value > interv[i].high())) return 0; + pt1[i] = value; pt2[i] = value; + } + if (mapIndex != NULL) (*mapIndex)[i] = i; + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == '[') // Explicit index-mapping? + { + if ((b = rviewParseIndexMapping(b, i, dims, mapIndex)) == NULL) return 0; + } + else if (*b == ':') // the upper boundaries follow after a colon. + { + if (freeDims != NULL) *freeDims |= (1<<i); + b++; + if (*b == '\0') break; + if (*b == '*') + { + value = interv[i].high(); b++; + } + else + { + value = (r_Range)strtol(b, (char**)&d, 0); + if (b == d) return 0; + b = d; + if ((value < interv[i].low()) || (value > interv[i].high())) return 0; + } + pt2[i] = value; + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == '[') + { + if ((b = rviewParseIndexMapping(b, i, dims, mapIndex)) == NULL) return 0; + } + } + i++; + } + return i; +} + + + + + +/* + * Determine the base type of an MDD object, returning a simple identifier. + */ +rviewBaseType rviewGetBasetype(r_Object *obj) +{ + rviewBaseType baseType; + const r_Type *bt; + + baseType = rbt_none; + + // New type schema... + bt = obj->get_type_schema(); + if (bt->type_id() == r_Type::MARRAYTYPE) + bt = ((r_GMarray*)obj)->get_base_type_schema(); + else if (bt->type_id() == r_Type::COLLECTIONTYPE) + bt = ((r_Collection<r_Ref_Any>*)obj)->get_element_type_schema(); + + if (bt != NULL) + { + if (((r_Type*)bt)->isStructType()) + { + if (((r_Base_Type*)bt)->size() == 3) + baseType = rbt_rgb; + else + baseType = rbt_none; + } + else + { + r_Primitive_Type *pt = (r_Primitive_Type *)bt; + + switch (pt->type_id()) + { + case r_Primitive_Type::BOOL: baseType = rbt_bool; break; + case r_Primitive_Type::CHAR: baseType = rbt_char; break; + case r_Primitive_Type::OCTET: baseType = rbt_uchar; break; + case r_Primitive_Type::SHORT: baseType = rbt_short; break; + case r_Primitive_Type::USHORT: baseType = rbt_ushort; break; + case r_Primitive_Type::LONG: baseType = rbt_long; break; + case r_Primitive_Type::ULONG: baseType = rbt_ulong; break; + case r_Primitive_Type::FLOAT: baseType = rbt_float; break; + case r_Primitive_Type::DOUBLE: baseType = rbt_double; break; + default: baseType = rbt_none; break; + } + } + } + else + { + char *name = (char*)(obj->get_type_name()); + if (name != NULL) + { + if (strcmp(name, "GreyImage") == 0) baseType = rbt_char; + else if (strcmp(name, "BoolImage") == 0) baseType = rbt_bool; + else if (strcmp(name, "RGBImage") == 0) baseType = rbt_rgb; + } + } + return baseType; +} + + + +/* + * rviewPrintTypedCell() prints the contents of a cell with a given base type into the buffer, + * returning the number of characters written. + */ + +// For internal use only +static void printPrimitiveCell(r_Primitive_Type *primType, char **buff, char *data, int numberBase) +{ + char *b = *buff; + + switch (numberBase) + { + case 8: + switch (primType->type_id()) + { + case r_Primitive_Type::BOOL: + b += sprintf(b, "%3o", *((r_Boolean*)data)); break; + case r_Primitive_Type::CHAR: + b += sprintf(b, "%3o", *((r_Char*)data)); break; + case r_Primitive_Type::OCTET: + b += sprintf(b, "%3o", *((r_Octet*)data)); break; + case r_Primitive_Type::SHORT: + b += sprintf(b, "%6o", *((r_Short*)data)); break; + case r_Primitive_Type::USHORT: + b += sprintf(b, "%6o", *((r_UShort*)data)); break; + case r_Primitive_Type::LONG: + b += sprintf(b, "%11lo", *((r_Long*)data)); break; + case r_Primitive_Type::ULONG: + b += sprintf(b, "%11lo", *((r_ULong*)data)); break; + case r_Primitive_Type::FLOAT: + b += sprintf(b, "%011o", *((r_Long*)data)); break; + case r_Primitive_Type::DOUBLE: + b += sprintf(b, "%011o:%011o", ((r_Long*)data)[0], ((r_Long*)data)[1]); break; + default: + break; + } + break; + case 16: + switch (primType->type_id()) + { + case r_Primitive_Type::BOOL: + b += sprintf(b, "%02x", *((r_Boolean*)data)); break; + case r_Primitive_Type::CHAR: + b += sprintf(b, "%02x", *((r_Char*)data)); break; + case r_Primitive_Type::OCTET: + b += sprintf(b, "%02x", *((r_Octet*)data)); break; + case r_Primitive_Type::SHORT: + b += sprintf(b, "%04x", *((r_Short*)data)); break; + case r_Primitive_Type::USHORT: + b += sprintf(b, "%04x", *((r_UShort*)data)); break; + case r_Primitive_Type::LONG: + b += sprintf(b, "%08x", *((r_Long*)data)); break; + case r_Primitive_Type::ULONG: + b += sprintf(b, "%08x", *((r_ULong*)data)); break; + case r_Primitive_Type::FLOAT: + b += sprintf(b, "%08x", *((r_Long*)data)); break; + case r_Primitive_Type::DOUBLE: + b += sprintf(b, "%08x:%08x", ((r_Long*)data)[0], ((r_Long*)data)[1]); break; + default: + break; + } + break; + default: + switch (primType->type_id()) + { + case r_Primitive_Type::BOOL: + b += sprintf(b, "%3d", *((r_Boolean*)data)); break; + case r_Primitive_Type::CHAR: + b += sprintf(b, "%3d", *((r_Char*)data)); break; + case r_Primitive_Type::OCTET: + b += sprintf(b, "%3d", *((r_Octet*)data)); break; + case r_Primitive_Type::SHORT: + b += sprintf(b, "%5d", *((r_Short*)data)); break; + case r_Primitive_Type::USHORT: + b += sprintf(b, "%5d", *((r_UShort*)data)); break; + case r_Primitive_Type::LONG: + b += sprintf(b, "%10ld", *((r_Long*)data)); break; + case r_Primitive_Type::ULONG: + b += sprintf(b, "%10ld", *((r_ULong*)data)); break; + case r_Primitive_Type::FLOAT: + b += sprintf(b, "%g", *((r_Float*)data)); break; + case r_Primitive_Type::DOUBLE: + b += sprintf(b, "%g", *((r_Double*)data)); break; + default: + break; + } + break; + } + *buff = b; +} + +// For internal use only +void printStructuredCell(r_Structure_Type *structType, char **buff, char *data, int numberBase) +{ + r_Type *newType; + unsigned long off; + char *b; + + b = *buff; + *b++ = '{'; + r_Structure_Type::attribute_iterator iter(structType->defines_attribute_begin()); + while (iter != structType->defines_attribute_end()) + { + newType = (*iter).type_of().clone(); + off = (*iter).offset(); + + if (newType->isStructType()) + { + r_Structure_Type *newStructType = (r_Structure_Type*)newType; + printStructuredCell(newStructType, &b, data + off, numberBase); + } + else + { + r_Primitive_Type *newPrimType = (r_Primitive_Type*)newType; + printPrimitiveCell(newPrimType, &b, data + off, numberBase); + } + delete newType; + *b++ = ','; + iter++; + } + b[-1] = '}'; *b = '\0'; + *buff = b; +} + +int rviewPrintTypedCell(const r_Type *baseType, char *buffer, char *data, int numberBase) +{ + char *b = buffer; + + if (((r_Type*)baseType)->isStructType()) + { + r_Structure_Type *structType = (r_Structure_Type*)baseType; + printStructuredCell(structType, &b, data, numberBase); + } + else + { + r_Primitive_Type *primType = (r_Primitive_Type*)baseType; + printPrimitiveCell(primType, &b, data, numberBase); + } + return (int)(b - buffer); +} + + + + +/* + * Quicksort a char *[] + */ + +#define QUICKSORT_STRING_SWAP(x,y) h=array[x]; array[x]=array[y]; array[y]=h; + +void rviewQuicksortStrings(char *array[], int from, int to) +{ + while (from < to) + { + int i, j; + char *h; + + j = (from+to)/2; + QUICKSORT_STRING_SWAP(from, j); j=from; + for (i=from+1; i<=to; i++) + { + if (strcmp(array[i], array[from]) < 0) + { + j++; + QUICKSORT_STRING_SWAP(i, j); + } + } + QUICKSORT_STRING_SWAP(from, j); + + if ((j-from) < (to-j)) + { + rviewQuicksortStrings(array, from, j-1); + from = j+1; + } + else + { + rviewQuicksortStrings(array, j+1, to); + to = j-1; + } + } +} + + + +/* + * Init character lookup tables (e.g. lower case table) + */ +void rviewInitCharacterTables(void) +{ + int i; + + for (i=0; i<256; i++) lowerCaseTable[i] = tolower(i); +} + + + +#define LOOKUP_KEYWORD_CORE \ + while (count != 0) \ + { \ + b = (const unsigned char*)(kti[pos].keyword); \ + d = (const unsigned char*)key; \ + while (TRANSLATE_CHARACTER(b) == TRANSLATE_CHARACTER(d)) \ + { \ + if (*b == 0) break; b++; d++; \ + } \ + if ((*b == 0) && (*d == 0)) return kti[pos].ident; \ + if (TRANSLATE_CHARACTER(b) >= TRANSLATE_CHARACTER(d)) \ + { \ + pos -= step; if (pos < 0) pos = 0; \ + } \ + else \ + { \ + pos += step; if (pos >= tabsize) pos = tabsize - 1; \ + } \ + step = (step+1) >> 1; count >>= 1; \ + } + +/* + * Lookup a keyword in a SORTED keyword_to_ident table. Case sensitivity + * is optional but has to match the sorting order of the table, of course. + */ +int rviewLookupKeyword(const char *key, const keyword_to_ident_c *kti, int tabsize, bool caseSensitive) +{ + int pos, step, count; + const unsigned char *b, *d; + + count = tabsize; + pos = (tabsize + 1) >> 1; if (pos >= tabsize) pos = tabsize - 1; + step = (pos + 1) >> 1; + if (caseSensitive) + { +#define TRANSLATE_CHARACTER(x) (*x) + LOOKUP_KEYWORD_CORE; +#undef TRANSLATE_CHARACTER + } + else + { +#define TRANSLATE_CHARACTER(x) lowerCaseTable[(*x)] + LOOKUP_KEYWORD_CORE; +#undef TRANSLATE_CHARACTER + } + return -1; +} + + + +/* Helper function making sure min/max of the init structure is used by the + colourspace mapper */ + +static void rviewEnsureCspaceRange(colourspaceMapper *csmap, const colourspace_params *cp) +{ + // make sure the min/max values are taken from the init structure as well + if (cp != NULL) + { + // assume it's initialized if either values differs from 0 + if ((cp->minVal != 0.0) || (cp->maxVal != 0.0)) + { + colourspace_params par; + csmap->getParameters(&par); + par.minVal = cp->minVal; + par.maxVal = cp->maxVal; + // do not do auto-update, otherwise we're stuck in an infinite loop + csmap->colourspaceChanged(&par, FALSE); + //cout << "MIN " << par.minVal << ", MAX " << par.maxVal << endl; + } + } +} + + +/* + * Check whether colourspace mapping is possible for the given + * base type and return 1 if so. *csmap must be a pointer to the + * colourspaceMapper to use or NULL if there isn't one yet in + * which case a new one will have been created on returning 1. + * If cmap == NULL the function just returns 0 or 1. If the int* + * arguments are not NULL they're set up correctly on exit too. + */ +int rviewCheckInitCspace(rviewBaseType baseType, colourspaceMapper **csmap, r_Ref<r_GMarray> &mddObj, bool fullRange, r_Minterval *domain, int w, int *newPitch, int *newDepth, int *newPad, int *virtualPitch, const colourspace_params *cp) +{ + colourspace_params par; + + switch (baseType) + { + case rbt_char: + case rbt_uchar: + case rbt_short: + case rbt_ushort: + if (csmap == NULL) return 1; + if (*csmap == NULL) + { + memcpy(&par, (cp == NULL) ? &(prefs->csp) : cp, sizeof(colourspace_params)); + *csmap = new colourspaceMapper(mddObj, baseType, &par, fullRange, domain); + } + else + (*csmap)->bindMapper(mddObj, baseType, fullRange, domain, cp); + + rviewEnsureCspaceRange(*csmap, cp); + + // always call this and let the mapper sort out whether it has to update + (*csmap)->buildCSTab15(); + if (newPitch == NULL) return 1; + if (*newPad < 32) *newPad = 32; + *newDepth = 15; + switch (*newPad) + { + case 32: *newPitch = (w*2 + 3) & ~3; break; + case 64: *newPitch = (w*2 + 7) & ~7; break; + case 128: *newPitch = (w*2 + 15) & ~15; break; + case 256: *newPitch = (w*2 + 31) & ~31; break; + default: + cerr << "Bad pad " << *newPad << endl; + return 0; + } + if (virtualPitch != NULL) *virtualPitch = *newPitch; + return 1; + break; + case rbt_long: + case rbt_ulong: // using 24bpp only makes sense for long + case rbt_float: + case rbt_double: // ... or larger types + { + int baseWidth; + + baseWidth = 4*w; + + if (csmap == NULL) return 1; + if (*csmap == NULL) + { + memcpy(&par, (cp == NULL) ? &(prefs->csp) : cp, sizeof(colourspace_params)); + *csmap = new colourspaceMapper(mddObj, baseType, &par, fullRange, domain); + } + else + (*csmap)->bindMapper(mddObj, baseType, fullRange, domain, cp); + + rviewEnsureCspaceRange(*csmap, cp); + + (*csmap)->buildCSTab24(); + if (newPitch == NULL) return 1; + if (*newPad < 32) *newPad = 32; + *newDepth = 32; + switch (*newPad) + { + case 32: *newPitch = (baseWidth + 3) & ~3; break; + case 64: *newPitch = (baseWidth + 7) & ~7; break; + case 128: *newPitch = (baseWidth + 15) & ~15; break; + case 256: *newPitch = (baseWidth + 31) & ~31; break; + default: cerr << "Bad pad " << *newPad << endl; + return 0; + } + if (virtualPitch != NULL) + *virtualPitch = (baseType == rbt_double) ? 2*(*newPitch) : (*newPitch); + return 1; + } + break; + default: break; + } + return 0; +} + + + +/* + * Smart number conversion functions. They also understand the 0x + * prefix and convert the number correctly. + */ + +long asctol(const char *str) +{ + return stringtol(str, NULL); +} + +int asctoi(const char *str) +{ + return (int)stringtol(str, NULL); +} + +double asctof(const char *str) +{ + return stringtof(str, NULL); +} + +long stringtol(const char *str, char **endptr) +{ + const char *b = str; + long value; + + while (isspace((unsigned int)(*b))) b++; + if ((b[0] == '0') && ((b[1] == 'x') || (b[1] == 'X'))) + { + b += 2; + value = strtol(b, endptr, 16); + } + else + { + value = strtol(b, endptr, 10); + } + if ((endptr != NULL) && ((const char*)(*endptr) == b)) *endptr = (char*)str; + return value; +} + +double stringtof(const char *str, char **endptr) +{ + const char *b = str; + double value; + + while (isspace((unsigned int)(*b))) b++; + if ((b[0] == '0') && ((b[1] == 'x') || (b[1] == 'X'))) + { + b += 2; + value = (double)strtol(b, endptr, 16); + } + else + { + value = strtod(b, endptr); + } + if ((endptr != NULL) && ((const char*)(*endptr) == b)) *endptr = (char*)str; + return value; +} + + + + + +/* + * Dynamic string class for easy management of dynamically allocated string storage + */ + +const char DynamicString::emptyString[] = ""; + + +DynamicString::DynamicString(void) +{ + myString = NULL; +} + +DynamicString::DynamicString(const DynamicString &ms) +{ + if (ms.myString == NULL) + { + myString = NULL; + } + else + { + myString = new char[strlen(ms.myString) + 1]; + strcpy(myString, ms.myString); + } +} + + +DynamicString::DynamicString(const char *str) +{ + if (str == NULL) + { + myString = NULL; + } + else + { + myString = new char[strlen(str) + 1]; + strcpy(myString, str); + } +} + + +DynamicString::~DynamicString(void) +{ + if (myString != NULL) delete [] myString; +} + + +DynamicString &DynamicString::first(const char *str, unsigned int num) +{ + if (myString != NULL) + { + delete [] myString; myString = NULL; + } + if (num != 0) + { + unsigned int len = strlen(str); + if (len > num) len = num; + myString = new char[len + 1]; + strncpy(myString, str, len); + myString[len] = '\0'; + } + return *this; +} + + +DynamicString &DynamicString::operator=(const DynamicString &ms) +{ + if (myString != NULL) + { + delete [] myString; myString = NULL; + } + if (ms.myString != NULL) + { + myString = new char[strlen(ms.myString) + 1]; + strcpy(myString, ms.myString); + } + return *this; +} + + +DynamicString &DynamicString::operator=(const char *str) +{ + if (myString != NULL) + { + delete [] myString; myString = NULL; + } + if (str != NULL) + { + myString = new char[strlen(str) + 1]; + strcpy(myString, str); + } + return *this; +} + + +const char *DynamicString::ptr(void) const +{ + if (myString == NULL) return emptyString; + return myString; +} + +DynamicString::operator const char*(void) const +{ + return ptr(); +} + + +bool DynamicString::operator==(const DynamicString &str) const +{ + return (strcmp(ptr(), str.ptr()) == 0); +} + + +bool DynamicString::operator==(const char *str) const +{ + return (strcmp(ptr(), str) == 0); +} + + + + + + +/* + * Generic handler function for events like button- and key-presses. + * It passes the event to the frameManager which then broadcasts + * it to all the frames it knows. If a frame recognizes obj as one + * of its children it claims the event, thus aborting the broadcast. + */ + +void rviewEventHandler(wxObject &obj, wxEvent &evt) +{ + if (frameManager != NULL) + frameManager->broadcastEvent(obj, evt); + //cout << endl; +} + + + + + + +/* + * rviewFrame member functions. + * This is an abstract class that is only used for deriving other + * (window/frame) classes from it. + */ + +rviewFrame::rviewFrame(wxFrame *parent, char *title, int x, int y, int w, int h) : wxFrame(parent, title, x, y, w, h) +{ + RMDBGONCE( 3, RMDebug::module_applications, "rviewFrame", "rviewFrame( " << this << ", ...)" ); + + if (frameManager != NULL) + frameManager->registerFrame(this); + + parentFrame = NULL; + // Init the size with illegal values to make sure the first OnSize gets through + frameWidth = -1; frameHeight = -1; + // All the frame's children should be destroyed when the frame is destroyed. + frames = new rviewFrameMgr(TRUE); +} + + +rviewFrame::~rviewFrame(void) +{ + RMDBGONCE( 3, RMDebug::module_applications, "rviewFrame", "~rviewFrame() " << this ); + + delete frames; + + if (frameManager != NULL) + frameManager->deregisterFrame(this); + + if (parentFrame != NULL) + { + parentFrame->deregisterChild(this); + } +} + + +const char *rviewFrame::getFrameName(void) const +{ + return "rviewFrame"; +} + +rviewFrameType rviewFrame::getFrameType(void) const +{ + return rviewFrameTypeGeneric; +} + + +void rviewFrame::setParent(rviewFrame *parent) +{ + parentFrame = parent; +} + + +void rviewFrame::registerChild(rviewFrame *child) +{ + child->setParent(this); + frames->registerFrame(child); +} + + +void rviewFrame::deregisterChild(rviewFrame *child) +{ + frames->deregisterFrame(child); + child->setParent(NULL); +} + + +// Called by the global frame manager when the application is about to die. +int rviewFrame::requestQuit(int level) +{ + // Default action on a hard quit: die + if (level != 0) + { + // Do not delete the children, that would upset the frame manager's loop. + frames->setDeleteMode(FALSE); setParent(NULL); + //delete this; + Close(TRUE); + } + return 0; +} + + + +// Called on a user event +int rviewFrame::userEvent(const user_event &ue) +{ + // Default action: nothing. Overload function in derived class if you're + // interested in user messages. + return 0; +} + + +// Called by the rviewFrameMgr object to determine which frame receives an event +int rviewFrame::checkobj(wxObject &obj) +{ + return checkobj_rec((wxWindow*)this, obj); +} + + +// Used internally by rviewFrame::checkobj(); don't access directly. Checks +// recursively all the children of a given frame against obj. +int rviewFrame::checkobj_rec(wxWindow *whence, wxObject &obj) +{ + if (&obj == (wxObject*)whence) return 1; + else + { + int i, n; + wxList *list; + + list = whence->GetChildren(); + n = list->Number(); + for (i=0; i<n; i++) + { + if (checkobj_rec((wxWindow*)(list->Nth(i)->Data()), obj) != 0) return 1; + } + } + return 0; +} + + + +// Virtual function. If a derived frame class should refuse to die in some +// circumstances, it must overload his function. +bool rviewFrame::OnClose(void) +{ + return TRUE; +} + + +void rviewFrame::childMouseEvent(wxWindow *child, wxMouseEvent &mevt) +{ + // by default this is ignored. +} + + + + + + +/* + * rviewFrameMgr member functions + */ + +rviewFrameMgr::rviewFrameMgr(void) +{ + listLength = 0; deleteChildren = FALSE; + frameList = NULL; tailList = NULL; +} + + +rviewFrameMgr::rviewFrameMgr(bool delChild) +{ + listLength = 0; deleteChildren = delChild; + frameList = NULL; tailList = NULL; +} + + +rviewFrameMgr::~rviewFrameMgr(void) +{ + int i; + frame_list *list, *last; + + // Deletes all the frames and their children + // Unlink all frames first! Otherwise there'll be trouble because the Frame Manager + // will loop over the frames and delete them; but if the frames have a valid parent + // they will deregister there which will screw up the Frame Manager's frame list + // _during the loop_ and hence crash. + list = frameList; + for (i=0; i<listLength; i++) + { + last = list; list = list->next; + // If it's the global frame manager it shouldn't delete the children, that's + // wxWindows' job. If it's a manager local to a frame it should delete all + // that frame's children, however. + if (deleteChildren) + { + last->frame->setParent(NULL); + //delete last->frame; + last->frame->Close(TRUE); + } + delete last; + } +} + + +void rviewFrameMgr::registerFrame(rviewFrame *client) +{ + frame_list *entry; + + //cout << "frameManager::registerFrame(" << (void*)client << ")" << endl; + if ((entry = new frame_list) == NULL) + { + //cerr << "frameManager: Unable to claim memory!\n" << endl; + return; + } + + entry->frame = client; entry->next = NULL; + + if (frameList == NULL) + { + frameList = entry; tailList = entry; + } + else + { + tailList->next = entry; + tailList = entry; + } + listLength++; + //cout << "Frame manager: Added frame to list. New length = " << listLength << endl; + //cout << "(anchor = " << (void*)frameList << ", tail = " << (void*)tailList << ")" << endl; +} + + +void rviewFrameMgr::deregisterFrame(rviewFrame *client) +{ + int i; + frame_list *list, *last; + + //cout << "frameManager::deregisterFrame(" << (void*)client << ")" << endl; + list = frameList; last = NULL; + for (i=0; i<listLength; i++) + { + if (list->frame == client) + { + list = list->next; + if (last == NULL) + { + delete frameList; frameList = list; last = list; + } + else + { + delete last->next; last->next = list; + } + listLength--; + if (last == NULL) tailList = NULL; + else + { + while (last->next != NULL) last = last->next; + tailList = last; + } + //cout << "Frame manager: Removed frame from list. New length = " << listLength << endl; + //cout << "(anchor = " << (void*)frameList << ", tail = " << (void*)tailList << ")" << endl; + return; + } + last = list; list = list->next; + } +} + + + +int rviewFrameMgr::numberOfFrames(void) const +{ + return listLength; +} + + + +void rviewFrameMgr::setDeleteMode(bool delChild) +{ + deleteChildren = delChild; +} + + +// Issues re-label requests to all frames. +void rviewFrameMgr::labelAll(void) +{ + int i; + frame_list *list; + + //cout << "frameManager::labelAll()" << endl; + list = frameList; + for (i=0; i<listLength; i++) + { + list->frame->label(); + list = list->next; + } +} + + + +/* + * Broadcasts the event to all frames until one claims the event by returning + * a value != 0. Warning: this function has to be re-entrant! + */ + +void rviewFrameMgr::broadcastEvent(wxObject &obj, wxEvent &evt) +{ + int i; + frame_list *list; + + // Note: the broadcast has to go in two steps, first determining which frame + // knows the object, then terminating the loop and calling the object as the + // last action. The reason is that this function is re-entrant and would + // crash badly if the frame list changed during the loop. + //cout << "frameManager::broadcastEvent()" << endl; + list = frameList; + for (i=0; i<listLength; i++) + { + //cout << "Broadcast to " << (void*)(list->frame) << endl; + if (list->frame->checkobj(obj) != 0) + { + //cout << "Frame manager: object known by frame " << (void*)list->frame << endl; + break; + } + list = list->next; + } + //cout << "Frame manager: broadcast " << ((list == NULL) ? "failed" : "successful") << endl; + + // Now call the frame. + if (list != NULL) {list->frame->process(obj, evt);} + // Uncomment this if there's any doubt it's re-entrant + //cout << "frameManager::broadcastEvent done" << endl; +} + + + +/* + * Broadcast a quit-message to all frames but the first one (the main frame). There + * are currently two levels, 0 means the frames can yet refuse to die by returning a + * value != 0, every other level doesn't give them this option. Returns 0 for failure. + * This must only be called for the _global_ frame manager! + */ + +int rviewFrameMgr::broadcastQuit(int level) +{ + int i, status; + frame_list *list; + + list = frameList->next; status = 1; + for (i=1; i<listLength; i++) + { + //if (level > 0) cout << "delete " << (void*)list << endl; + if (list->frame->requestQuit(level) != 0) status = 0; + list = list->next; + } + if (level == 0) return status; + + //cout << "Hard quit OK" << endl; + return 1; +} + + + +/* + * Broadcast a user event to all frames. + */ + +int rviewFrameMgr::broadcastUserEvent(const user_event &ue) +{ + int i, status; + frame_list *list; + + list = frameList; status = 0; + for (i=0; i<listLength; i++) + { + //cout << "broadcast to " << (void*)(list->frame) << endl; + status += list->frame->userEvent(ue); + list = list->next; + } + return status; // number of clients that recognised the event. +} + + + + + +/* + * rviewMultiline member functions + */ + +const int rviewMultiline::multiline_ppc10 = 63; + +void rviewMultiline::setupVariables(wxPanel *Panel, int X, int Y, int H, int Lines) +{ + int i; + + parent = Panel; lines = Lines; + if ((msg = new wxMessage *[lines]) == NULL) + { + cerr << "rviewMultiline::setupVariables(): " << lman->lookup("errorMemory") << endl; + return; + } + for (i=0; i<lines; i++) msg[i] = NULL; + + x = X; y = Y; lHeight = H; +} + + +rviewMultiline::rviewMultiline(wxPanel *Panel, int X, int Y, int H, int Lines) +{ + setupVariables(Panel, X, Y, H, Lines); +} + + +rviewMultiline::rviewMultiline(wxPanel *Panel, const char *Message, int X, int Y, int W, int H, int Lines) +{ + setupVariables(Panel, X, Y, H, Lines); + + rebuild(Message, W); +} + + +rviewMultiline::~rviewMultiline(void) +{ + // The wxMessage items have been created as children of the panel and will + // be deleted by the panel later on. So just delete the array. + delete [] msg; +} + + +void rviewMultiline::rebuild(const char *Message, int W) +{ + int length, i, cpl, pos; + char *buffer, *b, *base; + + if (msg == NULL) return; + + //cout << "rviewMultiline::rebuild()" << endl; + + cpl = (10*W) / multiline_ppc10; + + if ((buffer = new char[cpl + 1]) == NULL) + { + cerr << "rviewMultiline::rebuild(): " << lman->lookup("errorMemory") << endl; + return; + } + + length = strlen(Message); base = (char*)Message; + + for (i=0, pos=y; (length > 0) && (i<lines); i++, pos+=lHeight) + { + if (length > cpl) + { + b = base + cpl; + while ((*b > 32) && (b > base)) b--; + if (b <= base) b = base + cpl; else b++; + } + else + { + b = base + length; + } + memcpy(buffer, base, (int)(b - base)); buffer[(int)(b - base)] = '\0'; + //cout << buffer << endl; + if (msg[i] == NULL) + { + msg[i] = new wxMessage(parent, buffer, x, pos, W, lHeight, 0, "message"); + } + else + { + msg[i]->SetSize(x, pos, W, lHeight, 0); + msg[i]->SetLabel(buffer); + } + length -= (int)(b - base); base = b; + } + while (i<lines) + { + if (msg[i] != NULL) {delete msg[i]; msg[i] = NULL;} + i++; + } + delete [] buffer; +} + + + +int rviewMultiline::getMessageHeight(void) const +{ + int i; + + for (i=0; i<lines; i++) if (msg[i] == NULL) break; + return(i*lHeight); +} + + + +/* + * More convenient interface to standard widgets + */ + +rviewText::rviewText(wxPanel *parent, const char *value, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, (char*)value, x, y, w, h, wxTE_PROCESS_ENTER) +{ +} + +rviewText::rviewText(long style, wxPanel *parent, const char *value, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, (char*)value, x, y, w, h, style) +{ +} + +rviewText::rviewText(wxPanel *parent, const DynamicString &value, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, NULL, x, y, w, h, wxTE_PROCESS_ENTER) +{ + wxText::SetValue((char*)value.ptr()); +} + +rviewText::rviewText(wxPanel *parent, int value, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, NULL, x, y, w, h, wxTE_PROCESS_ENTER) +{ + char buffer[32]; + sprintf(buffer, "%d", value); + wxText::SetValue(buffer); +} + + +rviewText::rviewText(wxPanel *parent, long value, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, NULL, x, y, w, h, wxTE_PROCESS_ENTER) +{ + char buffer[32]; + sprintf(buffer, "%ld", value); + wxText::SetValue(buffer); +} + + +rviewText::rviewText(wxPanel *parent, double value, bool sciForm, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, NULL, x, y, w, h, wxTE_PROCESS_ENTER) +{ + char buffer[32]; + sprintf(buffer, (sciForm) ? "%g" : "%f", value); + wxText::SetValue(buffer); +} + +void rviewText::SetValue(char *value) +{ + wxText::SetValue(value); +} + +void rviewText::SetValue(const char *value) +{ + wxText::SetValue((char*)value); +} + +void rviewText::SetValue(const DynamicString &value) +{ + wxText::SetValue((char*)value.ptr()); +} + +void rviewText::SetValue(int value) +{ + char buffer[32]; + sprintf(buffer, "%d", value); + wxText::SetValue(buffer); +} + +void rviewText::SetValue(unsigned int value) +{ + char buffer[32]; + sprintf(buffer, "%u", value); + wxText::SetValue(buffer); +} + +void rviewText::SetValue(long value) +{ + char buffer[32]; + sprintf(buffer, "%ld", value); + wxText::SetValue(buffer); +} + +void rviewText::SetValue(double value, bool sciForm) +{ + char buffer[32]; + sprintf(buffer, (sciForm) ? "%g" : "%f", value); + wxText::SetValue(buffer); +} + +char *rviewText::GetValue(void) +{ + return wxText::GetValue(); +} + +void rviewText::GetValue(DynamicString &value) +{ + value = wxText::GetValue(); +} + +void rviewText::GetValue(int &value) +{ + value = asctoi(wxText::GetValue()); +} + +void rviewText::GetValue(long &value) +{ + value = asctol(wxText::GetValue()); +} + +void rviewText::GetValue(float &value) +{ + value = asctof(wxText::GetValue()); +} + +void rviewText::GetValue(double &value) +{ + value = asctof(wxText::GetValue()); +} + + + +rviewButton::rviewButton(wxPanel *parent, char *label, int x, int y, int w, int h, long style) : wxButton(parent, (wxFunction)rviewEventHandler, label, x, y, w, h, style) +{ +} + +rviewChoice::rviewChoice(wxPanel *parent, int n, char *choices[], char *label, int x, int y, int w, int h, long style) : wxChoice(parent, (wxFunction)rviewEventHandler, label, x, y, w, h, n, choices, style) +{ +} + +// hacked version to make up for wxWindows' lack of const +rviewChoice::rviewChoice(wxPanel *parent, int n, const char *choices[], char *label, int x, int y, int w, int h, long style) : wxChoice(parent, (wxFunction)rviewEventHandler, label, x, y, w, h, n, (char**)choices, style) +{ +} + +rviewCheckBox::rviewCheckBox(wxPanel *parent, char *label, int x, int y, int w, int h) : wxCheckBox(parent, (wxFunction)rviewEventHandler, label, x, y, w, h) +{ +} + +rviewRadioButton::rviewRadioButton(wxPanel *parent, char *label, bool value, int x, int y, int w, int h) : wxRadioButton(parent, (wxFunction)rviewEventHandler, label, value, x, y, w, h) +{ +} + +rviewScrollBar::rviewScrollBar(wxPanel *parent, int x, int y, int w, int h, long style) : wxScrollBar(parent, (wxFunction)rviewEventHandler, x, y, w, h, style) +{ +} + +rviewSlider::rviewSlider(wxPanel *parent, int value, int min_val, int max_val, int width, char *label, int x, int y, long style) : wxSlider(parent, (wxFunction)rviewEventHandler, label, value, min_val, max_val, x, y, style) +{ +} + + + +/* + * The special slider class when arbitrary mouse events are required. + */ + +const int rviewSpecialSlider::dflt_barwidth = 30; +const int rviewSpecialSlider::dflt_barheight = 10; +const int rviewSpecialSlider::dflt_width = 200; +const int rviewSpecialSlider::dflt_height = rviewSpecialSlider::dflt_barheight + 2*rviewSpecialSlider::dflt_border; +const int rviewSpecialSlider::dflt_border = 4; + +rviewSpecialSlider::rviewSpecialSlider(rviewFrame *logParent, wxPanel *parent, int val, int min, int max, int width, const char *label) : + wxCanvas(parent, -1, -1, (width==-1) ? dflt_width : width, dflt_height, wxRETAINED), + background(0x00,0x00,0x00), + foreground(0x00,0xff,0x00), + wellground(0xff,0xff,0xff), + outline(0x00,0x00,0x00), + labelColour(*(parent->GetLabelColour())), + bback(background, wxTRANSPARENT), + bfore(foreground, wxSOLID), + bwell(wellground, wxSOLID), + outlinePen(outline, 1, wxSOLID), + labelFont(12, wxROMAN, wxNORMAL, wxNORMAL), + myLabel(label) +{ + wxBrush *dcb; + logicalParent = logParent; + border = dflt_border; + barwidth = dflt_barwidth; barheight = dflt_barheight; + value = val; + vmin = min; vmax = max; + if (vmax <= vmin) + vmax = vmin+1; + dcb = ((wxPanel*)GetParent())->GetDC()->GetBackground(); + bback = wxBrush(dcb->GetColour(), dcb->GetStyle()); + SetBackground(&bback); + SetFont(&labelFont); + GetDC()->GetTextExtent(myLabel.ptr(), &textx, &texty); +} + +rviewSpecialSlider::~rviewSpecialSlider(void) +{ +} + +int rviewSpecialSlider::GetMin(void) const +{ + return vmin; +} + +int rviewSpecialSlider::GetMax(void) const +{ + return vmax; +} + +int rviewSpecialSlider::GetValue(void) const +{ + return value; +} + +void rviewSpecialSlider::SetRange(int min, int max) +{ + if (min < max) + { + vmin = min; vmax = max; + if (value < vmin) + value = vmin; + if (value > vmax) + value = vmax; + Refresh(TRUE); + } +} + +void rviewSpecialSlider::SetValue(int val) +{ + if ((vmin <= val) && (val <= vmax)) + { + float barx, bary, bheight, newbx; + + getBarParams(barx, bary, bheight); + value = val; + getBarParams(newbx, bary, bheight); + updateWell(barx, newbx, bary, bheight); + } +} + +void rviewSpecialSlider::SetLabel(const char *label) +{ + myLabel = label; + GetDC()->GetTextExtent(myLabel.ptr(), &textx, &texty); + Refresh(TRUE); +} + +bool rviewSpecialSlider::PositionInWell(float posx, float posy) +{ + int y0, y1; + + GetClientSize(&cwidth, &cheight); + getWellVert(y0, y1); + if (((float)y0 <= posy) && (posy <= (float)y1)) + { + if ((posx >= textx + 2*border) && (posx <= (float(cwidth - border)))) + return TRUE; + } + return FALSE; +} + + +void rviewSpecialSlider::getWellVert(int &y0, int &y1) +{ + y0 = cheight - 2*border - barheight; + if (y0 < 0) + y0 = 0; + y0 += border; + y1 = cheight - border; + if (y1 < y0) + y1 = y0; +} + +void rviewSpecialSlider::getBarParams(float &posx, float &posy, float &height) +{ + int y0, y1; + + GetClientSize(&cwidth, &cheight); + + posx = (float)((cwidth - 3*border - barwidth - textx) * (value-vmin)) / (vmax - vmin); + if (posx < 0) + posx = 0; + /*cout << "cwidth " << cwidth << ", textx " << textx << ", value " << value << ", border " << border + << ", barwidth " << barwidth << ", vmin " << vmin << ", vmax " << vmax << ", posx " << posx << endl;*/ + posx += (float)(2*border + textx); + getWellVert(y0, y1); + posy = (float)y0; + height = (float)(y1 - y0); +} + +int rviewSpecialSlider::calcNewValue(float posx, float posy, int &val, bool checky) +{ + int y0, y1; + + GetClientSize(&cwidth, &cheight); + + getWellVert(y0, y1); + if (!checky || ((float)y0 <= posy) && (posy <= (float)y1)) + { + float rem = (float)(cwidth - 3*border - barwidth - textx); + if (rem < 1.0) + rem = 1.0; + val = (int)((((posx - 2*border - textx) * (vmax - vmin)) / rem) + 0.5) + vmin; + if (val < vmin) + val = vmin; + if (val > vmax) + val = vmax; + + return 0; + } + return -1; +} + + +void rviewSpecialSlider::getUpdateInterval(float oldx, float newx, float &clipx, float &clipw) +{ + if (oldx < newx) + { + clipx = oldx; clipw = newx - oldx + barwidth; + } + else + { + clipx = newx; clipw = oldx - newx + barwidth; + } +} + + +void rviewSpecialSlider::updateWell(float oldx, float newx, float posy, float bheight) +{ + float clipx, clipw; + + getUpdateInterval(oldx, newx, clipx, clipw); + //cout << "CLIP " << clipx << ", " << posy << ", " << clipw << ", " << bheight << endl; + // need to enlarge the clipping region somewhat to make sure everything is redrawn + BeginDrawing(); + SetClippingRegion(clipx, 0, clipw+2, cheight); + //Clear(); + redrawCore(newx, posy, bheight); + DestroyClippingRegion(); + EndDrawing(); +} + + +void rviewSpecialSlider::redrawCore(float x, float y, float bheight) +{ + float extx, exty, nx, ny; + char number[32]; + + // first draw the label + SetFont(&labelFont); + SetTextForeground(&labelColour); + DrawText(myLabel.ptr(), 0, y); + + SetPen(&outlinePen); + SetBrush(&bwell); + DrawRectangle(textx + border, y-border, (float)cwidth - textx - border, bheight + 2*border); + SetBrush(&bfore); + DrawRectangle(x, y, (float)barwidth, bheight); + + // then draw the current value on top + sprintf(number, "%d", value); + GetDC()->GetTextExtent(number, &extx, &exty); + nx = x + (barwidth - extx)/2; ny = y - exty - border; + SetPen(NULL); + // must use a non-transparent brush. Finally found the right one! + SetBrush(((wxPanel*)GetParent())->GetDC()->GetBackground()); + DrawRectangle(0, ny, (float)cwidth, exty); + DrawText(number, nx, ny); +} + + +void rviewSpecialSlider::OnPaint(void) +{ + wxRect rect; + float x, y, bheight; + + getBarParams(x, y, bheight); + + BeginDrawing(); + + wxUpdateIterator upd(this); + while (upd) + { + upd.GetRect(&rect); + redrawCore(x, y, bheight); + upd++; + } + EndDrawing(); +} + +void rviewSpecialSlider::OnEvent(wxMouseEvent &mevt) +{ + int type = mevt.GetEventType(); + float barx, bary, bheight; + float mx, my; + int newVal = value; + + getBarParams(barx, bary, bheight); + mevt.Position(&mx, &my); + if ((type == wxEVENT_TYPE_LEFT_DOWN) || (type == wxEVENT_TYPE_RIGHT_DOWN)) + { + if (mx < barx) + { + if (calcNewValue(barx - barwidth, my, newVal, TRUE) != 0) + newVal = value; + } + else if (mx > barx + barwidth) + { + if (calcNewValue(barx + barwidth, my, newVal, TRUE) != 0) + newVal = value; + } + } + else if (type == wxEVENT_TYPE_MOTION) + { + if (mevt.LeftIsDown() || mevt.RightIsDown()) + { + if (calcNewValue(mx - barwidth/2, my, newVal, FALSE) != 0) + { + newVal = value; + } + } + } + if (newVal != value) + { + float newbx, newby, newbh; + + value = newVal; + getBarParams(newbx, newby, newbh); + updateWell(barx, newbx, bary, bheight); + + // additionally we build a regular command event + wxCommandEvent cmd(wxEVENT_TYPE_SLIDER_COMMAND); + //OnCommand(*GetParent(), cmd); + logicalParent->process(*this, cmd); + } + // the entire point of this class is to always pass on the actual mouse event to the parent + logicalParent->childMouseEvent(this, mevt); +} + + + + + + +/* + * rviewDialog member functions. + */ + +const int rviewDialog::dialog_width = 300; +const int rviewDialog::dialog_height = 170; +const int rviewDialog::dialog_border = 8; +const int rviewDialog::dialog_buttonsx = 80; +const int rviewDialog::dialog_buttonsy = 30; +const int rviewDialog::dialog_bheight = rviewDialog::dialog_buttonsy + 16; +const int rviewDialog::dialog_lines = 8; +const int rviewDialog::dialog_lheight = 20; + + +// The strings passed to this function may be labels (first char is a backslash) +// or explicit text. +rviewDialog::rviewDialog(const char *title, const char *message, int buttonNo, const char *buttons[]): + rviewFrame(NULL, "", 0, 0, dialog_width, dialog_height) +{ + int i, x, y; + char *string; + + panel = NULL; buttonNumber = 0; buttonPressed = 0; + + buttonText = new char *[buttonNo + 2]; + but = new rviewButton *[buttonNo]; + + if ((buttonText == NULL) || (but == NULL)) + { + cerr << "rviewDialog::rviewDialog(): " << lman->lookup("errorMemory") << endl; + if (buttonText != NULL) delete [] buttonText; + if (but != NULL) delete [] but; + return; + } + + buttonNumber = buttonNo; + + buttonText[0] = (char*)title; buttonText[1] = (char*)message; + for (i=0; i<buttonNumber; i++) + { + but[i] = NULL; buttonText[i+2] = (char*)buttons[i]; + } + + // Only memorize those strings that are a label identifier (first char a backslash) + for (i=0; i<2+buttonNumber; i++) + { + if (buttonText[i] != NULL) + { + // Don't combine the two ifs with && : there is no defined evaluation order for && ! + // Always memorize the message text. + if ((buttonText[i][0] == '\\') || (i == 1)) + { + // We remove the backslash but need one additional char for the + // line terminator ==> strlen -1 + 1. + if ((string = new char[strlen(buttonText[i]) + ((i==1) ? 1 : 0)]) == NULL) + { + cerr << "rviewDialog::rviewDialog(): " << lman->lookup("errorMemory") << endl; + while (--i >= 0) + { + if (buttonText[i] != NULL) delete [] buttonText[i]; + } + delete [] buttonText; buttonText = NULL; + delete [] but; but = NULL; + buttonNumber = 0; + return; + } + strcpy(string, buttonText[i] + ((i==1) ? 0 : 1)); buttonText[i] = string; + //cout << "buttonText[" << i << "] = " << buttonText[i] << endl; + } + else buttonText[i] = NULL; + } + } + + GetClientSize(&x, &y); + + panel = new wxPanel((wxWindow*)this, 100, 100, 10, 10); + + msg = new rviewMultiline(panel, dialog_border, dialog_border, dialog_lheight, dialog_lines); + + //cout << x << ", " << y << endl; + + // If the strings are not labels then set them now statically, otherwise they will + // be set in the following label() call. + if (title[0] != '\\') + { + SetTitle((char*)title); + } + + // Now handle all buttons. + for (i=0; i<buttonNumber; i++) + { + but[i] = new rviewButton(panel); + if (buttons[i][0] != '\\') + { + but[i]->SetLabel((char*)buttons[i]); + } + else // do this for the button size. + { + but[i]->SetLabel(lman->lookup(buttons[i]+1)); + } + } + + OnSize(x, y); + + label(); + + Show(TRUE); +} + + + +rviewDialog::~rviewDialog(void) +{ + int i; + + // Widgets that were created as children of other wx items are deleted by those items. + // They mustn't be destroyed here! + + if (msg != NULL) delete msg; + //if (panel != NULL) delete panel; + + if (but != NULL) + { + //for (i=0; i<buttonNumber; i++) {if (but[i] != NULL) delete but[i];} + delete [] but; + } + + if (buttonText != NULL) + { + for (i=0; i<2+buttonNumber; i++) {if (buttonText[i] != NULL) delete [] buttonText[i];} + delete [] buttonText; + } +} + + +const char *rviewDialog::getFrameName(void) const +{ + return "rviewDialog"; +} + +rviewFrameType rviewDialog::getFrameType(void) const +{ + return rviewFrameTypeDialog; +} + + +void rviewDialog::OnSize(int w, int h) +{ + int x, y, empty, pos, i; + + RMDBGONCE( 3, RMDebug::module_applications, "rviewDialog", "OnSize( " << x << ", " << h << " )" ); + + GetClientSize(&x, &y); + if ((frameWidth == x) && (frameHeight == y)) return; + frameWidth = x; frameHeight = y; + + x -= 2*dialog_border; y -= dialog_border; + panel->SetSize(0, 0, x, y); + + if (buttonText[1][0] == '\\') + msg->rebuild(lman->lookup(buttonText[1]+1), x); + else + msg->rebuild(buttonText[1], x); + + // Space out the buttons on the available space. + if (buttonNumber == 0) return; + + empty = (x - buttonNumber*dialog_buttonsx) / buttonNumber; + if(empty <0 ) + empty=0; + + pos = empty/2; + + for (i=0; i<buttonNumber; i++) + { + but[i]->SetSize(pos, y - (dialog_bheight + dialog_buttonsy)/2, dialog_buttonsx, dialog_buttonsy); + pos += empty + dialog_buttonsx; + } +} + + + +void rviewDialog::label(void) +{ + int i, x, y; + + //cout << "rviewDialog::label()" << endl; + // If the widgets are labels then set their new values. + + panel->GetClientSize(&x, &y); + if (buttonText[0] != NULL) + { + SetTitle(lman->lookup(buttonText[0])); + } + + // The message is always stored. + if (buttonText[1][0] == '\\') + msg->rebuild(lman->lookup(buttonText[1]+1), x); + else + msg->rebuild(buttonText[1], x); + + for (i=0; i<buttonNumber; i++) + { + if (buttonText[2+i] != NULL) + { + but[i]->SetLabel(lman->lookup(buttonText[2+i])); + } + } +} + + + + +/* + * Process events which are broadcast by the frame manager. + */ + +int rviewDialog::process(wxObject &obj, wxEvent &evt) +{ + int i, type; + + //cout << "rviewDialog::process()" << endl; + for (i=0; i<buttonNumber; i++) + { + if (&obj == (wxObject*)but[i]) break; + } + + if (i >= buttonNumber) return 0; + + type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + buttonPressed = i+1; + } + else + { + buttonPressed = 0; + } + //cout << "rviewDialog::process():" << (void*)this << " Button " << buttonPressed << endl; + + return buttonPressed; +} + + + +/* + * Polling interface to the dialog box. Returns the number of the button that + * was pressed, starting from 1 (0 means none). On reading the button number + * is reset to 0. + */ + +int rviewDialog::GetButton(void) +{ + int val = buttonPressed; + + buttonPressed = 0; + return val; +} + + + + + +/* + * Error box + * (an intrinsically modal special case of a dialog box) + */ + +char *rviewErrorboxDefaultButton[1] = {"\\textOK"}; + +rviewErrorbox::rviewErrorbox(const char *message): rviewDialog("\\errorFrom", message, 1, (const char**)rviewErrorboxDefaultButton) +{ + //place this in log file + cerr << lman->lookup("errorFrom") << ' ' << message << endl; +} + + +rviewErrorbox::rviewErrorbox(const char *title, const char *message, int buttonNo, const char *buttons[]): rviewDialog(title, message, buttonNo, buttons) +{ + //place this in log file + cerr << lman->lookup("errorFrom") << ' ' << message << endl; +} + + +rviewErrorbox::~rviewErrorbox(void) {;} + + +const char *rviewErrorbox::getFrameName(void) const +{ + return "rviewErrorbox"; +} + +rviewFrameType rviewErrorbox::getFrameType(void) const +{ + return rviewFrameTypeErrorbox; +} + + +int rviewErrorbox::activate(void) +{ + int pressed; + + MakeModal(TRUE); + while ((pressed = GetButton()) == 0) ::wxYield(); + MakeModal(FALSE); + return pressed; +} + + +// static member functions +char *rviewErrorbox::buildErrorMessage(const char *message, const char *classname, const char *funcname) +{ + char *buffer, *b; + int bsize = strlen(message) + 1; + + if (classname != NULL) + { + bsize += strlen(classname) + 3; // ": " + + // funcname is ignored unless class name is given + if (funcname != NULL) + bsize += strlen(funcname) + 3; // "::" + } + + buffer = new char[bsize]; + b = buffer; + + if (classname != NULL) + { + b += sprintf(b, "%s", classname); + + if (funcname != NULL) + b += sprintf(b, "::%s", funcname); + + b += sprintf(b, ": "); + } + b += sprintf(b, "%s", message); + + return buffer; +} + + +rviewErrorbox *rviewErrorbox::newErrorbox(const char *message, const char *classname, const char *funcname) +{ + char *msg; + rviewErrorbox *box; + + msg = buildErrorMessage(message, classname, funcname); + box = new rviewErrorbox(msg); + delete [] msg; + + return box; +} + + +rviewErrorbox *rviewErrorbox::newErrorbox(const char *title, const char *message, int buttonNo, const char *buttons[], const char *classname, const char *funcname) +{ + char *msg; + rviewErrorbox *box; + + msg = buildErrorMessage(message, classname, funcname); + box = new rviewErrorbox(msg); + delete [] msg; + + return box; +} + + +int rviewErrorbox::reportError(const char *message, const char *classname, const char *funcname) +{ + rviewErrorbox *box; + int but; + + box = newErrorbox(message, classname, funcname); + but = box->activate(); + box->Close(TRUE); + + return but; +} + + +int rviewErrorbox::reportError(const char *title, const char *message, int buttonNo, const char *buttons[], const char *classname, const char *funcname) +{ + rviewErrorbox *box; + int but; + + box = newErrorbox(title, message, buttonNo, buttons, classname, funcname); + but = box->activate(); + box->Close(TRUE); + + return but; +} + + + + + +/* + * rviewProgress members + */ + +rviewProgress::rviewProgress(const char *message): rviewDialog("\\messageFrom", message, 1, (const char**)rviewErrorboxDefaultButton) +{ + int x, y; + + RMDBGONCE( 3, RMDebug::module_applications, "rviewProgress", "rviewProgress( " << lman->lookup("messageFrom") << message << " )" ); + + GetSize(&x, &y); + y = 2*dialog_border + msg->getMessageHeight() + dialog_bheight; + SetSize(-1, -1, x, y); + + // Have to do this to make sure you actually see anything in the window + // Crude, but nothing else seems to work... + Refresh(TRUE); ::wxYield(); + // Wait 300ms + ::wxStartTimer(); + while (::wxGetElapsedTime(FALSE) < 300) ::wxYield(); +} + + + + +rviewProgress::~rviewProgress(void) {;} + + +int rviewProgress::process(wxObject &obj, wxEvent &evt) +{ + if (rviewDialog::process(obj, evt) != 0) + { + this->Close(TRUE); + return 1; + } + return 0; +} + + +const char *rviewProgress::getFrameName(void) const +{ + return "rviewProgress"; +} + +rviewFrameType rviewProgress::getFrameType(void) const +{ + return rviewFrameTypeProgress; +} + + + + +/* + * rviewResult members + */ + +const int rviewResult::result_x = 0; +const int rviewResult::result_y = 0; +const int rviewResult::result_width = 320; +const int rviewResult::result_height = 296; +const int rviewResult::result_border = 8; +const int rviewResult::result_lheight = 20; +const int rviewResult::result_header = (8 * rviewResult::result_lheight); +const int rviewResult::result_cwidth = 150; +const int rviewResult::result_twidth = 90; +const int rviewResult::result_theight = 30; +const int rviewResult::result_bwidth = 60; +const int rviewResult::result_bheight = 30; + +rviewResult::rviewResult(void): rviewFrame(NULL, "", result_x, result_y, result_width, result_height) +{ + RMDBGONCE( 3, RMDebug::module_applications, "rviewResult", "rviewResult() " << this); + + setupVariables(); + configureGrey(); + Show(FALSE); +} + +rviewResult::rviewResult(collection_desc *collection): rviewFrame(NULL, "", result_x, result_y, result_width, result_height) +{ + setupVariables(); + setCollection(collection); +} + + +rviewResult::~rviewResult(void) +{ + RMDBGONCE( 3, RMDebug::module_applications, "rviewResult", "rviewResult() " << this ); + + int i; + collection_desc *ptr = coll; + user_event ue; + + // give all viewers a chance to clean up + ue.type = usr_mdd_dying; + for (i=0; i<coll->number; i++) + { + ue.data = (void*)&(coll->mddObjs[i].mdd); + frames->broadcastUserEvent(ue); + } + + // All the widgets are deleted automatically. + if (selectedItems != NULL) delete [] selectedItems; + if (typeManager != NULL) {typeManager->unlinkParent(); typeManager->Close(TRUE);} + + rviewDeleteCollection(coll); +} + + +const char *rviewResult::getFrameName(void) const +{ + return "rviewResult"; +} + +rviewFrameType rviewResult::getFrameType(void) const +{ + return rviewFrameTypeResult; +} + + +void rviewResult::setCollection(collection_desc *collection) +{ + int x, y, i; + char res_string[STRINGSIZE]; + + // In case this is called when the frame already displays a result (shouldn't + // normally happen, though) + if (coll != NULL) + { + rviewDeleteCollection(coll); + delete list; + } + coll = collection; + + // Init flags + for (i=0; i<coll->number; i++) + { + coll->mddObjs[i].flags = 0; + } + + GetClientSize(&x, &y); + + list = new wxListBox(panel, (wxFunction)&rviewEventHandler, "", wxMULTIPLE, result_border, result_border + result_header, x, y - result_header, 0, NULL, wxALWAYS_SB | wxLB_MULTIPLE); + + ostrstream memstr(res_string, STRINGSIZE); + for (i=0; i<coll->number; i++) + { + list->Append(mddDescriptorString(memstr, i)); + } + + if (selectedItems != NULL) delete [] selectedItems; + i = (coll->number + 7) >> 3; + if ((selectedItems = new char[i]) == NULL) + { + cerr << "rviewResult::setCollection(): " << lman->lookup("errorMemory") << endl; + this->Close(TRUE); + return; + } + memset(selectedItems, 0, i); + + configureGrey(); + + label(); + + // Force resize + frameWidth = -1; frameHeight = -1; + OnSize(x, y); + + Show(TRUE); +} + + + +char *rviewResult::mddDescriptorString(ostrstream &memstr, int number) +{ + r_GMarray *mdd = coll->mddObjs[number].mdd.ptr(); + r_Data_Format fmt = mdd->get_current_format(); + + memstr.width(3); + memstr << number << ": ("; memstr.width(0); + memstr << mdd->get_type_length() << ") " + << mdd->spatial_domain() << ' ' + << fmt << '\0'; + memstr.seekp(0); + + return memstr.str(); +} + + +void rviewResult::label(void) +{ + char buffer[STRINGSIZE]; + + //cout << "labelling " << (void*)mBar << endl; + + SetTitle(lman->lookup("titleResult")); + + if (mBar != NULL) + { + // Configure all the text in the menu bar + mBar->SetLabel(MENU_RSLT_ITEM_OPENALL, lman->lookup("menRsltItemOpAll")); + mBar->SetLabel(MENU_RSLT_ITEM_THUMBALL, lman->lookup("menRsltItemThumbAll")); + mBar->SetLabel(MENU_RSLT_ITEM_CLOSE, lman->lookup("menRsltItemClose")); + mBar->SetHelpString(MENU_RSLT_ITEM_OPENALL, lman->lookup("helpRsltItemOpAll")); + mBar->SetHelpString(MENU_RSLT_ITEM_THUMBALL, lman->lookup("helpRsltItemThumbAll")); + mBar->SetHelpString(MENU_RSLT_ITEM_CLOSE, lman->lookup("helpRsltItemClose")); + + mBar->SetLabel(MENU_RSLT_SLCT_SLCTALL, lman->lookup("menRsltSlctSlctAll")); + mBar->SetLabel(MENU_RSLT_SLCT_CLEAR, lman->lookup("menRsltSlctClear")); + mBar->SetLabel(MENU_RSLT_SLCT_OPEN, lman->lookup("menRsltSlctOpen")); + mBar->SetLabel(MENU_RSLT_SLCT_THUMB, lman->lookup("menRsltSlctThumb")); + mBar->SetLabel(MENU_RSLT_SLCT_DELETE, lman->lookup("menRsltSlctDel")); + mBar->SetLabel(MENU_RSLT_SLCT_ENDIAN, lman->lookup("menRsltSlctEndian")); + mBar->SetLabel(MENU_RSLT_SLCT_TYPEMAN, lman->lookup("menRsltSlctTypeMan")); + mBar->SetLabel(MENU_RSLT_SLCT_INFO, lman->lookup("menRsltSlctInfo")); + mBar->SetHelpString(MENU_RSLT_SLCT_SLCTALL, lman->lookup("helpRsltSlctSlctAll")); + mBar->SetHelpString(MENU_RSLT_SLCT_CLEAR, lman->lookup("helpRsltSlctClear")); + mBar->SetHelpString(MENU_RSLT_SLCT_OPEN, lman->lookup("helpRsltSlctOpen")); + mBar->SetHelpString(MENU_RSLT_SLCT_THUMB, lman->lookup("helpRsltSlctThumb")); + mBar->SetHelpString(MENU_RSLT_SLCT_DELETE, lman->lookup("helpRsltSlctDel")); + mBar->SetHelpString(MENU_RSLT_SLCT_ENDIAN, lman->lookup("helpRsltSlctEndian")); + mBar->SetHelpString(MENU_RSLT_SLCT_TYPEMAN, lman->lookup("helpRsltSlctTypeMan")); + mBar->SetHelpString(MENU_RSLT_SLCT_INFO, lman->lookup("helpRsltSlctInfo")); + + mBar->SetLabelTop(0, lman->lookup("menRsltItem")); + mBar->SetLabelTop(1, lman->lookup("menRsltSlct")); + } + + if (list != NULL) + { + list->SetLabel(lman->lookup("textResult")); + } + + if (group != NULL) + { + group->SetLabel(lman->lookup("textCollHeader")); + sprintf(buffer, "%s: %s", lman->lookup("textCollName"), (coll->collName == NULL) ? "" : coll->collName); + collName->SetLabel(buffer); + sprintf(buffer, "%s: %s", lman->lookup("textCollType"), (coll->collType == NULL) ? "?" : coll->collType); + collType->SetLabel(buffer); + sprintf(buffer, (coll->collInfo == NULL) ? "" : coll->collInfo); + collInfo->SetLabel(buffer); + + if (choice != NULL) + { + choice->SetLabel(lman->lookup("dispModeLabel")); + } + } + resampBut->SetLabel(lman->lookup("textOK")); + + //cout << "labelled OK" << endl; +} + + + +void rviewResult::OnSize(int w, int h) +{ + if (panel != NULL) + { + int x, y; + + RMDBGONCE( 3, RMDebug::module_applications, "rviewResult", "OnSize( " << w << ", " << h << " )"); + + GetClientSize(&x, &y); + if ((frameWidth == x) && (frameHeight == y)) return; + frameWidth = x; frameHeight = y; + + panel->SetClientSize(x, y); + x -= 2*result_border; y -= 2*result_border; + + if (list != NULL) + list->SetSize(result_border, + result_lheight + result_border + result_header, + x, + y - result_header - result_lheight); + + if (group != NULL) + { + group->SetSize(result_border, + result_border, + x, + result_header); + //group->GetClientSize(&x, &y); + x -= 2*result_border; y -=2*result_border; + if (collName != NULL) + collName->SetSize(2*result_border, + result_lheight + result_border, + x, + result_lheight); + if (collType != NULL) + collType->SetSize(2*result_border, + 2*result_lheight + result_border, + x, + result_lheight); + if (collInfo != NULL) + collInfo->SetSize(2*result_border, + 3*result_lheight + result_border, + x, + result_lheight); + if (choice != NULL) + choice->SetSize(2*result_border, + 4*result_lheight + result_border, + result_cwidth - cbfactor, + result_lheight); + } + scaleMode->SetSize(3*result_border/2, + 6*result_lheight + result_border, + result_cwidth - 2*cbfactor, + result_lheight); + resampText->SetSize(3*result_border/2 + result_cwidth - cbfactor, + 6*result_lheight + result_border, + result_twidth, + result_theight); + resampBut->SetSize(3*result_border/2 + result_cwidth - cbfactor/2 + result_twidth, + 6*result_lheight + result_border, + result_bwidth, + result_bheight); + } +} + + + +void rviewResult::openViewer(int itemNo) +{ + + RMDBGONCE( 3, RMDebug::module_applications, "rviewResult", "openViewer( " << itemNo << " )" ); + + if ((itemNo < 0) || (itemNo >= coll->number)) return; + + rviewDisplay *newDisplay=NULL; + + switch (prefs->lastDisplay) + { + case 0: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_IMGFLAT) == 0) + { + newDisplay = new rviewFlatImage(coll->mddObjs + itemNo); + } + } + break; + case 1: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_IMGVOLM) == 0) + { + newDisplay = new rviewVolumeImage(coll->mddObjs + itemNo); + } + } + break; + case 2: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_IMGOSECT) == 0) + { + newDisplay = new rviewOSectionFullImage(coll->mddObjs + itemNo); + } + } + break; + case 3: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_IMGHGHT) == 0) + { + newDisplay = new rviewHeightImage(coll->mddObjs + itemNo); + } + } + break; + case 4: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_CHART) == 0) + { + newDisplay = new rviewChart(coll->mddObjs + itemNo); + } + } + break; + case 5: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_TABLE) == 0) + { + newDisplay = new rviewTable(coll->mddObjs + itemNo); + } + } + break; + case 6: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_SOUND) == 0) + { + newDisplay = new rviewSoundPlayer(coll->mddObjs + itemNo); + } + } + break; + case 7: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_STRVIEW) == 0) + { + newDisplay = new rviewStringViewer(coll->mddObjs + itemNo); + } + } + break; + default: + cerr << "Unsupported display mode " << prefs->lastDisplay << endl; + return; + } + + if (newDisplay != NULL) + { + if (newDisplay->openViewer() != 0) + newDisplay->Close(TRUE); + else + { + coll->mddObjs[itemNo].flags |= newDisplay->getViewerType(); + registerChild(newDisplay); + } + } +} + + + +/* + * This function must be called whenever the selections of the list box + * change (click / double click) or items in the listbox are modified or + * deleted (this seems to be some wxWindows-internal bug that always + * deselects listbox items if an item was changed). + */ +int rviewResult::updateSelection(void) +{ + int i, changes, sel=-1; + char val; + + if (selectedItems == NULL) return -1; + + changes = 0; + for (i=0; i<coll->number; i++) + { + val = selectedItems[i>>3] & (1<<(i&7)); + if (list->Selected(i)) + { + if (val == 0) {sel=i; changes++;} + selectedItems[i>>3] |= (1<<(i&7)); + } + else + { + if (val != 0) {sel=i; changes++;} + selectedItems[i>>3] &= ~(1<<(i&7)); + } + } + if (changes > 1) + { + cerr << "Warning: more than one selection changed!" << endl; + } + + configureGrey(); + + return sel; +} + + + +void rviewResult::operationPrologue(void) +{ + ::wxBeginBusyCursor(); + ::wxStartTimer(); +} + + +void rviewResult::operationEpilogue(const char *opname) +{ + int elapsed = ::wxGetElapsedTime(); + + ::wxEndBusyCursor(); + + if (opname != NULL) + { + char buffer[STRINGSIZE]; + + sprintf(buffer, "%s \"%s\": %dms", lman->lookup("textOpTime"), opname, elapsed); + cout << buffer << endl; + SetStatusText(buffer); + } +} + + +int rviewResult::parseResampleString(const char *resStr, double *values) +{ + int i; + const char *b; + + b = resStr; i = 0; + while (*b != 0) + { + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == ',') b++; + if (*b != 0) + { + if (values != NULL) values[i] = atof(b); + while ((*b != ' ') && (*b != '\t') && (*b != ',') && (*b != 0)) b++; + i++; + } + } + return i; +} + + +int rviewResult::resampleSelection(void) +{ + int i, j, res; + double *scale; + r_Dimension dim; + r_Minterval oldInterv, useInterv, newInterv; + r_Ref<r_GMarray> newMdd; + rviewBaseType baseType; + char buffer[STRINGSIZE]; + user_event usr; + char *valStr; + int dimString; + int smode; + const char *operation=NULL; + + ostrstream memstr(buffer, STRINGSIZE); + + valStr = resampText->GetValue(); + dimString = parseResampleString(valStr, NULL); + scale = new double[dimString]; + parseResampleString(valStr, scale); + + for (i=0; i<dimString; i++) + { + if (scale[i] <= 0.0) return 0; + if (scale[i] != 1.0) break; + } + + smode = scaleMode->GetSelection(); + + operationPrologue(); + + res = 0; + for (i=0; i<coll->number; i++) + { + if (list->Selected(i)) + { + double totalScale = 1.0; + + baseType = rviewGetBasetype((r_Object*)(&(*(coll->mddObjs[i].mdd)))); + oldInterv = (coll->mddObjs[i].mdd)->spatial_domain(); + dim = oldInterv.dimension(); + newInterv = r_Minterval(dim); useInterv = r_Minterval(dim); + for (j=0; j<(int)dim; j++) + { + double h; + + // Use the entire source object for scaling + useInterv << oldInterv[j]; + h = (j < dimString) ? scale[j] : scale[dimString-1]; totalScale *= h; + newInterv << r_Sinterval((r_Range)(oldInterv[j].low()), (r_Range)(oldInterv[j].low() + h*(oldInterv[j].high() - oldInterv[j].low() + 1) - 1)); + } + if (smode == 0) // resampling mode + { + if (totalScale > 1.0) + { + operation = lman->lookup("operationUpsamp"); + j = mdd_objectScaleInter(coll->mddObjs[i].mdd, useInterv, newMdd, newInterv); + } + else + { + operation = lman->lookup("operationDownsamp"); + j = mdd_objectScaleAverage(coll->mddObjs[i].mdd, useInterv, newMdd, newInterv); + } + } + else // simple scale mode + { + operation = lman->lookup("operationScale"); + j = mdd_objectScaleSimple(coll->mddObjs[i].mdd, useInterv, newMdd, newInterv); + } + if (j != 0) + { + usr.type = usr_mdd_dying; usr.data = (void*)(&((coll->mddObjs[i]).mdd)); + frames->broadcastUserEvent(usr); + coll->mddObjs[i].flags = 0; + (coll->mddObjs[i].mdd).destroy(); + coll->mddObjs[i].mdd = newMdd; + res++; + list->SetString(i, mddDescriptorString(memstr, i)); + selectedItems[i>>3] &= ~(1<<(i&7)); + } + } + } + + operationEpilogue(operation); + + delete [] scale; + + lastSelected = updateSelection(); + + return res; +} + + +int rviewResult::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (&obj == (wxObject*)list) + { + if (type == wxEVENT_TYPE_LISTBOX_COMMAND) + { + //cout << "rviewResult::process Listbox command" << endl; + lastSelected = updateSelection(); + return 1; + } + if (type == wxEVENT_TYPE_LISTBOX_DCLICK_COMMAND) + { + if ((lastSelected >= 0) && (lastSelected < coll->number)) + { + // This check is necessary + if (!list->Selected(lastSelected)) + { + list->SetSelection(lastSelected, TRUE); + if (selectedItems != NULL) selectedItems[lastSelected>>3] |= (1<<(lastSelected&7)); + } + openViewer(lastSelected); + } + return 1; + } + } + if (&obj == (wxObject*)choice) + { + if (type == wxEVENT_TYPE_CHOICE_COMMAND) + { + prefs->lastDisplay = choice->GetSelection(); + prefs->markModified(); + return 1; + } + } + if (&obj == (wxObject*)resampText) + { + if (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND) + { + resampleSelection(); + return 1; + } + } + if (&obj == (wxObject*)resampBut) + { + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + resampleSelection(); + return 1; + } + } + + return 0; +} + + + +int rviewResult::userEvent(const user_event &ue) +{ + if (ue.type == usr_viewer_closed) + { + mdd_frame *mf = (mdd_frame*)(ue.data); + int i; + + //{FILE *fp=fopen("xxx", "a+"); fprintf(fp, "Received viewer closed %p --- %p, %d\n", mf->mdd.ptr(), coll->mddObjs[0].mdd.ptr(), mf->flags); fclose(fp);} + for (i=0; i<coll->number; i++) + { + // must compare pointers, comparing r_Refs has strange effects on Win + if (mf->mdd.ptr() == coll->mddObjs[i].mdd.ptr()) break; + } + if (i < coll->number) + { + coll->mddObjs[i].flags &= ~(mf->flags); + return 1; + } + } + else if (ue.type == usr_child_closed) + { + if (ue.data == (void*)typeManager) typeManager = NULL; + return 1; + } + else if (ue.type == usr_typeman_convert) + { + convertSelectedItems(); + typeManager->Close(TRUE); typeManager = NULL; + return 1; + } + else if (ue.type == usr_close_viewers) + { + Close(TRUE); + return 1; + } + + return 0; +} + + + +void rviewResult::convertSelectedItems(void) +{ + int itemNo; + user_event usr; + char buffer[STRINGSIZE]; + ostrstream memstr(buffer, STRINGSIZE); + + operationPrologue(); + + for (itemNo=0; itemNo < coll->number; itemNo++) + { + if ((list->Selected(itemNo)) && (!coll->mddObjs[itemNo].mdd.is_null())) + { + r_Ref<r_GMarray> newMdd; + + if (typeManager->convert(coll->mddObjs[itemNo].mdd, newMdd) == 0) + { + usr.type = usr_mdd_dying; usr.data = (void*)(&((coll->mddObjs[itemNo]).mdd)); + frames->broadcastUserEvent(usr); + coll->mddObjs[itemNo].mdd.destroy(); + coll->mddObjs[itemNo].mdd = newMdd; + list->SetString(itemNo, mddDescriptorString(memstr, itemNo)); + } + else + { + newMdd.destroy(); + } + } + } + + operationEpilogue(lman->lookup("operationTypeProj")); + + lastSelected = updateSelection(); +} + + + +// Set up some variables and widgets in the frame. +void rviewResult::setupVariables(void) +{ + int x, y; + char *displayModes[RVIEW_RESDISP_NUMBER]; + char *scaleModes[2]; + wxMenu *mbarMenus[2]; + char buffer[STRINGSIZE]; + + coll = NULL; panel = NULL; list = NULL; collName = NULL; collType = NULL; + group = NULL; mBar = NULL; typeManager = NULL; + lastSelected = -1; + selectedItems = NULL; + + CreateStatusLine(1); + + mbarMenus[0] = new wxMenu; + mbarMenus[0]->Append(MENU_RSLT_ITEM_OPENALL, ""); + mbarMenus[0]->Append(MENU_RSLT_ITEM_THUMBALL, ""); + mbarMenus[0]->Append(MENU_RSLT_ITEM_CLOSE, ""); + + mbarMenus[1] = new wxMenu; + mbarMenus[1]->Append(MENU_RSLT_SLCT_SLCTALL, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_CLEAR, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_OPEN, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_THUMB, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_DELETE, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_ENDIAN, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_TYPEMAN, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_INFO, ""); + + mBar = new wxMenuBar; + sprintf(buffer, "&%s", lman->lookup("menRsltItem")); + mBar->Append(mbarMenus[0], buffer); + sprintf(buffer, "&%s", lman->lookup("menRsltSlct")); + mBar->Append(mbarMenus[1], buffer); + + GetClientSize(&x, &y); + panel = new wxPanel((wxWindow*)this, 0, 0, x, y); + x -= 2*result_border; y -= 2*result_border; + + // Create a group box surrounding the collection-items + group = new wxGroupBox(panel, "", 0, 0, x, result_header, wxBORDER); + x -= 2*result_border; y -= 2*result_border; + // Create the message widget showing the collection name + collName = new wxMessage(panel, "", 2*result_border, result_lheight + 2*result_border, x, result_lheight, 0, "message"); + // The same for the collection base type + collType = new wxMessage(panel, "", 2*result_border, 2*result_lheight + 2*result_border, x, result_lheight, 0, "message"); + // The info string + collInfo = new wxMessage(panel, "", 2*result_border, 3*result_lheight + 2*result_border, x, result_lheight, 0, "message"); + + // And the choice item for the display modes. + // In contrast to the menu stuff non-persistent objects are OK here. + displayModes[0] = lman->lookup("dispModeImage"); + displayModes[1] = lman->lookup("dispModeVolume"); + displayModes[2] = lman->lookup("dispModeOrtho"); + displayModes[3] = lman->lookup("dispModeHeight"); + displayModes[4] = lman->lookup("dispModeChart"); + displayModes[5] = lman->lookup("dispModeTable"); + displayModes[6] = lman->lookup("dispModeSound"); + displayModes[7] = lman->lookup("dispModeString"); + choice = new rviewChoice(panel, RVIEW_RESDISP_NUMBER, displayModes, lman->lookup("dispModeLabel")); + choice->SetSelection(prefs->lastDisplay); + + scaleModes[0] = lman->lookup("textResample"); + scaleModes[1] = lman->lookup("textScale"); + scaleMode = new rviewChoice(panel, 2, scaleModes); + scaleMode->SetSelection(0); + scaleMode->SetLabel(""); + + sprintf(buffer, "%f", 1.0); + resampText = new rviewText(panel, buffer); + resampBut = new rviewButton(panel, lman->lookup("textOK")); + + choice->GetSize(&x, &y); + cbfactor = x - result_cwidth; + + SetMenuBar(mBar); +} + + + + +void rviewResult::OnMenuCommand(int id) +{ + switch (id) + { + case MENU_RSLT_ITEM_OPENALL: + { + for (int i=0; i<coll->number; i++) + { + openViewer(i); + } + } + break; + case MENU_RSLT_ITEM_THUMBALL: + { + int i; + rviewThumb *thumb; + + thumb = new rviewThumb; + for (i=0; i<coll->number; i++) + { + thumb->addMDD(coll->mddObjs[i].mdd); + } + registerChild((rviewFrame*)thumb); + } + break; + case MENU_RSLT_ITEM_CLOSE: + { + this->Close(TRUE); + } + break; + case MENU_RSLT_SLCT_SLCTALL: + { + for (int i=0; i<coll->number; i++) + { + if (!list->Selected(i)) + { + list->SetSelection(i, TRUE); + } + } + if (selectedItems != NULL) memset(selectedItems, 0xff, (coll->number + 7) >> 3); + configureGrey(); + } + break; + case MENU_RSLT_SLCT_CLEAR: + { + for (int i=0; i<coll->number; i++) + { + if (list->Selected(i)) + { + list->SetSelection(i, FALSE); + } + } + if (selectedItems != NULL) memset(selectedItems, 0, (coll->number + 7) >> 3); + configureGrey(); + } + break; + case MENU_RSLT_SLCT_OPEN: + { + int i; + + for (i=0; i<coll->number; i++) + { + if (list->Selected(i)) + openViewer(i); + } + } + break; + case MENU_RSLT_SLCT_THUMB: + { + int i; + + for (i=0; i<coll->number; i++) + { + if (list->Selected(i)) break; + } + if (i < coll->number) + { + rviewThumb *thumb; + + thumb = new rviewThumb; + for (i=0; i<coll->number; i++) + { + if (list->Selected(i)) + thumb->addMDD(coll->mddObjs[i].mdd); + } + registerChild((rviewFrame*)thumb); + } + } + break; + case MENU_RSLT_SLCT_DELETE: + { + int itemNo, j; + user_event usr; + + do + { + for (itemNo=0; itemNo < coll->number; itemNo++) + if (list->Selected(itemNo)) break; + + if (itemNo >= coll->number) break; + list->Delete(itemNo); + // Notify all open display frames that a particular mdd object will die + usr.type = usr_mdd_dying; usr.data = (void*)(&((coll->mddObjs[itemNo]).mdd)); + frames->broadcastUserEvent(usr); + if (!coll->mddObjs[itemNo].mdd.is_null()) + { + coll->mddObjs[itemNo].mdd.destroy(); + } + (coll->number)--; + for (j=itemNo; j<coll->number; j++) + { + coll->mddObjs[j] = coll->mddObjs[j+1]; + // Don't forget to copy the selection too + selectedItems[j>>3] &= ~(1<<(j&7)); + if ((selectedItems[(j+1)>>3] & (1<<((j+1)&7))) != 0) selectedItems[j>>3] |= (1<<(j&7)); + } + } + while (1); + lastSelected = updateSelection(); + configureGrey(); + } + break; + case MENU_RSLT_SLCT_ENDIAN: + { + int itemNo; + user_event ue; + + operationPrologue(); + + ue.type = usr_mdd_dying; + for (itemNo = 0; itemNo < coll->number; itemNo++) + { + if ((list->Selected(itemNo)) && (!coll->mddObjs[itemNo].mdd.is_null())) + { + r_Minterval useInterv; + + ue.data = (void*)(&((coll->mddObjs[itemNo]).mdd)); + frames->broadcastUserEvent(ue); // close all open viewers + useInterv = coll->mddObjs[itemNo].mdd->spatial_domain(); + mdd_objectChangeEndianness(coll->mddObjs[itemNo].mdd, useInterv); + } + } + operationEpilogue(lman->lookup("operationEndian")); + } + break; + case MENU_RSLT_SLCT_TYPEMAN: + if (typeManager == NULL) + { + int itemNo; + const r_Type *sampleType = NULL; + + for (itemNo = 0; itemNo < coll->number; itemNo++) + { + if ((list->Selected(itemNo)) && (!coll->mddObjs[itemNo].mdd.is_null())) + { + sampleType = coll->mddObjs[itemNo].mdd->get_base_type_schema(); + break; + } + } + /*try { + sampleType = r_Type::get_any_type("struct { struct { float u, float v, float w }, float p, float te, float ed, float den, float vis }"); + } catch (r_Error &err) { + cerr << err.what() << endl; + }*/ + if (sampleType != NULL) + { + if (typeManager == NULL) + typeManager = new rviewTypeMan(this, sampleType); + } + } + break; + case MENU_RSLT_SLCT_INFO: break; + default: break; + } +} + + + +void rviewResult::configureGrey(void) +{ + int i; + + mBar->Enable(MENU_RSLT_ITEM_OPENALL, (coll->number > 0)); + mBar->Enable(MENU_RSLT_ITEM_THUMBALL, (coll->number> 0)); + + for (i=0; i<coll->number; i++) + { + if (list->Selected(i)) break; + } + bool enable = (i < coll->number); + + mBar->Enable(MENU_RSLT_SLCT_CLEAR, enable); + mBar->Enable(MENU_RSLT_SLCT_OPEN, enable); + mBar->Enable(MENU_RSLT_SLCT_THUMB, enable); + mBar->Enable(MENU_RSLT_SLCT_DELETE, enable); + mBar->Enable(MENU_RSLT_SLCT_ENDIAN, enable); + mBar->Enable(MENU_RSLT_SLCT_TYPEMAN, enable); + mBar->Enable(MENU_RSLT_SLCT_INFO, enable); +} + + + + + +/* + * rView about window + */ + +const int rviewAbout::about_width = 300; +const int rviewAbout::about_height = 150; +const int rviewAbout::about_border = 8; +const int rviewAbout::about_pheight = 50; +const int rviewAbout::about_bwidth = 80; +const int rviewAbout::about_bheight = 30; +const int rviewAbout::about_mheight = 16; + +rviewAbout::rviewAbout(void) : rviewFrame(NULL, "", -1, -1, about_width, about_height) +{ + labels = NULL; numlines = 0; + panel = new wxPanel((wxWindow*)this, 0, 0, about_width, about_height); + okBut = new rviewButton(panel); + + label(); + + OnSize(about_width, about_height); +} + + +rviewAbout::~rviewAbout(void) +{ + // the messages themselves will be sorted out by the panel destructor. + if (labels != NULL) delete [] labels; +} + + +const char *rviewAbout::getFrameName(void) const +{ + return "rviewAbout"; +} + +rviewFrameType rviewAbout::getFrameType(void) const +{ + return rviewFrameTypeAbout; +} + + +void rviewAbout::deleteLabels(void) +{ + if (labels != NULL) + { + int i; + + for (i=0; i<numlines; i++) delete labels[i]; + delete [] labels; labels = NULL; numlines = 0; + } +} + + +void rviewAbout::label(void) +{ + int i, number, x, y; + + SetTitle(lman->lookup("titleAbout")); + okBut->SetLabel(lman->lookup("textOK")); + + deleteLabels(); + + GetClientSize(&x, &y); + y = about_border; + number = atoi(lman->lookup("rviewAboutLineNum")); + if (number >= 1) + { + char lineName[64]; + + numlines = number; + labels = new wxMessage *[numlines]; x -= 2*about_border; + for (i=0; i<number; i++) + { + sprintf(lineName, "rviewAboutLine%d", i); + labels[i] = new wxMessage(panel, lman->lookup(lineName), about_border, y, x, about_mheight, 0, "message"); + y += about_mheight; + } + } + SetSize(-1, -1, x, y + about_pheight + rview_window_extra_height); +} + + +int rviewAbout::process(wxObject &obj, wxEvent &evt) +{ + if ((&obj == (wxObject*)okBut) && (evt.GetEventType() == wxEVENT_TYPE_BUTTON_COMMAND)) + { + Show(FALSE); return 1; + } + return 0; +} + + +void rviewAbout::OnSize(int w, int h) +{ + int x, y; + + GetClientSize(&x, &y); + + panel->SetSize(0, 0, x, y); + x -= 2*about_border; y = about_border; + if (panel != NULL) + { + for (int i=0; i<numlines; i++) + { + labels[i]->SetSize(about_border, y, x, about_mheight); + y += about_mheight; + } + } + x -= about_bwidth; y += (about_pheight - about_bheight)/2; + okBut->SetSize(x/2, y, about_bwidth, about_bheight); +} + + + + + +/* + * rviewStringSet window + */ + +const int rviewStringSet::strset_width = 300; +const int rviewStringSet::strset_height = 250; +const int rviewStringSet::strset_border = 8; +const int rviewStringSet::strset_reserve = 50; +const int rviewStringSet::strset_bwidth = 70; +const int rviewStringSet::strset_bheight = 40; +const int rviewStringSet::strset_mheight = 20; + +rviewStringSet::rviewStringSet(collection_desc *desc) : rviewFrame(NULL, "", -1, -1, strset_width, strset_height) +{ + int w, h; + char buffer[STRINGSIZE]; + + panel = new wxPanel((wxWindow*)this, 0, 0, strset_width, strset_height); + list = new wxListBox(panel, (wxFunction)rviewEventHandler, "", wxSINGLE, 0, 0, strset_width, strset_height, desc->number, desc->strObjs, wxALWAYS_SB); + dismiss = new rviewButton(panel); + + sprintf(buffer, "%s: %s", lman->lookup("textCollName"), (desc->collName == NULL) ? "" : desc->collName); + collName = new wxMessage(panel, buffer, 0, 0, 10, 10, 0, "message"); + sprintf(buffer, "%s: %s", lman->lookup("textCollType"), (desc->collType == NULL) ? "" : desc->collType); + collType = new wxMessage(panel, buffer, 0, 0, 10, 10, 0, "message"); + char* tmpChar1 = "message"; + char* tmpChar2 = ""; + collInfo = new wxMessage(panel, (desc->collInfo == NULL) ? tmpChar2 : desc->collInfo, 0, 0, 10, 10, 0, tmpChar1); + + GetClientSize(&w, &h); + + label(); + + OnSize(w, h); + + Show(TRUE); +} + + +rviewStringSet::~rviewStringSet(void) +{ +} + + +const char *rviewStringSet::getFrameName(void) const +{ + return "rviewStringSet"; +} + +rviewFrameType rviewStringSet::getFrameType(void) const +{ + return rviewFrameTypeStrSet; +} + + +void rviewStringSet::label(void) +{ + SetTitle(lman->lookup("titleStringSet")); + list->SetLabel(lman->lookup("strSetList")); + dismiss->SetLabel(lman->lookup("textClose")); +} + + +void rviewStringSet::OnSize(int w, int h) +{ + int x, y; + int head; + + GetClientSize(&x, &y); + panel->SetSize(0, 0, x, y); + head = 3*(strset_mheight + strset_border); + x -= 2*strset_border; y -= 2*strset_border; + collName->SetSize(strset_border, strset_border, x, strset_mheight); + collType->SetSize(strset_border, strset_mheight + 2*strset_border, x, strset_mheight); + collInfo->SetSize(strset_border, 2*strset_mheight + 3*strset_border, x, strset_mheight); + + list->SetSize(strset_border, strset_border + head, x, y - head - strset_reserve); + dismiss->SetSize((x - strset_bwidth) / 2, y + 2*strset_border - (strset_reserve + strset_bheight) / 2, strset_bwidth, strset_bheight); +} + + +int rviewStringSet::process(wxObject &obj, wxEvent &evt) +{ + if (&obj == (wxObject*)dismiss) + { + this->Close(TRUE); + return 1; + } + return 0; +} + + +int rviewStringSet::getNumItems(void) +{ + return list->Number(); +} + + +void rviewStringSet::addItem(const char *string) +{ + list->Append((char*)string); +} + + +char *rviewStringSet::getItem(int number) +{ + return list->GetString(number); +} + + +#ifdef EARLY_TEMPLATE +#undef __EXECUTABLE__ +#endif + +#endif // !defined(EARLY_TEMPLATE) || !defined(__EXECUTABLE__) + + + +#if (!defined(EARLY_TEMPLATE) || defined(__EXECUTABLE__)) + +/* + * Templates + */ + +// For placement new (DynamicStack template) +#include <new> + +/* + * Use malloc/free, placement new and explicit calls to destructors. + * Use placement new when initializing on the stack because that's + * raw memory, use assignment when initializing parameters + */ +template<class T> +DynamicStack<T>::DynamicStack(unsigned int gran) +{ + number = 0; max = 0; + if ((stack = (T*)malloc(gran * sizeof(T))) != NULL) + { + granularity = gran; + max = granularity; + memset(stack, 0, max * sizeof(T)); + } +} + + +template<class T> +DynamicStack<T>::DynamicStack(const DynamicStack<T> &src) +{ + number = 0; max = 0; + if ((stack = (T*)malloc(src.max * sizeof(T))) != NULL) + { + granularity = src.granularity; + max = src.max; + memset(stack, 0, max*sizeof(T)); + number = src.number; + + unsigned int i; + + // use loop for class safety + for (i=0; i<number; i++) + new(stack + i) T(src.stack[i]); // placement new + } +} + + +template<class T> +DynamicStack<T>::~DynamicStack(void) +{ + unsigned int i; + + for (i=0; i<number; i++) + stack[i].~T(); // direct destructor call + + if (stack != NULL) + free(stack); +} + + +template<class T> +int DynamicStack<T>::push(const T &item) +{ + if (ensureFree() != 0) return -1; + new(stack + number++) T(item); // placement new + return 0; +} + + +template<class T> +int DynamicStack<T>::pop(T &item) +{ + if (number == 0) return -1; + item = stack[--number]; // assignment + stack[number].~T(); // direct destructor call + return 0; +} + + +template<class T> +int DynamicStack<T>::peek(T &item) const +{ + if (number == 0) return -1; + item = stack[number-1]; // assignment + return 0; +} + + +template<class T> +unsigned int DynamicStack<T>::getNumber(void) const +{ + return number; +} + + +template<class T> +int DynamicStack<T>::ensureFree(void) +{ + if (number >= max) + { + if ((stack = (T*)realloc(stack, (max + granularity) * sizeof(T))) == NULL) + { + max = 0; number = 0; return -1; + } + memset(stack + max, 0, granularity * sizeof(T)); + max += granularity; + } + return 0; +} + +#endif diff --git a/applications/rview/rviewUtils.hh b/applications/rview/rviewUtils.hh new file mode 100644 index 0000000..32c292c --- /dev/null +++ b/applications/rview/rviewUtils.hh @@ -0,0 +1,1045 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** +* PURPOSE: +* +* Central definitions of shared objects and constants, global tool functions. +* +* - All menu codes used in the program (MENU_...) +* - Global tool functions for handling collections, base types, projection +* strings, X events, ... . See ``Global functions''. +* - rviewFrame class which must be the base class of all frames used in rView. +* - rviewFrameMgr class for handling all of rView's frames (dispatching events +* etc) +* - rviewMultiline class for displaying several lines of non-editable text. +* - rviewDialog class which is the base class for all rView's dialog windows. +* - rviewErrorbox class, derived from rviewDialog. +* - rviewProgress class, derived from rviewDialog. +* - rviewResults class for displaying database results and dispatching object +* viewers. Relies on code provided by rviewMDD for scaling/resampling/endian +* conversions. +* - rviewAbout class for displaying information about rView itself. +* - rviewStringSet class for displaying a set of strings in a scrollable +* list box. +* +* COMMENTS: +* none +*/ + + + +#ifndef _RVIEW_UTILS_H_ +#define _RVIEW_UTILS_H_ + + +#include <sstream> + +#include "rasodmg/ref.hh" +#include "rasodmg/marray.hh" +#include "rasodmg/gmarray.hh" +#include "rasodmg/database.hh" + +#include "labelManager.hh" + +// #include "wx_scrol.h" +#include "wx/xrc/xh_scrol.h" +#include "wx/msgout.h" + +// #define wxUSE_GLCANVAS 1 // need this? -- PB 2006-jan-01 +#include <wx/glcanvas.h> + + +#ifdef __VISUALC__ +#define DIR_SEPARATOR "\\" +#define DIR_SEPARATORC '\\' +#else +#define DIR_SEPARATOR "/" +#define DIR_SEPARATORC '/' +#endif + +extern const int rview_window_extra_height; +extern const int rview_choice_sub_width; + + + +#define STRINGSIZE 256 + + + +// base type to name translations + +// Identifiers for base types +enum rviewBaseType { + rbt_none, + rbt_bool, + rbt_char, + rbt_uchar, + rbt_short, + rbt_ushort, + rbt_long, + rbt_ulong, + rbt_rgb, + rbt_float, + rbt_double +}; + +// Maximum dimension of data rview can deal with +#define MAXIMUM_DIMENSIONS 4 + +// String names for objects / sets over the various basetypes. +extern char *rviewBaseTypes[]; +extern char *rviewTypeNames[][MAXIMUM_DIMENSIONS]; +extern char *rviewSetNames[][MAXIMUM_DIMENSIONS]; + +// Base type to image type mapping +extern int rviewImageTypes[]; +// Possible types: +enum rview_image_types { + RVIEW_IMGTYPE_NONE, + RVIEW_IMGTYPE_MONO, + RVIEW_IMGTYPE_GREY, + RVIEW_IMGTYPE_HIGH, + RVIEW_IMGTYPE_GREY12, + RVIEW_IMGTYPE_TRUE24, + RVIEW_IMGTYPE_TRUE32 +}; + + + +// Mouse buttons +#define MOUSE_LEFT 1 +#define MOUSE_MIDDLE 2 +#define MOUSE_RIGHT 4 +// Ctrl pressed while mouse event? +#define MOUSE_CONTROL 8 +// Maximum distance of mouse pointer to an object for manipulating / dragging +#define MOUSE_HOTZONE 8 + + + +// Menu items + +enum rview_menu_ident { + MENU_DUMMY_IDENT, // 0 is illegal for a menu id + + // First the ones for the main window + MENU_MAIN_FILE_QUERY, + MENU_MAIN_FILE_PREFS, + MENU_MAIN_FILE_EXIT, + + MENU_MAIN_VIEW_OPEN, + MENU_MAIN_VIEW_CLOSE, + + MENU_MAIN_COLL_LOOK, + MENU_MAIN_COLL_LKSCL, + MENU_MAIN_COLL_LKORTH, + MENU_MAIN_COLL_CREATE, + MENU_MAIN_COLL_DELETE, + + MENU_MAIN_HELP_ABOUT, + + // Now the menu items for the results window + MENU_RSLT_ITEM_OPENALL, + MENU_RSLT_ITEM_THUMBALL, + MENU_RSLT_ITEM_CLOSE, + + MENU_RSLT_SLCT_SLCTALL, + MENU_RSLT_SLCT_CLEAR, + MENU_RSLT_SLCT_OPEN, + MENU_RSLT_SLCT_THUMB, + MENU_RSLT_SLCT_DELETE, + MENU_RSLT_SLCT_ENDIAN, + MENU_RSLT_SLCT_TYPEMAN, + MENU_RSLT_SLCT_INFO, + + // Now the menu items for the display windows + MENU_DISP_DATA_INSERT, + MENU_DISP_DATA_INSERTPRO, + MENU_DISP_DATA_SAVE, + MENU_DISP_DATA_CLOSE, + MENU_DISP_DATA_SAVETIFF, + MENU_DISP_VIEW_SAVE, + MENU_DISP_VIEW_LOAD, + MENU_DISP_VIEW_SHOW, + // Additional menus in charts + MENU_CHART_MODE_BAR, + MENU_CHART_MODE_LINE, + MENU_CHART_MODE_SPLINE, + // Additional menus in images + MENU_IMAGE_MODE_FLAT, + MENU_IMAGE_MODE_SURF, + MENU_IMAGE_MODE_VOXEL, + MENU_IMAGE_MODE_HEIGHT, + MENU_IMAGE_SETUP_RENDER, + MENU_IMAGE_SETUP_RCONTROL, + MENU_IMAGE_SETUP_CSPACE, + MENU_IMAGE_SETUP_MOVIE, + MENU_IMAGE_MOVIE_ONCE, + MENU_IMAGE_MOVIE_START, + MENU_IMAGE_MOVIE_SWITCH, + MENU_IMAGE_CSPACE_ON, + MENU_IMAGE_CSPACE_FULL, + MENU_IMAGE_CSPACE_PROJ, + MENU_IMAGE_CSPACE_EDIT, + // Additional menus in tables + MENU_TABLE_MODE_DECIMAL, + MENU_TABLE_MODE_OCTAL, + MENU_TABLE_MODE_HEX, + + // Thumbnail window + MENU_THUMB_DATA_CLOSE, + MENU_THUMB_SETUP_CSPACE, + MENU_THUMB_CSPACE_ON, + MENU_THUMB_CSPACE_FULL, + MENU_THUMB_CSPACE_EDIT, + + // Now the menu items for the query window + MENU_QUERY_FILE_OPEN, + MENU_QUERY_FILE_SAVE, + MENU_QUERY_FILE_EXIT, + MENU_QUERY_EDIT_CUT, + MENU_QUERY_EDIT_COPY, + MENU_QUERY_EDIT_PASTE, + MENU_QUERY_HELP_HELP, + + // Total number of menus = first free menu ID for dynamic menus: + MENU_TOTAL_NUMBER +}; + +// Hotlist menu has no fixed length. Set upper limit with MENU_QUERY_HOTRANGE, however +#define MENU_QUERY_HOTLIST MENU_TOTAL_NUMBER +// upper limit for queries in hotlist +#define MENU_QUERY_HOTRANGE 64 + + + +// Number of display modes +#define RVIEW_RESDISP_NUMBER 8 +// Flags for each MDD object saying whether it's opened in a display mode +#define RVIEW_RESDISP_CHART 1 +#define RVIEW_RESDISP_TABLE 2 +#define RVIEW_RESDISP_SOUND 4 +#define RVIEW_RESDISP_IMGFLAT 8 +#define RVIEW_RESDISP_IMGVOLM 16 +#define RVIEW_RESDISP_IMGHGHT 32 +#define RVIEW_RESDISP_IMGOSECT 64 +#define RVIEW_RESDISP_STRVIEW 128 +// this one is special because it can't be used from the results window +#define RVIEW_RESDISP_IMGSCLD 256 + + + + + +// For frame_list type. Full declaration below. +class rviewFrame; +class rviewFrameMgr; + + + + +/* + * Types and structures. + */ + +// User events +enum rviewUserEvent { + usr_none, + usr_child_closed, // data = rviewFrame * + usr_mdd_dying, // data = r_Ref<r_GMarray>* + usr_db_opened, + usr_db_closed, + usr_update_closed, // data = r_Ref<r_GMarray>* + usr_viewer_closed, // data = mdd_frame * + usr_cspace_changed, // data = colourspaceMapper* + usr_typeman_convert, // data = rviewTypeMan * + usr_close_viewers +}; + + +// The frame list item used but the frameManager. +typedef struct frame_list { + rviewFrame *frame; + frame_list *next; +} frame_list; + +// An MDD object and some flags +typedef struct mdd_frame { + r_Ref < r_GMarray > mdd; + unsigned int flags; +} mdd_frame; + +// Collection descriptor (for lookup / query exection) +// mddObjs != NULL ==> collection of marrays +// strObjs != NULL ==> collection of strings +typedef struct collection_desc { + char *collName; + char *collType; + char *collInfo; + int number; + mdd_frame *mddObjs; + char **strObjs; +} collection_desc; + +// User event used in rviewFrames / frameManager +typedef struct user_event { + rviewUserEvent type; + void *data; +} user_event; + +// Keyword to identifier lookup structure +typedef struct keyword_to_ident { + int ident; + char *keyword; +} keyword_to_ident; + +// same with a const char * +typedef struct keyword_to_ident_c { + int ident; + const char *keyword; +} keyword_to_ident_c; + + + + +/* + * Global functions + */ + +// Free all memory allocated by a filled collection descriptor +void rviewDeleteCollection(collection_desc *coll); + +// Generic event handler. Calls the frame manager: +void rviewEventHandler(wxObject &obj, wxEvent &evt); + +// Parse a projection string and return its dimensionality +extern int rviewParseProjection(const r_Minterval &interv, r_Point &pt1, r_Point &pt2, const char *projString, unsigned int *freeDims=NULL, r_Point *mapIndex=NULL); + +// Return an object's base type as integer identifier +extern rviewBaseType rviewGetBasetype(r_Object *obj); + +// Prints the contents of a cell with a given RasDaMan base type into the buffer, returns number +// of characters written. +extern int rviewPrintTypedCell(const r_Type *baseType, char *buffer, char *data, int numberBase); + +// Quicksorts a char *[] +extern void rviewQuicksortStrings(char *array[], int from, int to); + +// Init character lookup tables +extern void rviewInitCharacterTables(void); + +// Look up a keyword in a sorted (!) keyword_to_ident array +extern int rviewLookupKeyword(const char *key, const keyword_to_ident_c *kti, int tabsize, bool caseSensitive); + +// Checks if colourspace mapping is possible for a given base type +// and sets up some variables if so. +class colourspaceMapper; +struct colourspace_params_s; +extern int rviewCheckInitCspace(rviewBaseType baseType, colourspaceMapper **csmap, r_Ref<r_GMarray> &mddObj, bool fullRange=FALSE, r_Minterval *domain=NULL, int w=0, int *newPitch=NULL, int *newDepth=NULL, int *newPad=NULL, int *virtualPitch=NULL, const struct colourspace_params_s *cp=NULL); + +// Smart number conversions: also understand 0x prefix +extern long asctol(const char *str); +extern int asctoi(const char *str); +extern double asctof(const char *str); +extern long stringtol(const char *str, char **endptr=NULL); +extern double stringtof(const char *str, char **endptr=NULL); + + + + +// Dynamic string class +class DynamicString +{ + public: + + DynamicString(void); + DynamicString(const DynamicString &ds); + DynamicString(const char *str); + ~DynamicString(void); + + DynamicString &first(const char *str, unsigned int num); + DynamicString &operator=(const DynamicString &ds); + DynamicString &operator=(const char *str); + bool operator==(const DynamicString &ds) const; + bool operator==(const char *str) const; + + const char *ptr(void) const; + operator const char*(void) const; + + + private: + + char *myString; + + static const char emptyString[]; +}; + + + +// A stack template class +template<class T> +class DynamicStack +{ + public: + + DynamicStack(unsigned int gran=8); + DynamicStack(const DynamicStack<T> &src); + ~DynamicStack(void); + + int push(const T &item); + int pop(T &item); + int peek(T &item) const; + unsigned int getNumber(void) const; + + + protected: + + int ensureFree(void); + + unsigned int number, max, granularity; + T *stack; +}; + + + + + +// Frame types +enum rviewFrameType { + rviewFrameTypeGeneric, + rviewFrameTypeMain, + rviewFrameTypeQuery, + rviewFrameTypePrefs, + rviewFrameTypeAbout, + rviewFrameTypeDialog, + rviewFrameTypeErrorbox, + rviewFrameTypeProgress, + rviewFrameTypeStrSet, + rviewFrameTypeCspace, + rviewFrameTypeImgSet, + rviewFrameTypeRenCtrl, + rviewFrameTypeResult, + rviewFrameTypeThumb, + rviewFrameTypeDisplay, + rviewFrameTypeImage, + rviewFrameTypeFltBsImage, + rviewFrameTypeFlatImage, + rviewFrameTypeRndImage, + rviewFrameTypeVolImage, + rviewFrameTypeHghtImage, + rviewFrameTypeScaledImage, + rviewFrameTypeOSectionImage, + rviewFrameTypeChart, + rviewFrameTypeTable, + rviewFrameTypeSound, + rviewFrameTypeStringViewer, + rviewFrameTypeRenView, + rviewFrameTypeNumberOfTypes +}; + + +/* + * The base class for all frames. This makes managing open frames + * in a uniform way possible. Include after wx.h ! + */ + +class rviewFrame: public wxFrame +{ + public: + + rviewFrame(wxFrame *parent, char *title, int x, int y, int w, int h); + virtual ~rviewFrame(void); + + // When the checkobj(...) method is called the frame checks whether + // it knows obj (e.g. it's a button contained in the frame) and claims + // the broadcast by returning a value != 0. This is used in frame manager + // broadcasts. You should not try to get involved with derived classes + // at this point. + int checkobj(wxObject &obj); + + // When the label() method is called the derived frame class + // has to relabel all its widgets + virtual void label(void) = 0; + + // The process() method is called for the object that claimed the + // checkobj() function. It has to process the event and return a value + // != 0 if it did anything, 0 if the event was ignored. The two + // functions are separated because the Frame manager broadcast call + // has to be re-entrant and we'll get huge problems if the frame + // list changes during the loop. + virtual int process(wxObject &obj, wxEvent &evt) = 0; + + // The userEvent method is called to notify frames of events like + // the mdd object a frame displays being deleted. + virtual int userEvent(const user_event &ue); + + // Called when the frame is to be killed. May be overloaded, but normally + // doesn't have to be. + virtual int requestQuit(int level); + + // Called for mouse events of some logical child. Normally ignored. + virtual void childMouseEvent(wxWindow *child, wxMouseEvent &mevt); + + // The setParent(parent) function makes this frame a (logical) child + // of parent. This will remove the frame from the parent's child frame + // list when it's deleted. + void setParent(rviewFrame *parent); + void registerChild(rviewFrame *child); + void deregisterChild(rviewFrame *child); + + // Returns the name of this class + virtual const char *getFrameName(void) const; + + // Returns the identifier of this frame + virtual rviewFrameType getFrameType(void) const; + + // By default each frame is closed by returning TRUE from this wxWindows + // function. If you wish to override this behaviour for certain frames, + // override the frame's virtual function OnClose(). + virtual bool OnClose(void); + + + protected: + + // For keeping track of child frames + rviewFrameMgr *frames; + rviewFrame *parentFrame; + int frameWidth, frameHeight; + + + + private: + + // Used internally by the checkobj() member function. + int checkobj_rec(wxWindow *whence, wxObject &obj); +}; + + + + +/* + * The frame manager. Provides central access to frames and + * offers functions like automatically re-labeling all existing + * frames. Each object of class rviewFrame registers itself here + * in the constructor and deregisters itself in the destructor. + */ + +class rviewFrameMgr +{ + public: + + rviewFrameMgr(void); + rviewFrameMgr(bool delChild); + ~rviewFrameMgr(void); + + void registerFrame(rviewFrame *client); + void deregisterFrame(rviewFrame *client); + int numberOfFrames(void) const; + void setDeleteMode(bool delChild); + + // The following functions cause the frame manager to iterate over + // all frames and call specific member functions. Some of these + // member functions (e.g. process) can stop the iteration by + // claiming the call. + void labelAll(void); + void broadcastEvent(wxObject &obj, wxEvent &evt); + int broadcastQuit(int level); + int broadcastUserEvent(const user_event &ue); + + + protected: + + frame_list *frameList; + frame_list *tailList; + int listLength; + bool deleteChildren; +}; + + + + + +/* + * A continuous text displayed in multiple lines + */ + +class rviewMultiline +{ + public: + + rviewMultiline(wxPanel *Panel, int X, int Y, int H, int Lines); + rviewMultiline(wxPanel *Panel, const char *Message, int X, int Y, int W, int H, int Lines); + ~rviewMultiline(void); + + // Returns the height used by the messages + int getMessageHeight(void) const; + + void rebuild(const char *Message, int W); + + // 10 * number of pixels used per character + static const int multiline_ppc10; + + + protected: + + void setupVariables(wxPanel *Panel, int X, int Y, int H, int Lines); + + wxPanel *parent; + wxMessageOutput **msg; + int lines; + int x, y, lHeight; +}; + + + +/* + * Convenient interface to standard widgets + */ +#include <wx/univ/textctrl.h> + +class rviewText : public wxTextCtrl +{ + public: + + rviewText(wxPanel *parent, const char *value=NULL, char *label="", int x=-1, int y=-1, int w=-1, int h=-1); + rviewText(long style, wxPanel *parent, const char *value=NULL, char *label="", int x=-1, int y=-1, int w=-1, int h=-1); + rviewText(wxPanel *parent, const DynamicString &value, char *label="", int x=-1, int y=-1, int w=-1, int h=-1); + rviewText(wxPanel *parent, int value, char *label="", int x=-1, int y=-1, int w=-1, int h=-1); + rviewText(wxPanel *parent, long value, char *label="", int x=-1, int y=-1, int w=-1, int h=-1); + rviewText(wxPanel *parent, double value, bool sciForm=FALSE, char *label="", int x=-1, int y=-1, int w=-1, int h=-1); + + void SetValue(char *value); + void SetValue(const char *value); + void SetValue(const DynamicString &value); + void SetValue(int value); + void SetValue(unsigned int value); + void SetValue(long value); + void SetValue(double value, bool sciFrom=FALSE); + + char *GetValue(void); + void GetValue(DynamicString &value); + void GetValue(int &value); + void GetValue(long &value); + void GetValue(float &value); + void GetValue(double &value); +}; + +class rviewButton : public wxButton +{ + public: + + rviewButton(wxPanel *parent, char *label="", int x=-1, int y=-1, int w=-1, int h=-1, long style=0); +}; + +// BUG IN wxCoice: NEVER CREATE A (wx|rview)Choice ITEM WITH AN EMPTY LABEL! +class rviewChoice : public wxChoice +{ + public: + + rviewChoice(wxPanel *parent, int n, char *choices[], char *label="X", int x=-1, int y=-1, int w=-1, int h=-1, long style=0); + // this is kind of a hack to fix wxWindows' lack of const + rviewChoice(wxPanel *parent, int n, const char *choices[], char *label="X", int x=-1, int y=-1, int w=-1, int h=-1, long style=0); +}; + +class rviewCheckBox : public wxCheckBox +{ + public: + + rviewCheckBox(wxPanel *parent, char *label="", int x=-1, int y=-1, int w=-1, int h=-1); +}; + +class rviewRadioButton : public wxRadioButton +{ + public: + + rviewRadioButton(wxPanel *parent, char *label="", bool value=FALSE, int x=-1, int y=-1, int w=-1, int h=-1); +}; + +class rviewScrollBar : public wxScrollBar +{ + public: + + rviewScrollBar(wxPanel *parent, int x=-1, int y=-1, int w=-1, int h=-1, long style=wxHORIZONTAL); +}; + +class rviewSlider : public wxSlider +{ + public: + + rviewSlider(wxPanel *parent, int value, int min_val, int max_val, int width, char *label="", int x=-1, int y=-1, long style=wxHORIZONTAL); +}; + + +/* + * The original slider class didn't allow me to pass on arbitrary mouse events + * to the (logical) parent which was needed in the orthosection viewer for + * automatically loading the data on a button up event. This slider class fixes + * this problem and has the same functionality as the original one. Use it + * whenever arbitrary mouse events are important. + */ + +class rviewSpecialSlider : public wxGLCanvas +{ + public: + rviewSpecialSlider(rviewFrame *logParent, wxPanel *parent, int val, int min, int max, int width=-1, const char *label=NULL); + ~rviewSpecialSlider(void); + + int GetMax(void) const; + int GetMin(void) const; + int GetValue(void) const; + void SetRange(int min, int max); + void SetValue(int val); + bool PositionInWell(float posx, float posy); + + virtual void SetLabel(const char *label); + virtual void OnPaint(void); + virtual void OnEvent(wxMouseEvent &mevt); + + + protected: + void getWellVert(int &y0, int &y1); + void getBarParams(float &posx, float &posy, float &height); + int calcNewValue(float posx, float posy, int &val, bool checky=FALSE); + void redrawCore(float x, float y, float bheight); + void getUpdateInterval(float oldx, float newx, float &clipx, float &clipw); + void updateWell(float oldx, float newx, float posy, float bheight); + wxColour background; + wxColour foreground; + wxColour wellground; + wxColour outline; + wxColour labelColour; + wxBrush bback; + wxBrush bfore; + wxBrush bwell; + wxPen outlinePen; + wxFont labelFont; + int border, barwidth, barheight; + int value, vmin, vmax; + int cwidth, cheight; + float textx, texty; + DynamicString myLabel; + rviewFrame *logicalParent; + static const int dflt_width; + static const int dflt_height; + static const int dflt_border; + static const int dflt_barwidth; + static const int dflt_barheight; +}; + + + + +/* + * Dialog box, containing a message and some buttons. + */ +class rviewDialog: public rviewFrame +{ + public: + + rviewDialog(const char *title, const char *message, int buttonNo, const char *buttons[]); + virtual ~rviewDialog(void); + void OnSize(int w, int h); + + int GetButton(void); + + // implementations of the rviewFrame virtual functions + void label(void); + int process(wxObject &obj, wxEvent &evt); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + // constants + // Width and height of the dialog box + static const int dialog_width; + static const int dialog_height; + // Borders used in the dialog box + static const int dialog_border; + // Width and height of the buttons + static const int dialog_buttonsx; + static const int dialog_buttonsy; + // Height reserved at the bottom for the buttons + static const int dialog_bheight; + // Number of text lines + static const int dialog_lines; + // Height of each of these lines + static const int dialog_lheight; + + + protected: + + wxPanel *panel; + char **buttonText; + //wxTextWindow *msg; + rviewMultiline *msg; + rviewButton **but; + int buttonNumber; + int buttonPressed; +}; + + + +/* + * An error box. + */ + +class rviewErrorbox: public rviewDialog +{ + public: + + // Default error-box: a message and an OK button. + rviewErrorbox(const char *message); + rviewErrorbox(const char *title, const char *message, int buttonNo, const char *buttons[]); + ~rviewErrorbox(void); + int activate(void); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + // creating errorboxes with formatted text + static rviewErrorbox *newErrorbox(const char *message, const char *classname=NULL, const char *funcname=NULL); + static rviewErrorbox *newErrorbox(const char *title, const char *message, int buttonNo, const char *buttons[], const char *classname=NULL, const char *funcname=NULL); + static int reportError(const char *message, const char *classname=NULL, const char *funcname=NULL); + static int reportError(const char *title, const char *message, int buttonNo, const char *buttons[], const char *classname=NULL, const char *funcname=NULL); + + private: + + static char *buildErrorMessage(const char *message, const char *classname, const char *funcname); +}; + + + +/* + * Progress reports + */ + +class rviewProgress: public rviewDialog +{ + public: + + // Progress reporter (mostly used by database) + rviewProgress(const char *message); + ~rviewProgress(void); + + int process(wxObject &obj, wxEvent &evt); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; +}; + + + +/* + * Window holding MDD results + */ + +class rviewTypeMan; + +class rviewResult: public rviewFrame +{ + public: + + rviewResult(void); + rviewResult(collection_desc *collection); + ~rviewResult(void); + void setCollection(collection_desc *collection); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + int userEvent(const user_event &ue); + + void OnSize(int w, int h); + void OnMenuCommand(int id); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + // constants + // X and Y position of result box + static const int result_x; + static const int result_y; + // Width and height of result box + static const int result_width; + static const int result_height; + // Borders used in results box + static const int result_border; + // Height of a line (collection info) + static const int result_lheight; + // Space reserved for header (collection info) + static const int result_header; + // Width of choice item + static const int result_cwidth; + // Width and height of resample group + static const int result_twidth; + static const int result_theight; + // Width and height of button + static const int result_bwidth; + static const int result_bheight; + + + protected: + + void setupVariables(void); + void openViewer(int item); + int updateSelection(void); + void configureGrey(void); + int parseResampleString(const char *resStr, double *values); + int resampleSelection(void); + char *mddDescriptorString(std::ostream &memstr, int number); + void convertSelectedItems(void); + // for printing information on time-consuming operations like scaling. + void operationPrologue(void); + void operationEpilogue(const char *opname); + + collection_desc *coll; + + char *selectedItems; // bitfield + int lastSelected; + int cbfactor; // number of units a checkbox is bigger than the size set + + wxGroupBox *group; + wxMessage *collName, *collType, *collInfo; + rviewChoice *choice; + wxPanel *panel; + wxListBox *list; + wxMenuBar *mBar; + rviewText *resampText; + rviewChoice *scaleMode; + rviewButton *resampBut; + + rviewTypeMan *typeManager; +}; + + + + +/* + * About rView window + */ +class rviewAbout: public rviewFrame +{ + public: + + rviewAbout(void); + ~rviewAbout(void); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + + void OnSize(int w, int h); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + // constants + // Width and height of About window + static const int about_width; + static const int about_height; + // Borders in About window + static const int about_border; + // Height of panel + static const int about_pheight; + // Width and height of buttons in About window + static const int about_bwidth; + static const int about_bheight; + // Height of message item + static const int about_mheight; + + + protected: + + void deleteLabels(void); + + wxPanel *panel; + rviewButton *okBut; + wxMessage **labels; + int numlines; +}; + + + +/* + * rviewStringSet window + */ +class rviewStringSet: public rviewFrame +{ + public: + + rviewStringSet(collection_desc *desc); + ~rviewStringSet(void); + + void label(void); + int process(wxObject &obj, wxEvent &evt); + void OnSize(int w, int h); + + int getNumItems(void); + void addItem(const char *string); + char *getItem(int number); + + virtual const char *getFrameName(void) const; + virtual rviewFrameType getFrameType(void) const; + + // constants + // Width and height of the string set window + static const int strset_width; + static const int strset_height; + // Borders in string set window + static const int strset_border; + // Reserve space at the bottom for the dismiss button + static const int strset_reserve; + // Width and height of the dismiss button + static const int strset_bwidth; + static const int strset_bheight; + // Height of message fields + static const int strset_mheight; + + + protected: + + wxPanel *panel; + wxListBox *list; + rviewButton *dismiss; + wxMessage *collName, *collType, *collInfo; +}; + + + + + + + +/* + * Global variables + */ + +extern labelManager *lman; +extern rviewFrameMgr *frameManager; +extern unsigned char lowerCaseTable[256]; + + +#if (defined(EARLY_TEMPLATE) && defined(__EXECUTABLE__)) +#include "rviewUtils.cpp" +#endif + +#endif diff --git a/applications/rview/wx_pixmap.cpp b/applications/rview/wx_pixmap.cpp new file mode 100644 index 0000000..cdb554c --- /dev/null +++ b/applications/rview/wx_pixmap.cpp @@ -0,0 +1,1706 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * Efficient bitmaps of colour depths 1, 2, 4, 8, 16, 24 and 32bpp + * + * COMMENTS: + * None + */ + + +#ifdef __GNUG__ +#pragma implementation +#pragma implementation "wx_pixmap.h" +#pragma implementation "wx_pixmap_dither.h" +#endif + + +#include <stdlib.h> +#include <iostream> +#include <iomanip> + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> +#include <wx/defs.h> + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOM +#include <wx/wx.h> +#include <wx/utils.h> +#include "wx_pixmap.h" +#include "wx_pixmap_translate.h" +#endif + +#ifdef wx_msw +#include <malloc.h> +#endif + +#ifdef wx_x +#include <X11/Xlib.h> +#endif + + + + + + +/* + * Globals + */ + +unsigned char *TrueTransTab = NULL; +int TrueTransCount = 0; +unsigned char *FastDitherTab = NULL; +int FastDitherCount = 0; + +#ifdef wx_x +typedef struct wxVisualType { + int depth, vclass; +} wxVisualType; + +static wxVisualType wxVisualTypes[] = { + {32, TrueColor}, + {24, TrueColor}, + {16, TrueColor}, + {15, TrueColor}, + {8, PseudoColor}, + {8, GrayScale}, + {-1, 0} + }; +#endif + + +/* Order of RGB components in true-colour modes */ +#define RGB_ORDER_NONE 0 +#define RGB_ORDER_RGB 1 +#define RGB_ORDER_BGR 2 + + + + + +/* + * Determine the colour component with the biggest weight for sorting and + * colour-lookup. + */ + +#if (COLOUR_WEIGHT_RED > COLOUR_WEIGHT_GREEN) +#error here +#define AUX1_COMPONENT green +#define COLOUR_WEIGHT_AUX1 COLOUR_WEIGHT_GREEN +#if (COLOUR_WEIGHT_RED > COLOUR_WEIGHT_BLUE) +#define SORT_COMPONENT red +#define AUX2_COMPONENT blue +#define COLOUR_WEIGHT_MAX COLOUR_WEIGHT_RED +#define COLOUR_WEIGHT_AUX1 COLOUR_WEIGHT_BLUE +#else +#define SORT_COMPONENT blue +#define AUX2_COMPONENT red +#define COLOUR_WEIGHT_MAX COLOUR_WEIGHT_BLUE +#define COLOUR_WEIGHT_AUX2 COLOUR_WEIGHT_RED +#endif +#else +#define AUX1_COMPONENT red +#define COLOUR_WEIGHT_AUX1 COLOUR_WEIGHT_RED +#if (COLOUR_WEIGHT_GREEN > COLOUR_WEIGHT_BLUE) +#define SORT_COMPONENT green +#define AUX2_COMPONENT blue +#define COLOUR_WEIGHT_MAX COLOUR_WEIGHT_GREEN +#define COLOUR_WEIGHT_AUX2 COLOUR_WEIGHT_BLUE +#else +#define SORT_COMPONENT blue +#define AUX2_COMPONENT green +#define COLOUR_WEIGHT_MAX COLOUR_WEIGHT_BLUE +#define COLOUR_WEIGHT_AUX2 COLOUR_WEIGHT_GREEN +#endif +#endif + +#define SELECT_COMPONENT(x,c) x.c + + + +// General, platform independent members +int wxPixmap::getWidth(void) {return(width);} + +int wxPixmap::getHeight(void) {return(height);} + +int wxPixmap::getDepth(void) {return(depth);} + +int wxPixmap::getPitch(void) {return(pitch);} + +char *wxPixmap::getData(void) {return(data);} + +wxColour *wxPixmap::getPalette(void) {return(palette);} + +// Returns the depth of the current mode +int wxPixmap::getModeDepth(void) {return(displayDepth);} + +// Returns the pitch of the current mode +int wxPixmap::getModePitch(void) {return(displayPitch);} + +// Returns a pointer to the mode-translated data or NULL if none. +char *wxPixmap::getModeData(void) {return(modeData);} + +// Returns a pointer to the translation table or NULL if none exists. +// The format depends on the display depth: <= 8 ==> 8bit, else 32bit +unsigned char *wxPixmap::getTranslationTable(void) +{ + if ((depth > 12) && (displayDepth <= 8)) return TrueTransTab; + return TransTab.c; +} + + +// Error reporting / handling +void wxPixmap::errorMemory(void) +{ +#if (DEBUG > 0) + *errorstr << "Can't claim memory!" << endl; +#endif +} + +void wxPixmap::errorGeneric(char *message) +{ +#if (DEBUG > 0) + *errorstr << message << endl; +#endif +} + + +// Display can't change on X, but it can on other systems. +// Returns depth of new mode. Internal variables holding mode information +// will be overwritten after calling this function, so make sure you copy +// them first if you need them. +void wxPixmap::getDisplayAttributes(void) +{ +#ifdef wx_x + int i; + +#if 0 + for (i=0; wxVisualTypes[i].depth >= 0; i++) + { + if (XMatchVisualInfo(display, screen, wxVisualTypes[i].depth, wxVisualTypes[i].vclass, &visualInfo)) break; + } +#else + displayDepth = wxDisplayDepth(); + for (i=0; wxVisualTypes[i].depth >= 0; i++) + { + if (wxVisualTypes[i].depth == displayDepth) + { + if (XMatchVisualInfo(display, screen, wxVisualTypes[i].depth, wxVisualTypes[i].vclass, &visualInfo)) break; + } + } +#endif + if (wxVisualTypes[i].depth < 0) + { + errorGeneric("Can't get appropriate X visual"); + displayDepth = -1; + } + + // On X we have to find the bpp manually + int j; + XPixmapFormatValues *pixvals = XListPixmapFormats(display, &i); + for (j=0; j<i; j++) + { + if (pixvals[j].depth == visualInfo.depth) + { + // Any match: set displayDepth + displayDepth = pixvals[j].bits_per_pixel; + // Exact match: stop. + if (pixvals[j].bits_per_pixel == pixvals[j].depth) break; + } + } + // Don't forget to free this structure! + XFree(pixvals); + + // cout << "Use depth " << displayDepth << endl; + + rgbOrder = RGB_ORDER_RGB; + if (displayDepth > 8) + { + if ((visualInfo.red_mask < visualInfo.green_mask) && (visualInfo.green_mask < visualInfo.blue_mask)) + rgbOrder = RGB_ORDER_RGB; + else if ((visualInfo.red_mask > visualInfo.green_mask) && (visualInfo.green_mask > visualInfo.blue_mask)) + rgbOrder = RGB_ORDER_BGR; +#if (DEBUG > 0) + else + *errorstr << "Unrecognized pixel format, assuming r,g,b" << endl; +#endif + } + + if (BitmapBitOrder(display) == LSBFirst) destBitorder = 0; else destBitorder = 1; + if (ImageByteOrder(display) == LSBFirst) destByteorder = 0; else destByteorder = 1; + //cout << "destBit " << destBitorder << ", destByte " << destByteorder << endl; +#endif + +#ifdef wx_msw + int bpp, planes, pfi; + PIXELFORMATDESCRIPTOR pfd; + + desktop = GetDesktopWindow(); + rootDC = GetDC(desktop); + bpp = GetDeviceCaps(rootDC, BITSPIXEL); + planes = GetDeviceCaps(rootDC, PLANES); + displayDepth = bpp * planes; + pfi = 1;//GetPixelFormat(rootDC); + if (pfi != 0) + { + if (DescribePixelFormat(rootDC, pfi, sizeof(PIXELFORMATDESCRIPTOR), &pfd) != 0) + { + displayDepth = (int)pfd.cRedBits + (int)pfd.cGreenBits + (int)pfd.cBlueBits; + rgbOrder = RGB_ORDER_BGR; + if (displayDepth > 8) + { + if ((pfd.cRedShift < pfd.cGreenShift) && (pfd.cGreenShift < pfd.cBlueShift)) + rgbOrder = RGB_ORDER_RGB; + else if ((pfd.cRedShift > pfd.cGreenShift) && (pfd.cGreenShift > pfd.cBlueShift)) + rgbOrder = RGB_ORDER_BGR; +#if (DEBUG > 0) + else + *errorstr << "Unrecognized pixel format, assuming b,g,r" << endl; +#endif + } +#if (DEBUG > 0) + else + *errorstr << "Error describing pixel format (" << GetLastError() << ")" << endl; +#endif + } + } +#if (DEBUG > 0) + else + *errorstr << "Error reading pixel format (" << GetLastError() << ")" << endl; +#endif + + // On NT use the machine order + destBitorder = WX_PIXMAP_SRC_BITORDER; destByteorder = WX_PIXMAP_SRC_BYTEORDER; +#endif + + // Filter out unsupported display depths + switch (displayDepth) + { + case 1: + case 2: + case 4: + case 8: + case 15: + case 16: + case 24: + case 32: break; + default: +#if (DEBUG > 0) + *errorstr << "Unsupported display depth " << displayDepth << endl; +#endif + displayDepth = -1; return; + } + +#if (DEBUG > 0) + *errorstr << "Display depth = " << displayDepth + << ", rgb format " << ((rgbOrder == RGB_ORDER_RGB) ? "r,g,b" : "b,g,r") << endl; +#endif +} + + +void wxPixmap::initColoursForMode(bool forceUpdate) +{ +#ifdef wx_pixmap_alloc_cols + // Clear colour allocation bitfield + memset(ColourAlloc, 0, 32); +#else + processParentPalette(forceUpdate); +#endif + setupTranslators(); +} + + +// Stuff shared by all constructors. +void wxPixmap::initVariables(void) +{ + data = NULL; modeData = NULL; palette = NULL; TransTab.c = NULL; validDisplay = FALSE; busyCursorMode = TRUE; + parentWin = NULL; errorstr = NULL; +#ifndef wx_pixmap_alloc_cols + parentPalette = NULL; pixmapPalette = NULL; parentPaletteDepth = 0; +#endif + width=0; height=0; depth=0; pad=0; + +#if (DEBUG > 0) +#ifdef wx_x + // For some reason the other variant crashes on Sun + errorstr = (ostream*)&cerr; +#else +#ifdef wx_msw + errorstr = new ofstream("debug.log",ios::out | ios::trunc); +#else + errorstr = new ostream(&streamBuf); +#endif +#endif +#endif + +#ifdef wx_x + xim = NULL; + display = wxGetDisplay(); + myGC = (GC)0; + + screen = XDefaultScreen(display); + rootwin = XRootWindow(display, screen); + + //Screen *myScreen = DefaultScreenOfDisplay(display); + //cout << "root depth " << myScreen->root_depth << endl; + +#endif + +#ifdef wx_msw + bitmap = (HBITMAP)0; + windowHandle = (HWND)0; + rootDC = (HDC)0; srcDC = (HDC)0; winDC = (HDC)0; oldDCObject = (HGDIOBJ)0; +#endif + + getDisplayAttributes(); +} + + +// New Depth and Flags are passed to this function to allow optimising colour handling. +void wxPixmap::freeResources(int Depth, unsigned int Flags) +{ +#ifdef wx_msw + if (bitmap != (HBITMAP)0) + { + DeleteObject(bitmap); bitmap = (HBITMAP)0; + } + if (modeData != data) + { + free(modeData); modeData = NULL; + } + // For symmetry to X + if (data != NULL) + { + free(data); data = NULL; + } + if (srcDC != (HDC)0) + { + SelectObject(srcDC, oldDCObject); DeleteObject(srcDC); srcDC = (HDC)0; + } +#endif + +#ifdef wx_x + if ((modeData != NULL) && (modeData != data)) + { + // In this case the XImage structure was initialised with modeData + // rather than data. Therefore on deleting xim modeData will be + // deleted automatically. Use malloc/free combination rather than + // new/delete. + free(data); data = NULL; + } + + // Free image if any. This also destroys the data it was initialised with + // (see above). + if (xim != NULL) + { + XDestroyImage(xim); xim = NULL; + } + + // The graphical context can change every time newPixmap is called, therefore + // it must be deleted here and not in the destructor. + if (myGC != (GC)0) + { + XFreeGC(display, myGC); myGC = (GC)0; + } + + // If the new palette is the same as the old one we leave the colours as they are. + // Note that calling with no colour translation but with samepalette doesn't + // free anthing - that's because the Translation table has to be present even + // if no _automatic_ translation takes place. +#ifdef wx_pixmap_alloc_cols + if ((Flags & WX_PIXFLAG_SAMEPALETTE) == 0) + { + // Free colours if any were allocated (i.e. displayDepth <= 8) + if ((displayDepth <= 8) && (TransTab.c != NULL)) + { + unsigned long pixels[256]; + int i, j; + + for (i=0, j=0; i<(1<<displayDepth); i++) + { + if ((ColourAlloc[i>>3] & (1 << (i & 7))) != 0) + { + pixels[j++] = i; + } + } + XFreeColors(display, xat.colormap, pixels, j, 0); + // *errorstr << j << " colours freed." << endl; + } + } +#endif +#endif // #ifdef wx_x + + // This is for all machines again + if ((Flags & WX_PIXFLAG_SAMEPALETTE) == 0) + { + // In case both depth > 8 (true-colour) and old depth > 8 we don't need to make a new + // colourtable, hence we don't have to delete the old one. + if ((depth <= 12) && (Depth <= 12) && (TransTab.c != NULL)) + { + delete [] TransTab.c; TransTab.c = NULL; + } + } + + // Update TrueColour Translation table + if ((depth > 12) && (displayDepth <= 8)) + { + if (depth != Depth) + { + TrueTransCount--; +#ifdef WX_PIXMAP_GLOBAL_VOLATILE + if (TrueTransCount == 0) + { + delete [] TrueTransTab; TrueTransTab = NULL; + } +#endif + } + } + // Fast dithering table? + if ((pixFlags & WX_PIXFLAG_FASTDITHER) != 0) + { + FastDitherCount--; +#ifdef WX_PIXMAP_GLOBAL_VOLATILE + if (FastDitherCount == 0) + { + delete [] FastDitherTab; FastDitherTab = NULL; + } +#endif + } + if (palette != NULL) + { + delete [] palette; palette = NULL; + } +} + + +#ifndef wx_pixmap_alloc_cols + +void wxPixmap::processParentPalette(bool forceUpdate) +{ + if (displayDepth > 8) + { + if (parentPalette != NULL) + { + delete [] parentPalette; parentPalette=NULL; parentPaletteDepth = 0; + } + } + // For non-truecolour modes read the screen palette and install fast lookup functions + // Essentially forceUpdate should only be set to true if only the palette, not the + // more depth has changed. + else if ((parentPaletteDepth != displayDepth) || forceUpdate) + { + unsigned int i, ncols; + ncols = (1<<displayDepth); + + // just in case... + if (parentPalette != NULL) {delete [] parentPalette; parentPalette = NULL; parentPaletteDepth = 0;} + +#ifdef wx_x + XColor displayColours[256]; + + // *errorstr << "Colours = " << ncols << endl; + for (i=0; i<ncols; i++) {displayColours[i].pixel = (unsigned long)i;} + XQueryColors(display, xat.colormap, displayColours, ncols); + if ((parentPalette = new wx_permute_cmap[ncols]) == NULL) + { + errorMemory(); return; + } + for (i=0; i<ncols; i++) + { + register wx_permute_cmap *pc = &(parentPalette[i]); + register XColor *xc = &(displayColours[i]); + + pc->red = ((xc->red)>>8); pc->green = ((xc->green)>>8); pc->blue = ((xc->blue)>>8); + pc->number = i; + } +#endif + +#ifdef wx_msw + PALETTEENTRY palEntries[256]; + + GetSystemPaletteEntries(rootDC, 0, ncols, palEntries); + + if ((parentPalette = new wx_permute_cmap[ncols]) == NULL) + { + errorMemory(); return; + } + for (i=0; i<ncols; i++) + { + register wx_permute_cmap *pc = &(parentPalette[i]); + register PALETTEENTRY *pe = &(palEntries[i]); + + pc->red = pe->peRed; pc->green = pe->peGreen; pc->blue = pe->peBlue; + pc->number = i; + } +#endif + + sortParentPalette(0, ncols-1); + + buildInverseTable(); + + parentPaletteDepth = displayDepth; + } +} + + +// Returns a pointer to the parent palette. For use with findBestColour. +wx_permute_cmap *wxPixmap::getParentPalette(void) +{ + return parentPalette; +} + + + +#define PPAL_SWAP(a,b,h) h=parentPalette[a]; parentPalette[a]=parentPalette[b]; parentPalette[b]=h; + +// Quicksorter of parent palette, using the component with the biggest loading value +void wxPixmap::sortParentPalette(int from, int to) +{ + while (from < to) + { + int mid, i, j; + wx_permute_cmap x, y; + + mid = (from + to)/2; j = from; + PPAL_SWAP(mid, from, x); // Side-effect: element at first index (before swap) is in x. + for (i=from+1; i<=to; i++) + { + if (SELECT_COMPONENT(parentPalette[i],SORT_COMPONENT) < SELECT_COMPONENT(x,SORT_COMPONENT)) + { + j++; + PPAL_SWAP(i,j,y); + } + } + PPAL_SWAP(from,j,y); + + // Decide which branch to recurse into to minimise Stack consumption + if ((j - from) < (to - j)) + { + sortParentPalette(from, j-1); + from = j+1; + } + else + { + sortParentPalette(j+1, to); + to = j-1; + } + } +} + + +// Builds an inverse lookup-table for the parent palette (after it was sorted). +// inverseTable[x] specifies the index in parentPalette from where to search for +// the colour with SORT_COMPONENT = x +void wxPixmap::buildInverseTable(void) +{ + unsigned int i, j, idx, last, value; + + i=0; last=0; + while (i < (unsigned int)(1<<displayDepth)) + { + idx = i; value = SELECT_COMPONENT(parentPalette[idx],SORT_COMPONENT); + while (++i < (unsigned int)(1<<displayDepth)) + { + if (SELECT_COMPONENT(parentPalette[i],SORT_COMPONENT) != value) break; + } + // *errorstr << '(' << idx << ',' << i << "):" << last << ',' << value << " "; + for (j=last; j<=value; j++) + { + parentInverse[j] = (i+idx-1)>>1; + } + last = value+1; + } + // fill remaining entries + while (last < 256) {parentInverse[last] = (1<<displayDepth)-1; last++;} + + /*for (i=0; i<256; i++) + { + *errorstr << '[' << i << ':' << (int)parentInverse[i] << "] "; + } + *errorstr << endl;*/ +} + + +#define square_val(x) ((x)*(x)) + +// Returns the index into parentPalette which addresses the best fit for the RGB colour +// passed to the function. Don't use for (display) modes with more than 8bpp. +// This requires parentPalette and parentInverse to be set up correctly. This function +// may only be used after parentPalette has been sorted and the inverse table has been +// built. +int wxPixmap::findBestColour(unsigned char red, unsigned char green, unsigned char blue) +{ + COLORREF bestMatch = 0x7fffffff, match; + int up, down, bestIdx=-1; + + down = parentInverse[SORT_COMPONENT]; up = down+1; + while ((down >= 0) || (up < (1<<displayDepth))) + { + // Twice basically the same for up and down, but speed is _very_ critical here + if (down >= 0) + { + match = COLOUR_WEIGHT_MAX * square_val((int)SORT_COMPONENT - (int)SELECT_COMPONENT(parentPalette[down], SORT_COMPONENT)); + if (match > bestMatch) down=-1; + else + { + match += COLOUR_WEIGHT_AUX1 * square_val((int)AUX1_COMPONENT - (int)SELECT_COMPONENT(parentPalette[down], AUX1_COMPONENT)) + COLOUR_WEIGHT_AUX2 * square_val((int)AUX2_COMPONENT - (int)SELECT_COMPONENT(parentPalette[down], AUX2_COMPONENT)); + if (match < bestMatch) {bestMatch = match; bestIdx = down;} + down--; + } + } + if (up < (1<<displayDepth)) + { + match = COLOUR_WEIGHT_MAX * square_val((int)SORT_COMPONENT - (int)SELECT_COMPONENT(parentPalette[up], SORT_COMPONENT)); + if (match > bestMatch) up=(1<<displayDepth); + else + { + match += COLOUR_WEIGHT_AUX1 * square_val((int)AUX1_COMPONENT - (int)SELECT_COMPONENT(parentPalette[up], AUX1_COMPONENT)) + COLOUR_WEIGHT_AUX2 * square_val((int)AUX2_COMPONENT - (int)SELECT_COMPONENT(parentPalette[up], AUX2_COMPONENT)); + if (match < bestMatch) {bestMatch = match; bestIdx = up;} + up++; + } + } + } + // *errorstr << "Best match for " << (int)red << ',' << (int)green << ',' << (int)blue << ": " << bestIdx << " (" << (int)parentPalette[bestIdx].red << ',' << (int)parentPalette[bestIdx].green << ',' << (int)parentPalette[bestIdx].blue << ')' << endl; + + return bestIdx; +} + + +int wxPixmap::findFastColour(unsigned char red, unsigned char green, unsigned char blue) +{ + return (int)(FastDitherTab[(red>>3) | ((green & 0xf8)<<2) | ((blue&0xf8)<<7)]); +} + + +// Converts the pixmap's palette into a wx_permute_cmap which is needed by the ditherer. +// Only call if depth <= 8! Other modes are assumed true-colour. +void wxPixmap::processPixmapPalette(void) +{ + int i; + int tabsize; + + if (depth <= 8) tabsize = (1<<depth); else tabsize = (1<<12); + + if (pixmapPalette != NULL) {delete [] pixmapPalette; pixmapPalette=NULL;} + + if ((pixmapPalette = new wx_permute_cmap[tabsize]) == NULL) + { + errorMemory(); return; + } + // Greyscale? + if (palette == NULL) + { + int step, grey; + + step = 0xff00/(tabsize-1); grey = 0; + for (i=0; i<tabsize; i++) + { + pixmapPalette[i].red = pixmapPalette[i].green = pixmapPalette[i].blue = (grey >> 8); + grey += step; + } + } + else + { + unsigned char red, green, blue; + + for (i=0; i<tabsize; i++) + { + // palette[i].Get(&red, &green, &blue); -- PB 2006-jan-01 + red = palette[i].Red(); + green = palette[i].Green(); + blue = palette[i].Blue(); + pixmapPalette[i].red = red; pixmapPalette[i].green = green; pixmapPalette[i].blue = blue; + } + } +} + + +#endif // wx_pixmap_alloc_cols + + + +// Builds the translation table for the current bitmap + palette / display setup. +void wxPixmap::buildTranslationTable(void) +{ +#ifndef wx_pixmap_alloc_cols + int bestIdx; +#else +#ifdef wx_x + char cstring[16]; // buffer for holding colour strings + XColor xcolour; + unsigned long pixel; +#endif +#endif + + int i; + unsigned char red, green, blue; + bool BuildTrueTable = FALSE; + + // *errorstr << "Build translation table" << endl; + + // Essentially this function shouldn't be called with a valid translation table + // but to avoid memory leaks let's free an old table anyway. + if (TransTab.c != NULL) {delete [] TransTab.c; TransTab.c = NULL;} + + TransTabSize = -1; + if (depth <= 8) TransTabSize = (1<<depth); + else if (depth == 12) TransTabSize = (1<<8); + else if (displayDepth <= 8) TransTabSize = (1<<15); + + if (TransTabSize > 0) + { + if (TransTabSize > 256) // For a true colour mode? + { + if (TrueTransTab == NULL) + { + if ((TrueTransTab = new unsigned char[(1<<15)]) == NULL) + { + errorMemory(); return; + } + BuildTrueTable = TRUE; + } + TrueTransCount++; + } + if (TransTabSize <= 256); + { + // Base type of translation table depends on display depth + if (displayDepth <= 8) TransTab.c = new unsigned char[TransTabSize]; + else TransTab.l = new COLORREF[TransTabSize]; + if (TransTab.c == NULL) + { + errorMemory(); return; + } + } + // *errorstr << "Allocated Translation table of size " << TransTabSize << endl; + } + + if (((pixFlags & WX_PIXFLAG_FASTDITHER) != 0) && (displayDepth <= 8)) + { + if (FastDitherTab == NULL) + { + if ((FastDitherTab = new unsigned char[(1<<15)]) == NULL) + { + errorMemory(); return; + } + if (busyCursorMode) ::wxBeginBusyCursor(); + for (i=0; i<0x8000; i++) + { + PALETTE_SHORT_TO_RGB(i, red, green, blue); + FastDitherTab[i] = findBestColour(red, green, blue); + } + if (busyCursorMode) ::wxEndBusyCursor(); + } + FastDitherCount++; + } + + // Palette indices --> pixel indices + if ((depth <= 12) && (displayDepth <= 8)) + { + int tabsize; + + if (depth <= 8) tabsize = (1<<depth); else tabsize = (1<<8); + for (i=0; i<tabsize; i++) + { + if (palette != NULL) + { + red = palette[i].Red(); + green = palette[i].Green(); + blue = palette[i].Blue(); + } + else + { + // Assume greyscale + red = green = blue = i; + } +#ifdef wx_pixmap_alloc_cols +#ifdef wx_x + sprintf(cstring, "rgb:%x/%x/%x", red, green, blue); + XParseColor(display, xat.colormap, cstring, &xcolour); + if (XAllocColor(display, xat.colormap, &xcolour)) + { + // ColourAlloc holds the display-, not the source colour numbers. + // Allocate each XColour only once! Haven't found an X-call that just + // returns the pixel value so I have to allocate and free it in case + // it was already allocated. + if ((ColourAlloc[(xcolour.pixel)>>3] & (1 << ((xcolour.pixel) & 7))) == 0) + { + ColourAlloc[(xcolour.pixel)>>3] |= (1 << ((xcolour.pixel) & 7)); + } + else + { + pixel = xcolour.pixel; + XFreeColors(display, xat.colormap, &pixel, 1, 0); + } + } + TransTab.c[i] = (unsigned char)xcolour.pixel; + // *errorstr << i << " (" << cstring << "): " << xcolour.pixel << endl; +#endif +#ifdef wx_msw + TransTab.c[i] = (unsigned char)(GetNearestPaletteIndex(rootDC, red | (green<<8) | (blue<<16))); +#endif +#else + bestIdx = findBestColour(red, green, blue); + TransTab.c[i] = parentPalette[bestIdx].number; +#endif + } + } + if ((depth > 8) && (displayDepth <= 8)) + { + if (BuildTrueTable) + { + if (busyCursorMode) ::wxBeginBusyCursor(); + // This is a bit heavy... + for (i=0; i<0x8000; i++) + { + // The internal format is always the same + PALETTE_SHORT_TO_RGB(i,red,green,blue); +#ifdef wx_pixmap_alloc_cols +#ifdef wx_x + sprintf(cstring, "rgb:%x/%x/%x", red, green, blue); + XParseColor(display, xat.colormap, cstring, &xcolour); + if (XAllocColor(display, xat.colormap, &xcolour)) + { + if ((ColourAlloc[(xcolour.pixel)>>3] & (1 << ((xcolour.pixel) & 7))) == 0) + { + ColourAlloc[(xcolour.pixel)>>3] |= (1 << ((xcolour.pixel) & 7)); + } + else + { + pixel = xcolour.pixel; + XFreeColors(display, xat.colormap, &pixel, 1, 0); + } + } + TrueTransTab[i] = (unsigned char)xcolour.pixel; +#endif +#ifdef wx_msw + TrueTransTab[i] = (unsigned char)(GetNearestPaletteIndex(rootDC, red | (green<<8) | (blue<<16))); +#endif +#else + bestIdx = (FastDitherTab == NULL) ? findBestColour(red, green, blue) : FastDitherTab[i]; + TrueTransTab[i] = parentPalette[bestIdx].number; +#endif + } + if (busyCursorMode) ::wxEndBusyCursor(); + } + } + + // Special translation tables which are platform independent (true-colour displays): + if ((depth <= 12) && (displayDepth > 8)) + { + int tabsize; + + if (depth <= 8) tabsize = (1<<depth); else tabsize = (1<<8); + + // Data with <= 8bpp and no palette interpreted as greyscales. + if (palette == NULL) + { + int step, grey, value = 0; + + step = 0xff00 / (tabsize -1); // stepping value in .8 fixpoint format + if (displayDepth == 15) // 15bpp + { + for (i=0; i<tabsize; i++, value += step) + { + grey = ((value + 0x400) >> 8); // convert fixpoint to integer, rounding. + if (grey > 255) grey = 255; + + if (destByteorder == WX_PIXMAP_SRC_BYTEORDER) + TransTab.l[i] = _RGBL_TO_PALETTE_SHORT15(grey, grey, grey); + else + TransTab.l[i] = _RGBL_TO_PALETTE_SHORT15i(grey, grey, grey); + } + } + else if (displayDepth == 16) // 16bpp + { + for (i=0; i<tabsize; i++, value += step) + { + grey = ((value + 0x400) >> 8); + if (grey > 255) grey = 255; + + if (destByteorder == WX_PIXMAP_SRC_BYTEORDER) + TransTab.l[i] = _RGBL_TO_PALETTE_SHORT16(grey, grey, grey); + else + TransTab.l[i] = _RGBL_TO_PALETTE_SHORT16i(grey, grey, grey); + } + } + else // 24/32bpp + { + for (i=0; i<tabsize; i++, value += step) + { + grey = (value + 0x80) >> 8; + + if (destByteorder == WX_PIXMAP_SRC_BYTEORDER) + TransTab.l[i] = _RGB_TO_PALETTE_LONG(grey, grey, grey); + else + TransTab.l[i] = _RGB_TO_PALETTE_LONGi(grey, grey, grey); + } + } + } + else // palette != NULL ==> determine the true-colour values + { + if (displayDepth == 15) + { + for (i=0; i<tabsize; i++) + { + red = palette[i].Red(); + green = palette[i].Green(); + blue = palette[i].Blue(); + + if (destByteorder == WX_PIXMAP_SRC_BYTEORDER) + TransTab.l[i] = _RGBL_TO_PALETTE_SHORT15(red, green, blue); + else + TransTab.l[i] = _RGBL_TO_PALETTE_SHORT15i(red, green, blue); + } + } + else if (displayDepth == 16) + { + for (i=0; i<tabsize; i++) + { + red = palette[i].Red(); + green = palette[i].Green(); + blue = palette[i].Blue(); + + if (destByteorder == WX_PIXMAP_SRC_BYTEORDER) + TransTab.l[i] = _RGBL_TO_PALETTE_SHORT16(red, green, blue); + else + TransTab.l[i] = _RGBL_TO_PALETTE_SHORT16i(red, green, blue); + } + } + else + { + for (i=0; i<tabsize; i++) + { + red = palette[i].Red(); + green = palette[i].Green(); + blue = palette[i].Blue(); + + if (destByteorder == WX_PIXMAP_SRC_BYTEORDER) + TransTab.l[i] = _RGB_TO_PALETTE_LONG(red, green, blue); + else + TransTab.l[i] = _RGB_TO_PALETTE_LONGi(red, green, blue); + } + } + } + } + +#if (DEBUG > 0) + *errorstr << "Translation table (" << TransTabSize << "), table entry size "; + if (displayDepth <= 8) *errorstr << (int)8; else *errorstr << (int)32; + *errorstr << " bits." << endl; + + *errorstr << hex; + if (displayDepth <= 8) + { + if (depth > 12) + { + for (i=0; i<TransTabSize; i++) + { + *errorstr << '[' << setw(2) << int(TrueTransTab[i]) << "] "; + if ((i & 7) == 7) {*errorstr << endl;} + } + } + else + { + for (i=0; i<TransTabSize; i++) + { + *errorstr << '[' << setw(2) << int(TransTab.c[i]) << "] "; + if ((i & 7) == 7) {*errorstr << endl;} + } + } + } + else + { + for (i=0; i<TransTabSize; i++) + { + *errorstr << '[' << setw(8) << TransTab.l[i] << "] "; + if ((i & 7) == 7) {*errorstr << endl;} + } + } + *errorstr << dec << endl; +#endif +} + + +// To avoid repetitions... +#define _TRANSLATORS_FALSE(target) \ + pixtrans1 = &wx_pixmap_translate_1_to_##target; pixdither1 = &wxPixmap::dither_1_to_##target; \ + pixtrans2 = &wx_pixmap_translate_2_to_##target; pixdither2 = &wxPixmap::dither_2_to_##target; \ + pixtrans4 = &wx_pixmap_translate_4_to_##target; pixdither4 = &wxPixmap::dither_4_to_##target; \ + pixtrans8 = &wx_pixmap_translate_8_to_##target; pixdither8 = &wxPixmap::dither_8_to_##target; \ + pixtrans12 = &wx_pixmap_translate_12_to_##target; pixdither12 = &wxPixmap::dither_12_to_##target; \ + pixtrans15 = &wx_pixmap_translate_15_to_##target; pixdither15 = &wxPixmap::dither_15_to_##target; \ + pixtrans24 = &wx_pixmap_translate_24_to_##target; pixdither24 = &wxPixmap::dither_24_to_##target; \ + pixtrans32 = &wx_pixmap_translate_32_to_##target; pixdither32 = &wxPixmap::dither_32_to_##target; + +#define _TRANSLATORS_TRUE(target,target2) \ + pixtrans1 = wx_pixmap_translate_1_to_##target; \ + pixtrans2 = wx_pixmap_translate_2_to_##target; \ + pixtrans4 = wx_pixmap_translate_4_to_##target; \ + pixtrans8 = wx_pixmap_translate_8_to_##target; \ + pixtrans12 = wx_pixmap_translate_12_to_##target; \ + pixtrans15 = wx_pixmap_translate_15_to_##target2; \ + pixtrans24 = wx_pixmap_translate_24_to_##target2; \ + pixtrans32 = wx_pixmap_translate_32_to_##target2; + +void wxPixmap::setupTranslators(void) +{ + // Init defaults... + pixtrans1 = NULL; pixtrans2 = NULL; pixtrans4 = NULL; pixtrans8 = NULL; pixtrans12 = NULL; + pixtrans15 = NULL; pixtrans24 = NULL; pixtrans32 = NULL; + pixdither1 = NULL; pixdither2 = NULL; pixdither4 = NULL; pixdither8 = NULL; pixdither12 = NULL; + pixdither15 = NULL; pixdither24 = NULL; pixdither32 = NULL; + +#ifdef wx_x + if ((destBitorder == WX_PIXMAP_SRC_BITORDER) && (destByteorder == WX_PIXMAP_SRC_BYTEORDER)) +#endif + { + switch (displayDepth) + { + case 1: + _TRANSLATORS_FALSE(1); + break; + case 2: + _TRANSLATORS_FALSE(2); + break; + case 4: + _TRANSLATORS_FALSE(4); + break; + case 8: + _TRANSLATORS_FALSE(8); + break; + case 15: + // For depths 1 to 12 the translators are identical. + if (rgbOrder == RGB_ORDER_RGB) + { + _TRANSLATORS_TRUE(16rgb, 15rgb); + } + else + { + _TRANSLATORS_TRUE(16bgr, 15bgr); + } + break; + case 16: + if (rgbOrder == RGB_ORDER_RGB) + { + _TRANSLATORS_TRUE(16rgb, 16rgb); + } + else + { + _TRANSLATORS_TRUE(16bgr, 16bgr); + } + break; + case 24: + if (rgbOrder == RGB_ORDER_RGB) + { + _TRANSLATORS_TRUE(24rgb, 24rgb); + } + else + { + _TRANSLATORS_TRUE(24bgr, 24bgr); + } + break; + case 32: + if (rgbOrder == RGB_ORDER_RGB) + { + _TRANSLATORS_TRUE(32rgb, 32rgb); + } + else + { + _TRANSLATORS_TRUE(32bgr, 32bgr); + } + break; + default: + break; + } + } +#ifdef wx_x + else + { + switch (displayDepth) + { + case 1: + _TRANSLATORS_FALSE(1i); + break; + case 2: + _TRANSLATORS_FALSE(2i); + break; + case 4: + _TRANSLATORS_FALSE(4i); + break; + case 8: + _TRANSLATORS_FALSE(8i); + break; + case 15: + // For depths 1 to 12 the translators are identical. + if (rgbOrder == RGB_ORDER_RGB) + { + _TRANSLATORS_TRUE(16irgb, 15irgb); + } + else + { + _TRANSLATORS_TRUE(16ibgr, 15ibgr); + } + break; + case 16: + if (rgbOrder == RGB_ORDER_RGB) + { + _TRANSLATORS_TRUE(16irgb, 16irgb); + } + else + { + _TRANSLATORS_TRUE(16ibgr, 16ibgr); + } + break; + case 24: + if (rgbOrder == RGB_ORDER_RGB) + { + _TRANSLATORS_TRUE(24irgb, 24irgb); + } + else + { + _TRANSLATORS_TRUE(24ibgr, 24ibgr); + } + break; + case 32: + if (rgbOrder == RGB_ORDER_RGB) + { + _TRANSLATORS_TRUE(32irgb, 32irgb); + } + else + { + _TRANSLATORS_TRUE(32ibgr, 32ibgr); + } + break; + default: + break; + } + } +#endif +} + + + +// Must be called when the current mode changes. If the depth of new and old +// screen mode is identical Flags determines whether a new Translation table +// should be built (currently only this one bit used). This obviously never +// gets called on X. +void wxPixmap::modeChange(unsigned int modeFlags) +{ + int lastDisplayDepth = displayDepth; + + getDisplayAttributes(); + + // depth hasn't changed or automatic translation not necessary ==> just build + // the tables. + if (((displayDepth == lastDisplayDepth) && ((modeFlags & WX_PIXMODE_PALETTE) != 0)) || + ((pixFlags & WX_PIXFLAG_TRANSLATE) == 0)) + { + initColoursForMode(TRUE); + buildTranslationTable(); + } + else + { + // otherwise we need to set up completely new buffers and stuff. + if (TrueTransTab != NULL) + { + delete [] TrueTransTab; TrueTransTab = NULL; TrueTransCount = 0; + } + if (FastDitherTab != NULL) + { + delete [] FastDitherTab; FastDitherTab = NULL; FastDitherCount = 0; + } + newPixmap(parentWin, width, height, depth, pad, data, pixFlags | WX_PIXFLAG_SAMEPALETTE, palette); + } +} + + +wxPixmap::wxPixmap(void) +{ + initVariables(); +} + + +wxPixmap::wxPixmap(wxWindow *Win, int Width, int Height, int Depth, int Pad, char *Data, unsigned int Flags, wxColour *Palette) +{ + initVariables(); + newPixmap(Win, Width, Height, Depth, Pad, Data, Flags, Palette); +} + + +wxPixmap::~wxPixmap(void) +{ + if (errorstr != NULL) + { +#ifndef wx_x +#ifdef wx_msw + errorstr->flush(); +#endif + delete errorstr; +#endif + } + + // Use illegal value for depth to make sure everything is freed + freeResources(0, 0); + +#ifndef wx_pixmap_alloc_cols + if (parentPalette != NULL) {delete [] parentPalette; parentPalette = NULL;} + if (pixmapPalette != NULL) {delete [] pixmapPalette; pixmapPalette = NULL;} +#endif +} + + +int wxPixmap::newPixmap(wxWindow *Win, int Width, int Height, int Depth, int Pad, char *Data, unsigned int Flags, wxColour *Palette) +{ + int lastDepth = depth; +#ifdef wx_msw + int virtWidth; +#endif + + // Can we display it at all? + if (displayDepth <= 0) return -1; + + // Filter out unsupported depths + switch (Depth) + { + case 1: + case 2: + case 4: + case 8: + case 12: + case 15: + case 24: + case 32: break; + default: +#if (DEBUG > 0) + *errorstr << "Unsupported colour depth " << Depth << endl; +#endif + return -1; + } + + freeResources(Depth, Flags); + + width = Width; height = Height; depth = Depth; pad = Pad; + if ((depth == 12) || (depth == 15) || (depth == 16)) + pitch = (width * 16 + Pad - 1) >> 3; + else + pitch = (width * depth + Pad - 1) >> 3; + + switch (Pad) + { + case 16: pitch &= ~1; break; + case 32: pitch &= ~3; break; + case 64: pitch &= ~7; break; + case 128: pitch &= ~15; break; + case 256: pitch &= ~31; break; + default: break; + } + data = Data; validDisplay = FALSE; pixFlags = Flags; parentWin = Win; + if ((pixFlags & WX_PIXFLAG_FASTDITHER) != 0) + { + pixFlags |= WX_PIXFLAG_DITHER; colour_matcher = &wxPixmap::findFastColour; + } + else + { + colour_matcher = &wxPixmap::findBestColour; + } + + // If the depth changed the translation tables _must_ be updated + if (lastDepth != Depth) + { + pixFlags &= ~WX_PIXFLAG_SAMEPALETTE; + } + + if ((pixFlags & WX_PIXFLAG_SAMEPALETTE) == 0) + { + if ((Palette != NULL) && (Depth <= 8)) + { + palette = new wxColour[1<<Depth]; + memcpy(palette, Palette, (1<<Depth)*sizeof(wxColour)); + } + } + +#ifndef wx_msw + // The data has to be translated if its depth differs from the display depth + // or the format is different from the internal one. A colour translation + // table is needed if translated data is needed AND either mode is <= 8bpp. + if (((depth == displayDepth) && (depth == 24) && (rgbOrder == RGB_ORDER_RGB)) || + ((pixFlags & WX_PIXFLAG_TRANSLATE) == 0)) + { + modeData = data; displayPitch = pitch; displayPad = Pad; + } + else +#endif + { + displayPad = 32; + int useDepth; + + if (displayDepth == 15) + useDepth = 16; + else + useDepth = displayDepth; + +#ifdef wx_x + displayPitch = ((width * useDepth + 31) >> 3) & ~3; + if ((modeData = (char*)malloc(displayPitch*height*sizeof(char))) == NULL) + { + errorMemory(); return(-1); + } +#endif +#ifdef wx_msw + virtWidth = width; + + while (((displayPitch = virtWidth * useDepth) & 31) != 0) virtWidth++; + displayPitch >>= 3; + if ((modeData = (char*)malloc(displayPitch * height * sizeof(char))) == NULL) + { + errorMemory(); return -1; + } +#endif + // *errorstr << "Allocated buffer of size " << width << '*' << height << ", " << displayPitch*height << endl; + } + +#ifdef wx_msw + bmDesc.bmType = 0; + bmDesc.bmWidth = virtWidth; + bmDesc.bmHeight = height; + bmDesc.bmWidthBytes = displayPitch; + bmDesc.bmPlanes = 1; + bmDesc.bmBitsPixel = displayDepth; + bmDesc.bmBits = (LPVOID)modeData; + bitmap = CreateBitmapIndirect(&bmDesc); + if (bitmap == (HBITMAP)0) + { + errorMemory(); return(-1); + } + // *errorstr << "bitmap handle = " << (long)bitmap << endl; +#endif + +#ifdef wx_x + // *errorstr << "Get Handle... " << endl; + windowHandle = parentWin->GetXWindow(); + // *errorstr << "windowHandle = " << windowHandle << endl; + + // Use visualInfo.depth rather than displayDepth (24bpp vs 32bpp!) + xim = XCreateImage(display, visualInfo.visual, visualInfo.depth, ZPixmap, 0, modeData, width, height, displayPad, displayPitch); + + // Get parent window attributes to get colourmap + XGetWindowAttributes(display, windowHandle, &xat); + //cout << "window depth " << xat.depth << endl; + xgcvals.function = GXcopy; + myGC = XCreateGC(display, windowHandle, GCFunction, &xgcvals); +#endif + +#ifdef wx_msw + windowHandle = parentWin->GetHWND(); + winDC = GetDC(windowHandle); + srcDC = CreateCompatibleDC(winDC); + //srcDC = CreateCompatibleDC(NULL); + oldDCObject = SelectObject(srcDC, (HGDIOBJ)bitmap); +#endif + + initColoursForMode(); + + // Now create the translation table if necessary (if depth, lastDepth > 8 we can use the + // previous one. + if (((depth != displayDepth) && (lastDepth <= 12)) || (depth <= 12)) + { + if ((pixFlags & WX_PIXFLAG_SAMEPALETTE) == 0) {buildTranslationTable();} + } + // In case the fast dither table has to be built, call function too... + else if ((FastDitherTab == NULL) && ((pixFlags & WX_PIXFLAG_FASTDITHER) != 0) && (displayDepth < 15)) + { + buildTranslationTable(); + } + + // In case the pixmap should be dithered: + if (((pixFlags & WX_PIXFLAG_DITHER) != 0) && (depth <= 12) && (displayDepth <= 8)) + { + processPixmapPalette(); + } + + return(0); +} + + + +#ifdef wx_msw +// Special Win hack +void wxPixmap::win_translate_24_to_24(unsigned char *dest) +{ + int i, j; + unsigned char *b, *d, *bline, *dline; + + bline = (unsigned char*)data; dline = dest; + for (i=0; i<height; i++, bline+=pitch, dline+=displayPitch) + { + b = bline; d = dline; + if (rgbOrder == RGB_ORDER_RGB) + { + for (j=0; j<width; j++) + { + d[0] = b[0]; + d[1] = b[1]; + d[2] = b[2]; + d += 3; b += 3; + } + } + else + { + for (j=0; j<width; j++) + { + d[0] = b[2]; + d[1] = b[1]; + d[2] = b[0]; + d += 3; b += 3; + } + } + } +} +#endif + + +// It's a biggie: translate the data into the correct format for the current display. +void wxPixmap::translateToMode(void) +{ + const unsigned char *src = (unsigned char *)data; + unsigned char *dest = (unsigned char *)modeData; + int status = 0; + + if ((data == NULL) || (modeData == NULL) || (displayDepth <= 0)) return; + + // *errorstr << "translate mode " << depth << " to " << displayDepth << endl; + if ((data != modeData) && ((pixFlags & WX_PIXFLAG_TRANSLATE) != 0)) + { + switch (depth) + { + case 1: + if (pixtrans1 != NULL) pixtrans1(src, dest, width, height, pitch, displayPitch, TransTab.c); + break; + case 2: + if (pixtrans2 != NULL) pixtrans2(src, dest, width, height, pitch, displayPitch, TransTab.c); + break; + case 4: + if (pixtrans4 != NULL) pixtrans4(src, dest, width, height, pitch, displayPitch, TransTab.c); + break; + case 8: + if (pixtrans8 != NULL) pixtrans8(src, dest, width, height, pitch, displayPitch, TransTab.c); + break; + case 12: + if (pixtrans12 != NULL) pixtrans12(src, dest, width, height, pitch, displayPitch, TransTab.c); + break; + case 15: + if (pixtrans15 != NULL) pixtrans15(src, dest, width, height, pitch, displayPitch, TrueTransTab); + break; + case 24: +#ifdef wx_msw + if (displayDepth == 24) + { + win_translate_24_to_24(dest); break; + } +#endif + if (pixtrans24 != NULL) pixtrans24(src, dest, width, height, pitch, displayPitch, TrueTransTab); + break; + case 32: + if (pixtrans32 != NULL) pixtrans32(src, dest, width, height, pitch, displayPitch, TrueTransTab); + break; + default: + status = -1; + break; + } + + // *errorstr << "Translate bitmap status " << status << endl; + + if (status < 0) + { +#if (DEBUG > 0) + *errorstr << "Can't translate from " << depth << " to " << displayDepth << endl; +#endif + } + } + validDisplay = TRUE; +} + + +// Ditherer to display modes with <= 8bpp. Otherwise use the simple translator. +void wxPixmap::ditherToMode(void) +{ + int status=0; + unsigned char *dest = (unsigned char *)modeData; + + if ((data == NULL) || (modeData == NULL) || (displayDepth <= 0)) return; + + /*for (int i=0; i<(1<<depth); i++) + { + *errorstr << '[' << (int)(pixmapPalette[i].red) << ',' << (int)(pixmapPalette[i].green) << ',' << (int)(pixmapPalette[i].blue) << ']'; + } + *errorstr << endl;*/ + + // *errorstr << "Dither mode " << depth << " to " << displayDepth << endl; + if (displayDepth <= 8) + { + if ((busyCursorMode) && ((pixFlags & WX_PIXFLAG_FASTDITHER) == 0)) + ::wxBeginBusyCursor(); + + switch (depth) + { + case 1: + if (pixdither1 != NULL) (this->*pixdither1)(dest, displayPad); + break; + case 2: + if (pixdither2 != NULL) (this->*pixdither2)(dest, displayPad); + break; + case 4: + if (pixdither4 != NULL) (this->*pixdither4)(dest, displayPad); + break; + case 8: + if (pixdither8 != NULL) (this->*pixdither8)(dest, displayPad); + break; + case 12: + if (pixdither12 != NULL) (this->*pixdither12)(dest, displayPad); + break; + case 15: + if (pixdither15 != NULL) (this->*pixdither15)(dest, displayPad); + break; + case 24: + if (pixdither24 != NULL) (this->*pixdither24)(dest, displayPad); + break; + case 32: + if (pixdither32 != NULL) (this->*pixdither32)(dest, displayPad); + break; + default: + status = -1; + break; + } + + if ((busyCursorMode) && ((pixFlags & WX_PIXFLAG_FASTDITHER) == 0)) + ::wxEndBusyCursor(); + + if (status < 0) + { +#if (DEBUG > 0) + *errorstr << "Can't dither from " << depth << " to " << displayDepth << endl; +#endif + } + } + validDisplay = TRUE; +} + + +int wxPixmap::plotPixmap(int PosX, int PosY) +{ + // *errorstr << "plotPixmap: parentWin=" << (long)parentWin << ", data=" << (long)data << ", modeData=" << (long)modeData << endl; + if ((parentWin != NULL) && (data != NULL)) + { + if (parentWin->IsShown()) + { + // Do we have to do a colour translation? + if (!validDisplay) + { + if (data != modeData) + { + if (((pixFlags & WX_PIXFLAG_DITHER) != 0) && (displayDepth <= 8)) {ditherToMode();} + else if ((pixFlags & WX_PIXFLAG_TRANSLATE) != 0) {translateToMode();} + } +#ifdef wx_msw + // I'd really like to avoid this but Windoze gets in the way + SetBitmapBits(bitmap, displayPitch * height, modeData); +#endif + } +#ifdef wx_x + /*cout << "width = " << xim->width << ", pitch = " << xim->bytes_per_line << ", bpp = " << xim->bits_per_pixel << ", depth = " << xim->depth << ", pad = " << xim->bitmap_pad << endl; + cout << "format = " << xim->format << ", byte_order = " << xim->byte_order << ", bitmap_unit = " << xim->bitmap_unit << ", bitmap_bit_order = " << xim->bitmap_bit_order << ", rmask = " << xim->red_mask << ", gmask = " << xim->green_mask << ", bmask = " << xim->blue_mask << endl;*/ + XPutImage(display, windowHandle, myGC, xim, 0, 0, PosX, PosY, width, height); +#endif + +#ifdef wx_msw + if (BitBlt(winDC, PosX, PosY, width, height, srcDC, 0, 0, SRCCOPY) == 0) + return -1; +#endif + } + return 0; + } + else + { + errorGeneric("Window or image definition missing!"); + } + return -1; +} + + +void wxPixmap::invalidatePixmap(void) {validDisplay = FALSE;} + + + +/* + * re-build the true-colour --> colourmapped table and the fast ditherer table + * (in case another application modified a colour and the display goes wrong). + */ +void wxPixmap::refreshGlobalTables(void) +{ + int newTables=0; + + if ((depth > 12) && (displayDepth <= 8)) + { + newTables |= 1; + if (TrueTransTab != NULL) + { + delete [] TrueTransTab; + TrueTransTab = NULL; TrueTransCount--; + } + } + if ((pixFlags & WX_PIXFLAG_FASTDITHER) != 0) + { + newTables |= 2; + if (FastDitherTab != NULL) + { + delete [] FastDitherTab; + FastDitherTab = NULL; FastDitherCount--; + } + } + if (newTables != 0) + buildTranslationTable(); +} + + + + +/* + * Display the busy mouse pointer when an operation takes longer (e.g. dithering)... + */ +bool wxPixmap::setBusyCursor(bool newMode) +{ + bool oldMode = busyCursorMode; + busyCursorMode = newMode; + return oldMode; +} + + +// Finally compile the auto-generated ditherers +#include "wx_pixmap_dither.cpp" diff --git a/applications/rview/wx_pixmap.h b/applications/rview/wx_pixmap.h new file mode 100644 index 0000000..8ce86d2 --- /dev/null +++ b/applications/rview/wx_pixmap.h @@ -0,0 +1,221 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * PURPOSE: + * Efficient bitmaps of colour depths 1, 2, 4, 8, 16, 24 and 32bpp + * + * COMMENTS: + * None + */ + + +#ifndef wx_pixmap_h +#define wx_pixmap_h + + +// should be in <wx/defs.h>, but only for OS/2 (?): -- PB 2006-jan-01 +typedef unsigned long COLORREF; + +// #include "wx_stat.h" +#include <wx/generic/statusbr.h> + +#ifdef __GNUG__ +#pragma interface +#endif + +#include "wx/object.h" + +// changed in new wxWindows +//#include "wx_prec.h" +#include "wx/wxprec.h" + + + + +/* Weights for colour matching */ +#define COLOUR_WEIGHT_RED 4 +#define COLOUR_WEIGHT_GREEN 9 +#define COLOUR_WEIGHT_BLUE 1 + + +/* No changes below this line should be necessary. */ + + +/* Flag bits for initialising functions*/ +#define WX_PIXFLAG_TRANSLATE 1 /* class-resident mode translation enabled? */ +#define WX_PIXFLAG_DITHER 2 /* Dither image. Overrides WX_PIXFLAG_TRANSLATE. */ +#define WX_PIXFLAG_SAMEPALETTE 4 /* The palette is the same as the old one */ +#define WX_PIXFLAG_FASTDITHER 8 /* Fast rather than best ditherer */ +#define WX_PIXMODE_PALETTE 1 /* force making new colour tables on a mode change */ + + + +/* Structure for a permuted colour map (for more efficient colour matching) */ +typedef struct wx_permute_cmap { + unsigned char red, green, blue, number; +} wx_permute_cmap; + + + + +/* + * Translation tables for true-colour-pixmaps to <= 8bpp modes and fast ditherer. + * Shared! Define WX_PIXMAP_GLOBAL_VOLATILE to free global translation tables + * as soon as no more references to it exist. Default is to keep them. + */ +extern unsigned char *TrueTransTab; +extern unsigned char *FastDitherTab; +extern int TrueTransCount; +extern int FastDitherCount; + + + +class wxPixmap; + +/* Type of pixmap translation function */ +typedef void (*wx_pixmap_translate)(const unsigned char *src, unsigned char *dest, int width, int height, int srcPitch, int destPitch, const unsigned char *tt); +/* Type of pixmap dithering function */ +typedef void (wxPixmap::*wx_pixmap_dither)(unsigned char *dest, int destPad); +/* Type of colour-matching functions (used in ditherers) */ +typedef int (wxPixmap::*pixmap_colour_match)(unsigned char r, unsigned char g, unsigned char b); + + +/* + * Class for efficiently displaying raster images of any depth, initialising + * them from binary data. + * The macro wx_pixmap_alloc_cols distinguishes between two colour models. + * If it's defined the colours are allocated (X), otherwise the current + * colourmap is read and processed internally (much faster and usually + * much more accurate). + */ + +class WXDLLEXPORT wxPixmap : public wxObject +{ + public: + /* Public member functions */ + wxPixmap(void); + wxPixmap(wxWindow *Win, int Width, int Height, int Depth, int Pad, char *Data, unsigned int Flags=WX_PIXFLAG_TRANSLATE, wxColour *Palette=NULL); + ~wxPixmap(void); + int newPixmap(wxWindow *Win, int Width, int Height, int Depth, int Pad, char *Data, unsigned int Flags=WX_PIXFLAG_TRANSLATE, wxColour *Palette=NULL); + int plotPixmap(int PosX, int PosY); + void invalidatePixmap(void); + int getWidth(void); + int getHeight(void); + int getDepth(void); + int getPitch(void); + char *getData(void); + wxColour *getPalette(void); + int getModeDepth(void); + int getModePitch(void); + char *getModeData(void); + unsigned char *getTranslationTable(void); + void modeChange(unsigned int Flags); + void buildTranslationTable(void); + void refreshGlobalTables(void); + bool setBusyCursor(bool newMode); +#ifndef wx_pixmap_alloc_cols + void processParentPalette(bool forceUpdate=FALSE); + void processPixmapPalette(void); + wx_permute_cmap *getParentPalette(void); + int findBestColour(unsigned char red, unsigned char green, unsigned char blue); + int findFastColour(unsigned char red, unsigned char green, unsigned char blue); + /* Include auto-generated ditherer headers; these need the colour maps set up + correctly, so it only works with wx_pixmap_alloc_cols not defined. */ +#include "wx_pixmap_dither.h" +#endif + + protected: + /* protected member functions */ + void errorMemory(void); + void errorGeneric(char *message); + void initVariables(void); + void freeResources(int Depth, unsigned int Flags); + void translateToMode(void); + void ditherToMode(void); + void getDisplayAttributes(void); + void initColoursForMode(bool forceUpdate=FALSE); + void setupTranslators(void); +#ifndef wx_pixmap_alloc_cols + void sortParentPalette(int from, int to); /* Quicksorter */ + void buildInverseTable(void); +#endif + + /* protected variables */ + int width, height, depth, pad, pitch; /* parameters for image */ + int displayDepth, displayPitch, displayPad; /* parameters for current display */ + char *data; /* Raw source data */ + char *modeData; /* Translated data suitable for plotting */ + union {unsigned char *c; COLORREF *l;} TransTab; /* Colour translation table. */ + /* COLORREF is defined in wxWindows */ + int TransTabSize; + wxColour *palette; /* Source data palette */ + wxWindow *parentWin; /* The parent window date should be plotted to */ + unsigned int pixFlags; +#ifdef wx_pixmap_alloc_cols + char ColourAlloc[32]; /* bitfield (256 bits). Bit i set ==> X-colour #i allocated OK. */ + /* only used for displayDepth <= 8. */ +#else + int parentPaletteDepth; + wx_permute_cmap *parentPalette, *pixmapPalette; + unsigned char parentInverse[256]; +#endif + bool validDisplay; + bool busyCursorMode; + // wxDebugStreamBuf streamBuf; -- PB 2006-jan-01 + wxOutputStream streamBuf; + std::ostream *errorstr; + int destBitorder, destByteorder, rgbOrder; + wx_pixmap_translate pixtrans1, pixtrans2, pixtrans4, pixtrans8, pixtrans12; + wx_pixmap_translate pixtrans15, pixtrans24, pixtrans32; + wx_pixmap_dither pixdither1, pixdither2, pixdither4, pixdither8, pixdither12; + wx_pixmap_dither pixdither15, pixdither24, pixdither32; + pixmap_colour_match colour_matcher; + +#ifdef wx_x + /* System Specifics */ + Display *display; + XVisualInfo visualInfo; + XImage *xim; + XGCValues xgcvals; + XWindowAttributes xat; + GC myGC; + int screen; + Window rootwin, windowHandle; +#endif + +#ifdef wx_msw + void win_translate_24_to_24(unsigned char *dest); + + BITMAP bmDesc; + HBITMAP bitmap; + HWND windowHandle; + HWND desktop; + HDC rootDC, winDC, srcDC; + HGDIOBJ oldDCObject; + HPALETTE currentPal; +#endif +}; + + +#endif |