From: Kevin M. Rosenberg Date: Fri, 2 Feb 2001 00:46:38 +0000 (+0000) Subject: r480: no message X-Git-Tag: debian-4.5.3-3~537 X-Git-Url: http://git.kpe.io/?p=ctsim.git;a=commitdiff_plain;h=f692b2d39f56ffbafc04283f32233c098aa2978b r480: no message --- diff --git a/configure.in b/configure.in index db07adf..e04c771 100644 --- a/configure.in +++ b/configure.in @@ -389,4 +389,4 @@ fi CXXFLAGS="$CFLAGS" -AC_OUTPUT(Makefile libctgraphics/Makefile libctsupport/Makefile libctsim/Makefile Makefile man/Makefile doc/Makefile cgi-bin/ctsim.cgi cgi-bin/Makefile html/simulate.html html/Makefile include/Makefile getopt/Makefile tools/sample-ctsim.sh cgi-bin/ctsim.conf tools/Makefile src/Makefile doc/tex2rtf/Makefile) +AC_OUTPUT(Makefile libctgraphics/Makefile libctsupport/Makefile libctsim/Makefile Makefile man/Makefile doc/Makefile cgi-bin/ctsim.cgi cgi-bin/Makefile html/simulate.html html/Makefile include/Makefile getopt/Makefile tools/sample-ctsim.sh cgi-bin/ctsim.conf tools/Makefile src/Makefile) diff --git a/doc/ctsim-concepts.tex b/doc/ctsim-concepts.tex index ba0c2ad..9cb5cfe 100644 --- a/doc/ctsim-concepts.tex +++ b/doc/ctsim-concepts.tex @@ -3,20 +3,258 @@ \setfooter{\thepage}{}{}{}{}{\thepage}% \section{Overview}\label{conceptoverview}\index{Concepts,Overview}% +In CTSim, a phantom object, or a geometrical description of the object +of a CT study is constructed and an image can be created. Then a +scanner geometry can be specified, and the projection data simulated. +Finally that projection data can be reconstructed using various user +controlled algorithms producing an image of the phantom or study object. + +In order to use CTSim effectively, some knowledge of how CTSim works +and the approach taken is required. \ctsim deals with a variety of +object, but the two we need to be concerned with are the 'phantom' and +the 'scanner'. \section{Phantoms}\label{conceptphantom}\index{Concepts,Phantoms}% \subsection{Overview}\label{phantomoverview}\index{Concepts,Phantoms,Overview}% -\subsection{Phantom Elements}\label{phantoelements}\index{Concepts,Phantoms,Elements} + +CTSim uses geometrical objects to +describe the object being scanned: rectangles, triangles, ellipses, +sectors and segments. With these the standard phantoms used in the CT +literature (the Herman and the Shepp-Logan) can be constructed. In fact +CTSim provides a shortcut to construct those phantoms for you. It also +allows you to write a file in which the composition of your own phantom is +described. + +The types of phantom elements and their definitions are taken from Herman's 1980 +book\cite{HERMAN80}. + +\subsection{Phantom File}\label{phantomfile}\index{Concepts,Phantoms,File} +Each line in the text file describes an element of the +phantom. Each line contains seven entries, in the following form: +\begin{verbatim} +item cx cy dx dy r a +\end{verbatim} +The first entry defines the type of the element, one +of {\tt rectangle}, {\tt ellipse}, {\tt triangle}, {\tt sector}, or {\tt segment}. +{\tt cx}, {\tt cy}, {\tt dx} and {\tt dy} have different meanings depending on the element type. + +{\tt r} is the rotation applied to the object in degrees counterclockwise, +and {\tt a} is the X-ray attenuation coefficient of the object. +Where objects overlap, the attenuations of the overlapped objects are summed. + + + +\subsection{Phantom Elements}\label{phantomelements}\index{Concepts,Phantoms,Elements} + \subsubsection{ellipse} +Ellipses use dx and dy to define the semi-major and semi-minor axis lengths, +with the centre of the ellipse at cx and cy. Of note, the commonly used +phantom described by Shepp and Logan\cite{SHEPP77} uses only ellipses. + \subsubsection{rectangle} +Rectangles use +cx and cy to define the position of the centre of the rectangle with respect +to the origin. dx and dy are the half-width and half-height of the +rectangle. + \subsubsection{triangle} +Triangles are drawn with the centre of the base at cx,cy, with a base +width of 2*dx in x direction, and a height of dy. Rotations are then +applied about the origin. + \subsubsection{sector} +It appears that dx and dy +define the end points of a radius of the sector, from which the radius and +the angle of the two arms of the sector are calculated. But then +orientation and centreing of the sector don't make much sense yet. + \subsubsection{segment} +Segments are the segments of a circle between a chord and the +perimeter of the circle. This also isn't clear to me, but it appears that +perhaps the distance from chord to circle perimeter, and circle radius is +defined by dx and dy. Chord is always horizontal through the origin, then +translated and then rotated (???). + +\subsection{Phantom Size} +Also note that the overall dimensions of the phantom are increased by 1\% +above the specified sizes to avoid clipping due to round-off errors. If the phantom is defined as +a rectangle of size 0.1 by 0.1, the actual phantom has extent $\pm$0.101 in +each direction. \section{Scanner}\label{conceptscanner}\index{Concepts,Scanner}% \subsection{Geometries} -\subsection{Focal Length} -\subsection{Field of View} +This is where things get tricky. There are two possible approaches. The +simple approach would be to define the size of a phantom which is put at +the centre of the scanner. The scanner would have it's bore size defined, +or perhaps better, the field of view defined. Here, field of view would be +the radius or diameter of the circular area from which data is collected +and an image reconstructed. In a real CT scanner, if the object being +scanned is larger than the field of view, you get image artifacts. And of +course you can't stuff an object into a scanner if the object is larger +than the bore! In this model, the scanner size or field of view would +be used as the standard length scale. + +However, CTSim takes another approach. I believe this approach arose +because the "image" of the phantom produced from the phantom description +was being matched to the reconstruction image of the phantom. That is, +the dimensions of the 'before' and 'after' images were being matched. +The code has a Phantom object and a Scanner object. The geometry of the +Scanner is defined in part by the properties of the Phantom. In fact, +all dimensions are determined in terms of the phantom size, which is used +as the standard length scale. Remember, as mentioned above, the +phantom dimensions are also padded by 1\%. + +The maximum of the phantom length and height is used as the phantom +dimension, and one can think of a square bounding box of this size +which completely contains the phantom. Let $l_p$ be the width (or height) +of this square. + +\subsubsection{Focal Length & Field of View} +The two other important variables are the field-of-view-ratio ($f_{vR}$) +and the focal-length-ratio ($f_{lR}$). These are used along with $l_p$ to +define the focal length and the field of view (not ratios) according to +\begin{equation} +f_l = \sqrt{2} (l_p/2)(f_{lR})= (l_p/\sqrt{2}) f_{lR} +\end{equation} +\begin{equation} +f_v = \sqrt{2}l_p f_{vR} +\end{equation} +So the field of view ratio is specified in units of the phantom diameter, +whereas the focal length is specified in units of the phantom radius. The +factor of $\sqrt(2)$ can be understood if one refers to figure 1, where +we consider the case of a first generation parallel beam CT scanner. + +\subsubsection{Parallel Geometry}\label{geometryparallel}\index{Concepts,Scanner,Geometries,Parallel} +\begin{figure} +\includegraphics[width=\textwidth]{ctsimfig1.eps} +\caption{Geometry used for a 1st generation, parallel beam CT scanner.} +\end{figure} + +In figure 1A, the excursion of the source and detector need only be $l_p$, +the height (or width) of the phantom's bounding square. However, if the +field of view were only $l_p$, then the projection shown in figure 1B +would clip the corners of the phantom. By increasing the field of view by +$\sqrt{2}$ the whole phantom is included in every projection. Of course, +if the field-of-view ratio $f_{vR}$ is larger than 1, there is no problem. +However, if $f_{vR}$ is less than one and thus the scanner is smaller than +the phantom, then distortions will occur without warning from the program. + +The code also sets the detector length equal to the field of view in this +case. The focal length is chosen to be $\sqrt{2}l_p$ so the phantom will +fit between the source and detector at all rotation angles, when the focal +length ratio is specified as 1. Again, what happens if the focal length +ratio is chosen less than 1? + +The other thing to note is that in this code the detector array is set to +be the same size as the field-of-view $f_v$, equation (2). So, one has to +know the size of the phantom to specify a given scanner geometry with a +given source-detector distance (or $f_l$ here) and a given range of +excursion ($f_v$ here). + +\subsubsection{Divergent Geometries}\label{geometrydivergent}\index{Concepts,Scanner,Geometries,Divergent} +Next consider the case of equilinear (second generation) and equiangular +(third, fourth, and fifth generation) geometries. +The parts of the code relevant to this +discussion are the same for both modes. In the equilinear mode, a single +source produces a fan beam which is read by a linear array of detectors. If +the detectors occupy an arc of a circle, then the geometry is equiangular. +See figure 2. +\begin{figure} +\includegraphics[width=\textwidth]{ctsimfig2.eps} +\caption{Equilinear and equiangular geometries.} +\end{figure} + +For these geometries, the following logic is executed: A variable dHalfSquare +$d_{hs}$ is defined as +\begin{equation} +d_{hs} = (f_v)/(2\sqrt{2}) = (l_p/2) f_{vR} +\end{equation} +This is then subtracted from the focal length $f_l$ as calculated above, and +assigned to a new variable $\mathrm{dFocalPastPhm} = f_l - d_{hs}$. Since $f_l$ and +$d_{hs}$ are derived from the phantom dimension and the input focal length and field of view ratios, one can write, +\begin{equation} +\mathrm{dFocalPastPhm} = f_l -d_{hs} + = \sqrt{2}(l_p/2) f_{lR} - (l_p/2) f_{vR} = l_p(\sqrt{2}f_{lR} - f_{vR}) +\end{equation} +If this quantity is less than or equal to zero, then at least for some +projections the source is inside the phantom. Perhaps a figure will help at +this point. Consider first the case where $f_{vR} = f_{lR} =1 $, figure 3. The +square in the figure bounds the phantom and has sides $l_p$. For this case +then, +\[ +f_l=\sqrt{2}l_p/2 = l_p/\sqrt{2}, +\] +\[ +f_v = \sqrt{2}l_p, +\] +and +\[ +d_{hs} = {l_p}/{2}. +\] +Then +\[ +\mathrm{dFocalPastPhm} = ({l_p}/{2}) (\sqrt{2}-1) +\] +\begin{figure} +\includegraphics[height=0.5\textheight]{ctsimfig3.eps} +\caption{Equilinear and equiangluar geometry when focal length ratio = +field of view ratio = 1.} +\end{figure} +The angle $\alpha$ is now defined as shown in figure 3, and the detector +length is adjusted to subtend the angle $2\alpha$ as shown. Note that the +size of the detector array may have changed and the field of view is not +used. +For a circular array of detectors, the detectors are spaced around a +circle covering an angular distance of $2\alpha$. The dotted circle in +figure 3 indicates the positions of the detectors in this case. Note that +detectors at the ends of the range would not be illuminated by the source. + +Now, consider increasing the focal length ratio to two leaving the +field of view ratio as 1, as in Figure 4. Now the detectors array is +denser, and the real field of view is closer to that specified, but note +again that the field of view is not used. Instead, the focal length is +used to give a distance from the centre of the phantom to the source, and +the detector array is adjusted to give an angular coverage to include the +whole phantom. +\begin{figure} +\includegraphics[width=\textwidth]{ctsimfig4.eps} +\caption{Equilinear and equiangluar geometry when focal length ratio = 2 +and the field of view ratio = 1.} +\end{figure} +Now consider a focal length ratio of 4 (figure 5). As expected, the angle +$\alpha$ is smaller still. The dotted square is the bounding square of +the phantom rotated by 45 degrees, corresponding to the geometry of a +projection taken at that angle. Note that the fan beam now clips the top +and bottom corners of the bounding square. This illustrates that one may +still be clipping the phantom, despite CTSim's best efforts. You have +been warned. +\begin{figure} +\includegraphics[width=\textwidth]{ctsimfig5.eps} +\caption{Equilinear and equiangluar geometry when focal length ratio = 4.} + +\end{figure} + \section{Reconstruction}\label{conceptreconstruction}\index{Concepts,Reconstruction}% +\subsection{Overview} \subsection{Filtered Backprojection} +\subsection{Direct Inverse Fourier} +This method is not currently implemented in \ctsim, however it is planned for a +future release. This method does not give as accurate result as filtered +backprojection mostly due to interpolation occuring in the frequency domain rather +than the spatial domain. The technique is comprised of two sequential steps: +filtering projections and then backprojecting the filtered projections. Though +these two steps are sequential, each view position can be processed individually. +This parallelism is exploited in the MPI versions of \ctsim where the data from +all the views are spread about amongst all of the processors. This has been testing +in a 16-CPU cluster with good results. + +\subsubsection{Filter projections} +The projections for a single view have their frequency data multipled by +a filter of absolute(w). \ctsim permits four different ways to accomplish this +filtering. Two of the methods use convolution of the projection data with the +inverse fourier transform of absolute(x). The other two methods perform an fourier +transform of the projection data and multiply that by the absolute(x) filter and +then perform an inverse fourier transform. + +\item{Backprojection of filtered projections} \ No newline at end of file diff --git a/doc/ctsim-gui.tex b/doc/ctsim-gui.tex index 99bc69d..a8df9fe 100644 --- a/doc/ctsim-gui.tex +++ b/doc/ctsim-gui.tex @@ -2,6 +2,7 @@ \setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}% \setfooter{\thepage}{}{}{}{}{\thepage}% +\section{Overview} \ctsim is the graphical shell for the CTSim project. It is written using the wxLibrary for cross-platform compatibility with GTK, Motif, and Microsoft Windows. It includes all of the functionality of @@ -12,3 +13,16 @@ the command-line tool {\tt ctsimtext} as well as image processing and visualizat \usage ctsim [OPTIONS] [files to open...] +\section{Files Supported} +\subsection{Phantom} +\subsection{Image} +\subsection{Projection} +\subsection{Plot} + +\section{Phantom Menus} + +\section{Image Menus} + +\section{Projection Menus} + +\section{Plot Menus} diff --git a/doc/ctsim.bib b/doc/ctsim.bib index c29be25..30441bd 100644 --- a/doc/ctsim.bib +++ b/doc/ctsim.bib @@ -23,6 +23,12 @@ year = {1992} } +@book{FOLEY80, + author = {Foley and Van Dam}, + title = {Principles of Computer Graphics}, + year = {circa 1980} +} + @article{SHEPP74, author = {L. Shepp and B. Logan}, title = {The Fourier Reconstruction of a Head Section}, diff --git a/doc/ctsim.tex b/doc/ctsim.tex index 6b42f8d..be1030f 100644 --- a/doc/ctsim.tex +++ b/doc/ctsim.tex @@ -1,5 +1,6 @@ \documentclass[11pt]{report}% %\input{psbox.tex} +\usepackage{graphicx} \usepackage{texhelp} \usepackage{fancyheadings} \usepackage{mysober} @@ -87,6 +88,8 @@ The \ctsim package has two executable files: the graphical \helprefn{ctsim}{ctsi \include{ctsim-web} +\include{ctsim-appendix} + \newpage \bibliographystyle{plain} diff --git a/doc/mytitle.sty b/doc/mytitle.sty index 4d5652c..5f9c689 100644 --- a/doc/mytitle.sty +++ b/doc/mytitle.sty @@ -7,14 +7,15 @@ \vspace*{2cm}\begin{flushleft} {\huge \sf\@title\\\rule{\textwidth}{0.5mm}} \vskip 3em {\large \lineskip .75em {\sf\@author} -\par} +\par +Thanks to Ian Kay as co-author of this manual} \vskip 1.5em {\large\sf \@date \par} \end{flushleft} \par \@thanks \vfill {\sf\small\begin{flushright}% +kevin@rosenberg.net Heart Hospital of New Mexico\\ Albuquerque, New Mexico\\ -Tel. 1-505-724-2378 \end{flushright}} \null \end{titlepage} diff --git a/include/ctsupport.h b/include/ctsupport.h index d52c083..dbf7dec 100644 --- a/include/ctsupport.h +++ b/include/ctsupport.h @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: ctsupport.h,v 1.23 2001/01/28 19:10:18 kevin Exp $ +** $Id: ctsupport.h,v 1.24 2001/02/02 00:46:38 kevin Exp $ ** ** ** This program is free software; you can redistribute it and/or modify @@ -191,6 +191,10 @@ template inline T nearest (double x) { return (x > 0 ? static_cast(x+0.5) : static_cast(x-0.5)); } +template +inline T maxValue (T x, T y) +{ return (x > y ? x : y); } + inline bool isEven (int n) { return (n % 2) == 0; } diff --git a/libctsim/scanner.cpp b/libctsim/scanner.cpp index 30b75d2..120ddb5 100644 --- a/libctsim/scanner.cpp +++ b/libctsim/scanner.cpp @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: scanner.cpp,v 1.27 2001/01/28 19:10:18 kevin Exp $ +** $Id: scanner.cpp,v 1.28 2001/02/02 00:46:38 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -132,9 +132,8 @@ Scanner::Scanner (const Phantom& phm, const char* const geometryName, int nDet, m_initPos.yd2 = m_dYCenter - m_dFocalLength; m_initPos.angle = 0.0; } else if (m_idGeometry == GEOMETRY_EQUILINEAR) { -#if 0 - double dAngle = (m_dFieldOfView / 2) / cos (asin (m_dFieldOfView / 2 / m_dFocalLength)); -#else + + double dAngle1 = atan ((m_dFieldOfView / 2) / m_dFocalLength); double dHalfSquare = m_dFieldOfView / SQRT2 / 2; double dFocalPastPhm = m_dFocalLength - dHalfSquare; if (dFocalPastPhm <= 0.) { @@ -142,8 +141,12 @@ Scanner::Scanner (const Phantom& phm, const char* const geometryName, int nDet, m_failMessage = "Focal Point inside of phantom"; return; } - double dAngle = atan( dHalfSquare / dFocalPastPhm ); -#endif + double dAngle2 = atan( dHalfSquare / dFocalPastPhm ); + double dAngle = maxValue (dAngle1, dAngle2); +#if 0 + double dAngle = (m_dFieldOfView / 2) / cos (asin (m_dFieldOfView / 2 / m_dFocalLength)); +#endif + double dHalfDetLen = 2 * m_dFocalLength * tan (dAngle); m_detLen = dHalfDetLen * 2; @@ -162,9 +165,8 @@ Scanner::Scanner (const Phantom& phm, const char* const geometryName, int nDet, m_initPos.yd2 = m_dYCenter - m_dFocalLength; m_initPos.angle = 0.0; } else if (m_idGeometry == GEOMETRY_EQUIANGULAR) { -#if 0 - double dAngle = atan ((m_dFieldOfView / 2) / m_dFocalLength); -#else + double dAngle1 = atan ((m_dFieldOfView / 2) / m_dFocalLength); + double dHalfSquare = m_dFieldOfView / SQRT2 / 2; double dFocalPastPhm = m_dFocalLength - dHalfSquare; if (dFocalPastPhm <= 0.) { @@ -172,8 +174,9 @@ Scanner::Scanner (const Phantom& phm, const char* const geometryName, int nDet, m_failMessage = "Focal Point inside of phantom"; return; } - double dAngle = atan ( dHalfSquare / dFocalPastPhm ); -#endif + double dAngle2 = atan ( dHalfSquare / dFocalPastPhm ); + double dAngle = maxValue (dAngle1, dAngle2); + m_detLen = 2 * dAngle; m_detInc = m_detLen / m_nDet; if (m_nDet % 2 == 0) // Adjust for Even number of detectors diff --git a/msvc/ctsim/ctsim.plg b/msvc/ctsim/ctsim.plg index b0ee1d3..f34e15a 100644 --- a/msvc/ctsim/ctsim.plg +++ b/msvc/ctsim/ctsim.plg @@ -6,13 +6,13 @@ --------------------Configuration: ctsim - Win32 Debug--------------------

Command Lines

-Creating temporary file "C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP27.tmp" with contents +Creating temporary file "C:\DOCUME~1\kevin\LOCALS~1\Temp\RSPAC.tmp" with contents [ /nologo /G6 /MTd /W3 /Gm /Gi /GR /GX /Zi /Od /Gy /I "\wx2.2.5\include" /I "..\..\..\fftw-2.1.3\fftw" /I "\wx2.2.5\src\png" /I "\wx2.2.5\src\zlib" /I "..\..\include" /I "..\..\getopt" /I "..\..\..\fftw-2.1.3\rfftw" /D VERSION=\"3.0.0beta1\" /D "_DEBUG" /D "__WXMSW__" /D "HAVE_SGP" /D "HAVE_PNG" /D "HAVE_WXWINDOWS" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "HAVE_STRING_H" /D "HAVE_FFTW" /D "HAVE_RFFTW" /D "HAVE_GETOPT_H" /D "MSVC" /D "__WIN95__" /D "__WIN32__" /D WINVER=0x0400 /D "STRICT" /D CTSIMVERSION=\"3.0.0beta1\" /FR"Debug/" /Fp"Debug/ctsim.pch" /YX /Fo"Debug/" /Fd"Debug/" /FD /GZ /c "C:\ctsim\src\graph3dview.cpp" ] -Creating command line "cl.exe @C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP27.tmp" -Creating temporary file "C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP28.tmp" with contents +Creating command line "cl.exe @C:\DOCUME~1\kevin\LOCALS~1\Temp\RSPAC.tmp" +Creating temporary file "C:\DOCUME~1\kevin\LOCALS~1\Temp\RSPAD.tmp" with contents [ comctl32.lib winmm.lib rpcrt4.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ../libctsim/Debug/libctsim.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib libcmtd.lib ..\..\..\fftw-2.1.3\Win32\FFTW2st\Debug\FFTW2st.lib ..\..\..\fftw-2.1.3\Win32\RFFTW2st\Debug\RFFTW2st.lib wxd.lib xpmd.lib tiffd.lib zlibd.lib pngd.lib opengl32.lib glu32.lib /nologo /subsystem:windows /incremental:yes /pdb:"Debug/ctsim.pdb" /debug /machine:I386 /out:"Debug/ctsim.exe" /pdbtype:sept /libpath:"\wx2.2.5\lib" .\Debug\ctsim.obj @@ -33,27 +33,28 @@ comctl32.lib winmm.lib rpcrt4.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib w \wx2.2.5\lib\zlibd.lib \wx2.2.5\lib\tiffd.lib ] -Creating command line "link.exe @C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP28.tmp" +Creating command line "link.exe @C:\DOCUME~1\kevin\LOCALS~1\Temp\RSPAD.tmp"

Output Window

Compiling... graph3dview.cpp -C:\ctsim\src\graph3dview.cpp(230) : warning C4101: 'n2' : unreferenced local variable -C:\ctsim\src\graph3dview.cpp(230) : warning C4101: 'n3' : unreferenced local variable -C:\ctsim\src\graph3dview.cpp(230) : warning C4101: 'n4' : unreferenced local variable -C:\ctsim\src\graph3dview.cpp(296) : warning C4305: 'initializing' : truncation from 'const double' to 'float' -C:\ctsim\src\graph3dview.cpp(296) : warning C4305: 'initializing' : truncation from 'const double' to 'float' -C:\ctsim\src\graph3dview.cpp(296) : warning C4305: 'initializing' : truncation from 'const double' to 'float' -C:\ctsim\src\graph3dview.cpp(301) : warning C4305: 'initializing' : truncation from 'const double' to 'float' -C:\ctsim\src\graph3dview.cpp(301) : warning C4305: 'initializing' : truncation from 'const double' to 'float' -C:\ctsim\src\graph3dview.cpp(301) : warning C4305: 'initializing' : truncation from 'const double' to 'float' -C:\ctsim\src\graph3dview.cpp(302) : warning C4305: 'initializing' : truncation from 'const double' to 'float' -C:\ctsim\src\graph3dview.cpp(302) : warning C4305: 'initializing' : truncation from 'const double' to 'float' +C:\ctsim\src\graph3dview.cpp(287) : warning C4101: 'n2' : unreferenced local variable +C:\ctsim\src\graph3dview.cpp(287) : warning C4101: 'n3' : unreferenced local variable +C:\ctsim\src\graph3dview.cpp(287) : warning C4101: 'n4' : unreferenced local variable +C:\ctsim\src\graph3dview.cpp(424) : warning C4305: 'initializing' : truncation from 'const double' to 'float' +C:\ctsim\src\graph3dview.cpp(424) : warning C4305: 'initializing' : truncation from 'const double' to 'float' +C:\ctsim\src\graph3dview.cpp(424) : warning C4305: 'initializing' : truncation from 'const double' to 'float' +C:\ctsim\src\graph3dview.cpp(431) : warning C4305: 'initializing' : truncation from 'const double' to 'float' +C:\ctsim\src\graph3dview.cpp(431) : warning C4305: 'initializing' : truncation from 'const double' to 'float' +C:\ctsim\src\graph3dview.cpp(431) : warning C4305: 'initializing' : truncation from 'const double' to 'float' +C:\ctsim\src\graph3dview.cpp(432) : warning C4305: 'initializing' : truncation from 'const double' to 'float' +C:\ctsim\src\graph3dview.cpp(432) : warning C4305: 'initializing' : truncation from 'const double' to 'float' +C:\ctsim\src\graph3dview.cpp(432) : warning C4305: 'initializing' : truncation from 'const double' to 'float' Linking...

Results

-ctsim.exe - 0 error(s), 11 warning(s) +ctsim.exe - 0 error(s), 12 warning(s) diff --git a/src/ctsim.h b/src/ctsim.h index 412fcca..e33c05c 100644 --- a/src/ctsim.h +++ b/src/ctsim.h @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: ctsim.h,v 1.39 2001/01/31 01:01:22 kevin Exp $ +** $Id: ctsim.h,v 1.40 2001/02/02 00:46:38 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -310,6 +310,11 @@ enum { PLOTMENU_VIEW_SCALE_MINMAX, PLOTMENU_VIEW_SCALE_AUTO, PLOTMENU_VIEW_SCALE_FULL, + + GRAPH3D_VIEW_SURFACE, + GRAPH3D_VIEW_COLOR, + GRAPH3D_VIEW_LIGHTING, + GRAPH3D_VIEW_SMOOTH, }; #endif diff --git a/src/docs.cpp b/src/docs.cpp index f82fc60..ae82675 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: docs.cpp,v 1.21 2001/01/31 01:01:22 kevin Exp $ +** $Id: docs.cpp,v 1.22 2001/02/02 00:46:38 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -376,14 +376,14 @@ TextFileDocument::getTextCtrl() IMPLEMENT_DYNAMIC_CLASS(Graph3dFileDocument, wxDocument) Graph3dFileDocument::Graph3dFileDocument(void) -: m_bBadFileOpen(false), m_nVertices(0), m_pVertices(0), m_pNormals(0) +: m_bBadFileOpen(false), m_nVertices(0), m_pVertices(0), m_pNormals(0),m_nx(0),m_ny(0),m_array(0) { } Graph3dFileDocument::~Graph3dFileDocument() { - delete [] m_pVertices; - delete [] m_pNormals; +// delete [] m_pVertices; +// delete [] m_pNormals; } bool @@ -419,30 +419,14 @@ Graph3dFileDocument::getView() const bool Graph3dFileDocument::createFromImageFile (const ImageFile& rImageFile) { - delete [] m_pVertices; - delete [] m_pNormals; +// delete [] m_pVertices; +// delete [] m_pNormals; + m_nx = rImageFile.nx(); m_ny = rImageFile.ny(); m_array = rImageFile.getArray(); -#if 0 - const int nTriangles = nx * ny; - m_nVertices = nTriangles; - m_pVertices = new glTripleFloat [nTriangles]; - m_pNormals = new glTripleFloat [nTriangles]; - - for (unsigned int ix = 0; ix < nx; ix++) { - for (unsigned int iy = 0; iy < ny; iy++) { - const int iTriangle = ix * iy; - m_pVertices[iTriangle][0] = ix; - m_pVertices[iTriangle][1] = iy; - m_pVertices[iTriangle][2] = v[ix][iy]; - m_pNormals[iTriangle][0] = 0; - m_pNormals[iTriangle][1] = 0; - m_pNormals[iTriangle][2] = 0; - } - } -#endif + return true; } diff --git a/src/docs.h b/src/docs.h index 86f6cce..e7b6815 100644 --- a/src/docs.h +++ b/src/docs.h @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: docs.h,v 1.20 2001/01/31 01:01:22 kevin Exp $ +** $Id: docs.h,v 1.21 2001/02/02 00:46:38 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -251,6 +251,11 @@ class Graph3dFileDocument: public wxDocument bool getBadFileOpen() const { return m_bBadFileOpen; } void setBadFileOpen() { m_bBadFileOpen = true; } bool createFromImageFile (const ImageFile& rImageFile); + + int nx() const { return m_nx; } + int ny() const { return m_ny; } + ImageFileArray getArray() { return m_array; } + ImageFileArrayConst getArray() const { return m_array; } }; #endif // wxUSE_GLCANVAS diff --git a/src/graph3dview.cpp b/src/graph3dview.cpp index eb5cbf4..c9b28f7 100644 --- a/src/graph3dview.cpp +++ b/src/graph3dview.cpp @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: graph3dview.cpp,v 1.4 2001/01/31 01:01:22 kevin Exp $ +** $Id: graph3dview.cpp,v 1.5 2001/02/02 00:46:38 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -70,6 +70,35 @@ #include #endif +// Rainbow: Purple->Blue->Cyan->Green->Yellow->Red = (1,0,1)-(0,0,1)-(0,1,1)-(0,1,0)-(1,1,0)-(1,0,0) +static void +intensityToColor (double dIntensity, float vecColor[3]) +{ + double dRange = dIntensity * 5; + int iRange = static_cast(floor (dRange)); + double dFrac = dRange - iRange; + + switch (iRange) { + case 0: + vecColor[0] = 1 - dFrac; vecColor[1] = 0; vecColor[2] = 1; + break; + case 1: + vecColor[0] = 0; vecColor[1] = dFrac; vecColor[2] = 1; + break; + case 2: + vecColor[0] = 0; vecColor[1] = 1; vecColor[2] = 1 - dFrac; + break; + case 3: + vecColor[0] = dFrac; vecColor[1] = 1; vecColor[2] = 0; + break; + case 4: + vecColor[0] = 0; vecColor[1] = 1 - dFrac; vecColor[2] = 0; + break; + case 5: + vecColor[0] = 1; vecColor[1] = 0; vecColor[2] = 0; + break; + } +} //*********************************************************************** // Function: CalculateVectorNormal @@ -110,17 +139,25 @@ IMPLEMENT_DYNAMIC_CLASS(Graph3dFileView, wxView) BEGIN_EVENT_TABLE(Graph3dFileView, wxView) EVT_MENU(IFMENU_FILE_PROPERTIES, Graph3dFileView::OnProperties) +EVT_MENU(GRAPH3D_VIEW_LIGHTING, Graph3dFileView::OnLighting) +EVT_MENU(GRAPH3D_VIEW_COLOR, Graph3dFileView::OnColor) +EVT_MENU(GRAPH3D_VIEW_SMOOTH, Graph3dFileView::OnSmooth) +EVT_MENU(GRAPH3D_VIEW_SURFACE, Graph3dFileView::OnSurface) END_EVENT_TABLE() Graph3dFileView::Graph3dFileView () -: m_pFileMenu(NULL) +: m_pFileMenu(NULL), m_pViewMenu(NULL) { - m_bUseVertexArrays = GL_FALSE; - m_bDoubleBuffer = GL_TRUE; - m_bSmooth = GL_TRUE; - m_bLighting = GL_TRUE; + m_bUseVertexArrays = false; + m_bDoubleBuffer = true; + m_bSmooth = true; + m_bLighting = true; + m_bSurface = true; + m_bLighting = true; + m_bColor = true; m_dXRotate = 0; m_dYRotate = 0; + m_dZRotate = 0; } Graph3dFileView::~Graph3dFileView() @@ -152,6 +189,10 @@ Graph3dFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) ) m_pFrame->Show(true); Activate(true); + m_pViewMenu->Check (GRAPH3D_VIEW_COLOR, m_bColor); + m_pViewMenu->Check (GRAPH3D_VIEW_LIGHTING, m_bLighting); + m_pViewMenu->Check (GRAPH3D_VIEW_SMOOTH, m_bSmooth); + m_pViewMenu->Check (GRAPH3D_VIEW_SURFACE, m_bSurface); return true; } @@ -188,67 +229,112 @@ Graph3dFileView::CreateCanvas (wxFrame* parent) void Graph3dFileView::DrawSurface() { - int nVertices = GetDocument()->m_nVertices; - glTripleFloat* pVertices = GetDocument()->m_pVertices; - glTripleFloat* pNormals = GetDocument()->m_pNormals; - #ifdef GL_EXT_vertex_array if (m_bUseVertexArrays) { - glDrawArraysEXT( GL_TRIANGLE_STRIP, 0, nVertices ); + // glDrawArraysEXT( GL_TRIANGLE_STRIP, 0, nVertices ); } else { #endif - double edge = 1.; - unsigned int nx = GetDocument()->m_nx; - unsigned int ny = GetDocument()->m_ny; - const ImageFileArray v = GetDocument()->m_array; - if (nx == 0 || ny == 0 || ! v) - return; - - double dMin = v[0][0]; - double dMax = dMin; - unsigned int ix; - for (ix = 0; ix < nx; ix++) - for (unsigned int iy = 0; iy < ny; iy++) - if (v[ix][iy] < dMin) - dMin = v[ix][iy]; - else if (v[ix][iy] > dMax) - dMax = v[ix][iy]; - - double actOffset = dMin; - double actScale = 0.3 * sqrt(nx*nx+ny*ny) / (dMax - dMin); - - glRotatef( m_dYRotate, 0.0, 0.0, 1.0 ); - glRotatef( m_dXRotate, 1.0, 0.0, 0.0 ); - glTranslatef (-static_cast(nx) / 2, -static_cast(ny) / 2, 0); - -// glNewList(opnListNum++,GL_COMPILE); - for (ix = 0; ix < nx-1; ix++) { - for (unsigned int iy = 0; iy < ny-1; iy++) { - - float p1[3], p2[3], p3[3], p4[3]; - float n1[3], n2[3], n3[3], n4[3]; - glBegin(GL_LINE_LOOP); - - p1[0] = ix; p1[1] = actScale * (v[ix][iy] + actOffset); p1[2] = iy; - p2[0] = ix+1; p2[1] = actScale * (v[ix+1][iy] + actOffset); p2[2] = iy; - p3[0] = ix+1; p3[1] = actScale * (v[ix+1][iy+1] + actOffset); p3[2] = iy; - p4[0] = ix; p4[1] = actScale * (v[ix][iy+1] + actOffset); p4[2] = iy; - - n1[0] = -(p2[1] - p1[1])*(p3[2] - p1[2]) + (p2[2] - p1[2])*(p3[1] - p2[1]); - n1[1] = -(p2[2] - p1[2])*(p3[0] - p2[0]) + (p2[0] - p1[0])*(p3[2] - p2[2]); - n1[2] = -(p2[0] - p1[0])*(p3[1] - p2[1]) + (p2[1] - p1[1])*(p3[0] - p2[0]); - - glVertex3fv(p1); glNormal3fv(n1); - glVertex3fv(p2); glNormal3fv(n1); - glVertex3fv(p3); glNormal3fv(n1); - glVertex3fv(p4); glNormal3fv(n1); - glEnd(); - } - - } - glEndList(); - + double edge = 1.; + unsigned int nx = GetDocument()->nx(); + unsigned int ny = GetDocument()->ny(); + const ImageFileArrayConst v = GetDocument()->getArray(); + if (nx == 0 || ny == 0 || ! v) + return; + + glRotatef( m_dXRotate, 1.0, 0.0, 0.0 ); + glRotatef( m_dZRotate, 0.0, 1.0, 0.0 ); + glRotatef( m_dYRotate, 0.0, 0.0, 1.0 ); + glTranslatef (-static_cast(nx) / 2., 0, -static_cast(ny) / 2.); + + InitMaterials(); + + if (m_bSmooth) { + glShadeModel (GL_SMOOTH); + } else { + glShadeModel (GL_FLAT); + } + + if (m_bLighting) { + glEnable (GL_LIGHTING); + } else { + glDisable (GL_LIGHTING); + } + + double dMin = v[0][0]; + double dMax = dMin; + unsigned int ix; + for (ix = 0; ix < nx; ix++) + for (unsigned int iy = 0; iy < ny; iy++) + if (v[ix][iy] < dMin) + dMin = v[ix][iy]; + else if (v[ix][iy] > dMax) + dMax = v[ix][iy]; + + double dIntensityScale = dMax - dMin; + double actOffset = dMin; + double actScale = 0.3 * sqrt(nx*nx+ny*ny) / (dMax - dMin); + + // glNewList(opnListNum++,GL_COMPILE); + if (! m_bColor) + glColor3f (1.0, 1.0, 1.0); + + glDisable (GL_CULL_FACE); + for (ix = 0; ix < nx-1; ix++) { + for (unsigned int iy = 0; iy < ny-1; iy++) { + + float p1[3], p2[3], p3[3], p4[3]; + float n1[3], n2[3], n3[3], n4[3]; + if (m_bSurface) + glBegin(GL_QUADS); + else + glBegin(GL_LINE_LOOP); + + p1[0] = ix; p1[1] = actScale * (v[ix][iy] + actOffset); p1[2] = iy; + p2[0] = ix+1; p2[1] = actScale * (v[ix+1][iy] + actOffset); p2[2] = iy; + p3[0] = ix+1; p3[1] = actScale * (v[ix+1][iy+1] + actOffset); p3[2] = iy+1; + p4[0] = ix; p4[1] = actScale * (v[ix][iy+1] + actOffset); p4[2] = iy+1; + + // n1[0] = -(p2[1] - p1[1])*(p3[2] - p1[2]) + (p2[2] - p1[2])*(p3[1] - p2[1]); + // n1[1] = -(p2[2] - p1[2])*(p3[0] - p2[0]) + (p2[0] - p1[0])*(p3[2] - p2[2]); + // n1[2] = -(p2[0] - p1[0])*(p3[1] - p2[1]) + (p2[1] - p1[1])*(p3[0] - p2[0]); + CalculateVectorNormal (p1, p2, p4, &n1[0], &n1[1], &n1[2]); + //CalculateVectorNormal (p2, p1, p3, &n2[0], &n2[1], &n2[2]) + //CalculateVectorNormal (p3, p2, p4, &n1[0], &n1[1], &n1[2]) + double dIntensity1, dIntensity2, dIntensity3, dIntensity4; + if (m_bColor) { + dIntensity1 = (v[ix][iy] - dMin) / dIntensityScale; + dIntensity2 = (v[ix+1][iy] - dMin) / dIntensityScale; + dIntensity3 = (v[ix+1][iy+1] - dMin) / dIntensityScale; + dIntensity4 = (v[ix][iy+1] - dMin) / dIntensityScale; + } + float vecColor[3]; + if (m_bColor) { + intensityToColor (dIntensity1, vecColor); + glColor3fv (vecColor); + } + glVertex3fv (p1); glNormal3fv (n1); + if (m_bColor) { + intensityToColor (dIntensity2, vecColor); + glColor3fv (vecColor); + } + glVertex3fv (p2); glNormal3fv (n1); + if (m_bColor) { + intensityToColor (dIntensity3, vecColor); + glColor3fv (vecColor); + } + glVertex3fv (p3); glNormal3fv (n1); + if (m_bColor) { + intensityToColor (dIntensity4, vecColor); + glColor3fv (vecColor); + } + glVertex3fv (p4); glNormal3fv (n1); + glEnd(); + } + + } + glEndList(); + #ifdef GL_EXT_vertex_array } #endif @@ -258,10 +344,43 @@ Graph3dFileView::DrawSurface() void Graph3dFileView::OnProperties (wxCommandEvent& event) { - std::ostringstream os; - *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<\n"; - wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Imagefile Properties", wxOK | wxICON_INFORMATION); - dialogMsg.ShowModal(); + std::ostringstream os; + *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<\n"; + wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Imagefile Properties", wxOK | wxICON_INFORMATION); + dialogMsg.ShowModal(); +} + +void +Graph3dFileView::OnLighting (wxCommandEvent& event) +{ + m_bLighting = ! m_bLighting; + m_pViewMenu->Check (GRAPH3D_VIEW_LIGHTING, m_bLighting); + + m_pCanvas->Refresh(); +} + +void +Graph3dFileView::OnSurface (wxCommandEvent& event) +{ + m_bSurface = ! m_bSurface; + m_pViewMenu->Check (GRAPH3D_VIEW_SURFACE, m_bSurface); + m_pCanvas->Refresh(); +} + +void +Graph3dFileView::OnColor (wxCommandEvent& event) +{ + m_bColor = ! m_bColor; + m_pViewMenu->Check (GRAPH3D_VIEW_COLOR, m_bColor); + m_pCanvas->Refresh(); +} + +void +Graph3dFileView::OnSmooth (wxCommandEvent& event) +{ + m_bSmooth = ! m_bSmooth; + m_pViewMenu->Check (GRAPH3D_VIEW_SMOOTH, m_bSmooth); + m_pCanvas->Refresh(); } @@ -274,6 +393,9 @@ Graph3dFileView::OnDraw (wxDC* dc) #endif Draw(); + std::ostringstream os; + os << "Xangle=" << m_dXRotate << ", Yangle=" << m_dYRotate << ", Zangle=" << m_dZRotate; + m_statusBar.SetStatusText (os.str().c_str()); m_pCanvas->SwapBuffers(); } @@ -293,38 +415,134 @@ Graph3dFileView::Draw () void Graph3dFileView::InitMaterials() { + if (! GetDocument()) + return; + int nx = GetDocument()->nx(); + int ny = GetDocument()->ny(); + +#if 1 static float ambient[] = {0.1, 0.1, 0.1, 1.0}; - static float diffuse[] = {0.5, 1.0, 1.0, 1.0}; - static float position0[] = {0.0, 0.0, 20.0, 0.0}; - static float position1[] = {0.0, 0.0, -20.0, 0.0}; - static float front_mat_shininess[] = {60.0}; - static float front_mat_specular[] = {0.2, 0.2, 0.2, 1.0}; - static float front_mat_diffuse[] = {0.5, 0.28, 0.38, 1.0}; + static float diffuse[] = {1.0, 1.0, 1.0, 1.0}; + static float position0[] = {0, 0, -nx/4, 0, 0.0}; + static float position1[] = {nx/2, ny/2, nx/4, 0.0}; + // static float position0[] = {0.0, 0.0, 20.0, 0.0}; + // static float position1[] = {0.0, 0.0, -20.0, 0.0}; + static float front_mat_shininess[] = {5.0}; + static float front_mat_specular[] = {0.1, 0.1, 0.1, 1.0}; + static float front_mat_diffuse[] = {0.3, 0.3, 0.3, 1.0}; /* static float back_mat_shininess[] = {60.0}; - static float back_mat_specular[] = {0.5, 0.5, 0.2, 1.0}; - static float back_mat_diffuse[] = {1.0, 1.0, 0.2, 1.0}; + static float back_mat_specular[] = {0.2, 0.2, 0.2, 1.0}; + static float back_mat_diffuse[] = {1.0, 1.0, 1.0, 1.0}; */ static float lmodel_ambient[] = {1.0, 1.0, 1.0, 1.0}; static float lmodel_twoside[] = {GL_FALSE}; - glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); - glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); - glLightfv(GL_LIGHT0, GL_POSITION, position0); - glEnable(GL_LIGHT0); + glLightfv (GL_LIGHT0, GL_AMBIENT, ambient); + glLightfv (GL_LIGHT0, GL_DIFFUSE, diffuse); + glLightfv (GL_LIGHT0, GL_POSITION, position0); + glEnable (GL_LIGHT0); - glLightfv(GL_LIGHT1, GL_AMBIENT, ambient); - glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse); - glLightfv(GL_LIGHT1, GL_POSITION, position1); - glEnable(GL_LIGHT1); + glLightfv (GL_LIGHT1, GL_AMBIENT, ambient); + glLightfv (GL_LIGHT1, GL_DIFFUSE, diffuse); + glLightfv (GL_LIGHT1, GL_POSITION, position1); + glEnable (GL_LIGHT1); + + glLightModelfv (GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); + glLightModelfv (GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside); + + glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, front_mat_shininess); + glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, front_mat_specular); + glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, front_mat_diffuse); + + glColorMaterial (GL_FRONT_AND_BACK, GL_DIFFUSE); + // glColorMaterial (GL_FRONT_AND_BACK, GL_SPECULAR); + glEnable(GL_COLOR_MATERIAL); +#else + GLfloat impLPos[] = {1.0, 1.0, 1.0, 0.0}; + + GLfloat defaultLightAmb [] = {.2, .2, .2, 1.0}; + GLfloat defaultLightDiff [] = {.2, .2, .2, 1.0}; + GLfloat defaultLightSpec [] = { .3, .3, .3, 1.0}; + + GLfloat defaultGlobalAmb [] = {.3, .3, .3, 1.0}; + GLfloat defaultGlobalDiff[] = {.3, .3, .3, 1.0}; + + GLfloat defaultMatShine[] = { 30.0 }; + GLfloat defaultMatSpec[] = { .4, .4, .4, 1.0}; + GLfloat defaultMatAmb[] = { .3, .3, .3, 1.0}; + GLfloat defaultMatDiff[] = { .5, .5, .5, 1.0}; + + GLfloat brassMatAmb[] = { .33, .22, .03, 1.0}; + GLfloat brassMatDiff[] = { .78, .57, .11, 1.0}; + GLfloat brassMatSpec[] = { .99, .91, .81, 1.0}; + GLfloat brassMatShine[] = { 27.8 }; + + GLfloat emeraldMatAmb[] = { .021, .1745 , .021, 1.0}; + GLfloat emeraldMatDiff[] = { .075, .6142 , .075, 1.0 }; + GLfloat emeraldMatSpec[] = { .633, .7278 , .633, 1.0 }; + GLfloat emeraldMatShine[] = { 76.8 }; + + GLfloat slateMatAmb[] = { .02, .02 , .02, 1.0 }; + GLfloat slateMatDiff[] = { .02, .01 , .01, 1.0 }; + GLfloat slateMatSpec[] = { .4, .4 , .4 , 1.0 }; + GLfloat slateMatShine[] = { .768 }; + + // double opnX = nx, opnY = ny, opnZ = z; + // eyeX = 1; eyeY = 1, eyeZ = 1; + + impLPos[0] = nx/2.; impLPos[1]= ny/2.; impLPos[2] = 0.; + //opnListNum = 1; + //impGraphicsFlag = IMP__3D; + + // glutInitDisplayMode (GLUT_DOUBLE| GLUT_RGB | GLUT_DEPTH | GLUT_ACCUM); + // glutInitWindowSize (IMP_WIN_X, IMP_WIN_Y); + // glutInitWindowPosition (100, 100); + // glutCreateWindow ("- imp3D graphics -" ); + + glClearColor(0.0, 0.0, 0.0, 0.0); + + glShadeModel (GL_SMOOTH); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glHint(GL_LINE_SMOOTH, GL_DONT_CARE); + glEnable(GL_NORMALIZE); + + + glEnable(GL_DEPTH_TEST); + + glLightfv(GL_LIGHT0, GL_AMBIENT, defaultLightAmb); + glLightfv(GL_LIGHT0, GL_DIFFUSE, defaultLightDiff); + glLightfv(GL_LIGHT0, GL_SPECULAR,defaultLightSpec); + + glLightfv(GL_LIGHT1, GL_AMBIENT, defaultLightAmb); + glLightfv(GL_LIGHT1, GL_DIFFUSE, defaultLightDiff); + glLightfv(GL_LIGHT1, GL_SPECULAR,defaultLightSpec); + + glLightfv(GL_LIGHT2, GL_AMBIENT , defaultLightAmb); + glLightfv(GL_LIGHT2, GL_DIFFUSE , defaultLightDiff); + glLightfv(GL_LIGHT2, GL_SPECULAR, defaultLightSpec); + + glLightfv(GL_LIGHT0, GL_POSITION,impLPos); + glLightfv(GL_LIGHT1, GL_POSITION,impLPos); + glLightfv(GL_LIGHT2, GL_POSITION,impLPos); + + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT , defaultMatAmb); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE , defaultMatDiff); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR , defaultMatSpec); + glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, defaultMatShine); + + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, defaultGlobalAmb); + + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + + glEnable(GL_COLOR_MATERIAL); - glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); - glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside); glEnable(GL_LIGHTING); + glEnable(GL_LIGHT1); + glEnable(GL_LIGHT2); + glEnable(GL_LIGHT0); +#endif - glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_mat_shininess); - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_mat_specular); - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, front_mat_diffuse); } @@ -333,17 +551,8 @@ Graph3dFileView::InitGL () { glClearColor(0.0, 0.0, 0.0, 0.0); - glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); - InitMaterials(); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho (-300, 300, -300, 300, 200, -200); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - } void @@ -352,34 +561,19 @@ Graph3dFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) ) int nVertices = GetDocument()->m_nVertices; glTripleFloat* pVertices = GetDocument()->m_pVertices; glTripleFloat* pNormals = GetDocument()->m_pNormals; - -#if 0 - const ImageFile& rIF = GetDocument()->getImageFile(); - ImageFileArrayConst v = rIF.getArray(); - int nx = rIF.nx(); - int ny = rIF.ny(); - if (v != NULL && nx != 0 && ny != 0) { - unsigned char* imageData = new unsigned char [nx * ny * 3]; - for (int ix = 0; ix < nx; ix++) { - for (int iy = 0; iy < ny; iy++) { - double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255; - int intensity = static_cast(scaleValue + 0.5); - intensity = clamp (intensity, 0, 255); - int baseAddr = ((ny - 1 - iy) * nx + ix) * 3; - imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity; - } - } - wxImage image (nx, ny, imageData, true); - m_bitmap = image.ConvertToBitmap(); - delete imageData; - int xSize = nx; - int ySize = ny; - ySize = clamp (ySize, 0, 800); - m_pFrame->SetClientSize (xSize, ySize); - m_pCanvas->SetScrollbars(20, 20, nx/20, ny/20); - m_pCanvas->SetBackgroundColour(*wxWHITE); - } -#endif + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + if (! GetDocument()) + return; + int nx = GetDocument()->nx(); + int ny = GetDocument()->ny(); + int maxDim = maxValue (nx, ny); + + glOrtho (-maxDim * 0.71, maxDim * 0.71, -maxDim * 0.71, maxDim * 0.71, maxDim * 0.71, -maxDim * 0.71); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + #ifdef GL_EXT_vertex_array if (m_bUseVertexArrays) { @@ -389,6 +583,7 @@ Graph3dFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) ) glEnable( GL_NORMAL_ARRAY_EXT ); } #endif + if (m_pCanvas) m_pCanvas->Refresh(); } @@ -434,13 +629,14 @@ Graph3dFileView::CreateChildFrame (wxDocument *doc, wxView *view) #endif theApp->setIconForFrame (subframe); + m_statusBar.Create (subframe, -1); + subframe->SetStatusBar (&m_statusBar); + m_pFileMenu = new wxMenu; m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P"); m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F"); m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O"); - m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S"); - m_pFileMenu->Append(wxID_SAVEAS, "Save &As..."); m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W"); m_pFileMenu->AppendSeparator(); @@ -457,6 +653,12 @@ Graph3dFileView::CreateChildFrame (wxDocument *doc, wxView *view) GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu); GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu); + m_pViewMenu = new wxMenu; + m_pViewMenu->Append(GRAPH3D_VIEW_SURFACE, "&Surface\tCtrl-U", "", true); + m_pViewMenu->Append(GRAPH3D_VIEW_SMOOTH, "&Smooth\tCtrl-M", "", true); + m_pViewMenu->Append(GRAPH3D_VIEW_COLOR, "&Color\tCtrl-R", "", true); + m_pViewMenu->Append(GRAPH3D_VIEW_LIGHTING, "&Lighting\tCtrl-L", "", true); + wxMenu *help_menu = new wxMenu; help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1"); help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H"); @@ -465,6 +667,7 @@ Graph3dFileView::CreateChildFrame (wxDocument *doc, wxView *view) wxMenuBar *menu_bar = new wxMenuBar; menu_bar->Append(m_pFileMenu, "&File"); + menu_bar->Append(m_pViewMenu, "&View"); menu_bar->Append(help_menu, "&Help"); subframe->SetMenuBar(menu_bar); @@ -473,16 +676,15 @@ Graph3dFileView::CreateChildFrame (wxDocument *doc, wxView *view) wxAcceleratorEntry accelEntries[10]; accelEntries[0].Set (wxACCEL_CTRL, static_cast('O'), wxID_OPEN); - accelEntries[1].Set (wxACCEL_CTRL, static_cast('S'), wxID_SAVE); - accelEntries[2].Set (wxACCEL_CTRL, static_cast('W'), wxID_CLOSE); - accelEntries[3].Set (wxACCEL_CTRL, static_cast('H'), MAINMENU_HELP_TOPICS); - accelEntries[4].Set (wxACCEL_CTRL, static_cast('P'), MAINMENU_FILE_CREATE_PHANTOM); - accelEntries[5].Set (wxACCEL_CTRL, static_cast('F'), MAINMENU_FILE_CREATE_FILTER); - accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS); - accelEntries[7].Set (wxACCEL_CTRL, static_cast('A'), IFMENU_VIEW_SCALE_AUTO); - accelEntries[8].Set (wxACCEL_CTRL, static_cast('U'), IFMENU_VIEW_SCALE_FULL); - accelEntries[9].Set (wxACCEL_CTRL, static_cast('E'), IFMENU_VIEW_SCALE_MINMAX); - wxAcceleratorTable accelTable (10, accelEntries); + accelEntries[1].Set (wxACCEL_CTRL, static_cast('H'), MAINMENU_HELP_TOPICS); + accelEntries[2].Set (wxACCEL_CTRL, static_cast('P'), MAINMENU_FILE_CREATE_PHANTOM); + accelEntries[3].Set (wxACCEL_CTRL, static_cast('F'), MAINMENU_FILE_CREATE_FILTER); + accelEntries[4].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS); + accelEntries[5].Set (wxACCEL_CTRL, static_cast('U'), GRAPH3D_VIEW_SURFACE); + accelEntries[6].Set (wxACCEL_CTRL, static_cast('R'), GRAPH3D_VIEW_COLOR); + accelEntries[7].Set (wxACCEL_CTRL, static_cast('L'), GRAPH3D_VIEW_LIGHTING); + accelEntries[8].Set (wxACCEL_CTRL, static_cast('M'), GRAPH3D_VIEW_SMOOTH); + wxAcceleratorTable accelTable (9, accelEntries); subframe->SetAcceleratorTable (accelTable); return subframe; @@ -546,7 +748,8 @@ Graph3dFileCanvas::OnChar(wxKeyEvent& event) if (! m_pView) return; - switch(event.KeyCode()) { + wxCommandEvent dummyEvent; + switch (event.KeyCode()) { case WXK_LEFT: m_pView->m_dYRotate -= 15.0; break; @@ -559,21 +762,20 @@ Graph3dFileCanvas::OnChar(wxKeyEvent& event) case WXK_DOWN: m_pView->m_dXRotate -= 15.0; break; + case 'z': case 'Z': + m_pView->m_dZRotate += 15.0; + break; + case 'x': case 'X': + m_pView->m_dZRotate -= 15.0; + break; case 's': case 'S': - m_pView->m_bSmooth = !m_pView->m_bSmooth; - if (m_pView->m_bSmooth) { - glShadeModel(GL_SMOOTH); - } else { - glShadeModel(GL_FLAT); - } + m_pView->OnSmooth (dummyEvent); break; case 'l': case 'L': - m_pView->m_bLighting = !m_pView->m_bLighting; - if (m_pView->m_bLighting) { - glEnable(GL_LIGHTING); - } else { - glDisable(GL_LIGHTING); - } + m_pView->OnLighting (dummyEvent); + break; + case 'c': case 'C': + m_pView->OnColor (dummyEvent); break; default: { @@ -598,8 +800,11 @@ Graph3dFileCanvas::OnMouseEvent(wxMouseEvent& event) static int dragging = 0; static float last_x, last_y; + if (! m_pView) + return; + if(event.LeftIsDown()) { - if(!dragging) { + if(! dragging) { dragging = 1; } else { m_pView->m_dXRotate += (event.GetX() - last_x)*1.0; diff --git a/src/graph3dview.h b/src/graph3dview.h index 6ae6e78..1cb68cf 100644 --- a/src/graph3dview.h +++ b/src/graph3dview.h @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: graph3dview.h,v 1.2 2001/01/30 10:58:13 kevin Exp $ +** $Id: graph3dview.h,v 1.3 2001/02/02 00:46:38 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -46,12 +46,18 @@ private: DECLARE_EVENT_TABLE() wxMenu* m_pFileMenu; + wxMenu *m_pViewMenu; + wxStatusBar m_statusBar; + GLfloat m_dXRotate; GLfloat m_dYRotate; - GLboolean m_bUseVertexArrays; - GLboolean m_bDoubleBuffer; - GLboolean m_bSmooth; - GLboolean m_bLighting; + GLfloat m_dZRotate; + bool m_bUseVertexArrays; + bool m_bDoubleBuffer; + bool m_bSmooth; + bool m_bLighting; + bool m_bSurface; + bool m_bColor; void Draw(); void DrawSurface(); @@ -87,13 +93,17 @@ public: void OnUpdate(wxView *sender, wxObject *hint = NULL); bool OnClose (bool deleteWindow = true); void OnProperties (wxCommandEvent& event); + void OnLighting (wxCommandEvent& event); + void OnSurface (wxCommandEvent& event); + void OnColor (wxCommandEvent& event); + void OnSmooth (wxCommandEvent& event); #if CTSIM_MDI wxDocMDIChildFrame* getFrame() { return m_pFrame; } #else wxDocChildFrame* getFrame() { return m_pFrame; } #endif - + Graph3dFileCanvas* getCanvas() { return m_pCanvas; } Graph3dFileDocument* GetDocument() { return dynamic_cast(wxView::GetDocument()); } }; diff --git a/src/views.cpp b/src/views.cpp index ba400e7..9352b4c 100644 --- a/src/views.cpp +++ b/src/views.cpp @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: views.cpp,v 1.94 2001/01/31 01:01:22 kevin Exp $ +** $Id: views.cpp,v 1.95 2001/02/02 00:46:38 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -1125,8 +1125,13 @@ ImageFileView::OnConvert3d (wxCommandEvent& event) Graph3dFileDocument* pGraph3d = theApp->newGraph3dDoc(); pGraph3d->setBadFileOpen(); pGraph3d->createFromImageFile (rIF); - pGraph3d->getView()->getFrame()->SetClientSize (200, 200); + pGraph3d->getView()->OnUpdate (this, NULL); + pGraph3d->UpdateAllViews(); + pGraph3d->getView()->getFrame()->SetClientSize (400, 400); pGraph3d->getView()->getFrame()->Show (true); + GetDocumentManager()->ActivateView (pGraph3d->getView(), true, false); + ::wxYield(); + pGraph3d->getView()->getCanvas()->SetFocus(); } #endif