summaryrefslogtreecommitdiffstats
path: root/applications/rview
diff options
context:
space:
mode:
Diffstat (limited to 'applications/rview')
-rw-r--r--applications/rview/Makefile499
-rw-r--r--applications/rview/cube_render.c3106
-rw-r--r--applications/rview/cube_render.h228
-rw-r--r--applications/rview/cube_render_core.c182
-rw-r--r--applications/rview/cube_render_line.c172
-rw-r--r--applications/rview/cube_render_mesh.c48
-rw-r--r--applications/rview/cube_render_voxline.c482
-rw-r--r--applications/rview/generate_trans.sh1013
-rw-r--r--applications/rview/labelManager.cpp314
-rw-r--r--applications/rview/labelManager.hh69
-rw-r--r--applications/rview/labels.txt451
-rw-r--r--applications/rview/rview.cpp548
-rw-r--r--applications/rview/rview.hh139
-rw-r--r--applications/rview/rview.tex1479
-rw-r--r--applications/rview/rviewApp.cpp828
-rw-r--r--applications/rview/rviewApp.hh112
-rw-r--r--applications/rview/rviewChart.cpp1024
-rw-r--r--applications/rview/rviewColMap.cpp1183
-rw-r--r--applications/rview/rviewColMap.hh254
-rw-r--r--applications/rview/rviewDModes.hh1094
-rw-r--r--applications/rview/rviewDb.cpp970
-rw-r--r--applications/rview/rviewDb.hh110
-rw-r--r--applications/rview/rviewDisplay.cpp1005
-rw-r--r--applications/rview/rviewDisplay.hh224
-rw-r--r--applications/rview/rviewIO.cpp718
-rw-r--r--applications/rview/rviewIO.hh121
-rw-r--r--applications/rview/rviewImage.cpp5230
-rw-r--r--applications/rview/rviewMDD.cpp1006
-rw-r--r--applications/rview/rviewMDD.hh185
-rw-r--r--applications/rview/rviewOSection.cpp1313
-rw-r--r--applications/rview/rviewOSection.hh230
-rw-r--r--applications/rview/rviewPrefs.cpp1571
-rw-r--r--applications/rview/rviewPrefs.hh275
-rw-r--r--applications/rview/rviewQuery.cpp647
-rw-r--r--applications/rview/rviewQuery.hh128
-rw-r--r--applications/rview/rviewSound.cpp1738
-rw-r--r--applications/rview/rviewSound.hh274
-rw-r--r--applications/rview/rviewStrings.cpp216
-rw-r--r--applications/rview/rviewTable.cpp770
-rw-r--r--applications/rview/rviewThumb.cpp1183
-rw-r--r--applications/rview/rviewThumb.hh203
-rw-r--r--applications/rview/rviewTypeMan.cpp564
-rw-r--r--applications/rview/rviewTypeMan.hh106
-rw-r--r--applications/rview/rviewTypes.hh81
-rw-r--r--applications/rview/rviewUtils.cpp3630
-rw-r--r--applications/rview/rviewUtils.hh1045
-rw-r--r--applications/rview/wx_pixmap.cpp1706
-rw-r--r--applications/rview/wx_pixmap.h221
48 files changed, 38695 insertions, 0 deletions
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