...
 
Commits (83)
......@@ -10,6 +10,8 @@ DataOpacity.cc
DataOpacity.h
DeadDataGUI.cc
DeadDataGUI.h
FlyBy.cc
FlyBy.h
HDF5DataBrowser.cc
HDF5DataBrowser.h
HiLiteWinInfo.cc
......@@ -25,3 +27,4 @@ TimeLink.h
trimesh.cc
trimesh.h
.DS_Store
doc/
stages:
- build
- test
- release
variables:
TAG: ${CI_COMMIT_TAG}
# PDF manual
PDF_MANUAL_MAKER_IMAGE: docker.opencarp.org/opencarp/opencarp/manual-maker:latest
PDF_MANUAL: meshalyzer-manual-latest.pdf
PDF_MANUAL_RELEASE: meshalyzer-manual-${CI_COMMIT_TAG}.pdf
PDF_MANUAL_RELEASE_URL: https://opencarp.org/manual/meshalyzer-manual-${CI_COMMIT_TAG}.pdf
PDF_MANUAL_DESTINATION: 'opencarp@sulmass.scc.kit.edu:/var/www/manual/'
# Releases
RELEASE_API_URL: https://git.opencarp.org/api/v4/projects/${CI_PROJECT_ID}/releases
RELEASE_ARCHIVE_URL: https://git.opencarp.org/openCARP/meshalyzer/-/archive/${CI_COMMIT_TAG}/meshalyzer-${CI_COMMIT_TAG}.tar.gz
RELEASE_DESCRIPTION: |
Meshalyzer is a graphical program for display time dependent data on 3D finite elment meshes. <br>
Find the changelog [here](https://git.opencarp.org/openCARP/meshalyzer/blob/master/CHANGELOG.md).
include:
- local: .gitlab/ci/pdf.gitlab-ci.yml
- local: .gitlab/ci/release.gitlab-ci.yml
build-pdf-manual:
stage: build
image: $PDF_MANUAL_MAKER_IMAGE
only:
- tags
script:
- cd manual && pdflatex manual && pdflatex manual
artifacts:
paths:
- manual/manual.pdf
expire_in: 2 hour
release-pdf-manual:
stage: release
image: ubuntu:18.04
dependencies:
- build-pdf-manual
only:
- tags
before_script:
# setup rsync
- apt-get update && apt-get install -y openssh-client rsync
- eval $(ssh-agent -s)
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_PRIVATE_KEY" | base64 -d | ssh-add -
- ssh-keyscan -H sulmass.scc.kit.edu > ~/.ssh/known_hosts
script:
- cp manual/manual.pdf $PDF_MANUAL
- "rsync -av --delete $PDF_MANUAL $PDF_MANUAL_DESTINATION/"
- >
if [ "$CI_COMMIT_TAG" != "" ]; then
cp manual/manual.pdf $PDF_MANUAL_RELEASE &&
rsync -av --delete $PDF_MANUAL_RELEASE $PDF_MANUAL_DESTINATION/
fi
release-create:
stage: release
image: python:3.7
only:
- tags
except:
- branches
before_script:
# setup pipelines
- git clone https://opencarp-admin:${PRIVATE_TOKEN}@git.opencarp.org/infrastructure/pipelines.git
- pip install -r pipelines/requirements.txt
script:
- >
python pipelines/scripts/create_release.py
$PDF_MANUAL_RELEASE_URL
......@@ -6,8 +6,18 @@ meshalyzer uses a vMAJOR.PATCH versioning scheme. We increase the
* MAJOR version when we add functionality, and the
* PATCH version when we make backwards compatible bug fixes
## [2.1] - 2020-11-18
- vertices by default are now only drawn on displayed surfaces and connections
- auxilliary grids now have dead data
- new colour map Kindlmann
- threaded data reader replaced with on-demand reader
- display of element-based data
- can flip surface normals
- lighting effects for 2D points
- compiles with VTK9
- many bug fixes and things working as advertised
## [2.0] - 2020-28-06
## [2.0] - 2020-06-28
- manipulation of meshes improved by completely rewriting rendering to use Vertex Buffer Objects
- Lighting
- lighting direction controlled by trackball
......@@ -26,7 +36,9 @@ meshalyzer uses a vMAJOR.PATCH versioning scheme. We increase the
- more PNG metadata added for auxgrids and vector data
- axes drawn as coloured 3D cylinders
- vertex selection only possible when vertices are displayed
- Purple-orange divergent colour scale added
- Colour scales added
- divergent Purple-orange
- cyclic Twilight
## [1.0] - 2020-23-06
- Initial and final release for immediate mode OpenGL rendering (pre 3.3)
This diff is collapsed.
......@@ -5,35 +5,124 @@ Meshalyzer is regularly used on Linux and Mac machines. It has worked under Wind
## Prerequisities
1. [libpng](http://libpng.org)
2. [OpenMP](http://openmp.org) is recommended too improve certain operations. For all compilers except *clang* supplied by Apple. A simple way to include it on Apple machines is with homebrew:
2. [OpenMP](http://openmp.org) is recommended too improve certain operations. For all compilers, except *clang* supplied by Apple, it is standard. A simple way to include it on Apple machines is with homebrew:
`brew install libomp`
2. [FLTK 1.3.x](http://www.fltk.org/) or higher. Manually downloadad and installed is better since the prepackaged versions often have issues with fltk-config.
3. [glew](http://glew.sourceforge.net) for Linux and Windows only.
2. [glut](https://www.opengl.org/resources/libraries/glut/) for Linux and Windows only.
3. To make the "windowless" rendering version, [OSMesa](https://www.mesa3d.org/osmesa.html)
3. [cmake](https://cmake.org) if you want to make compiling easier.
3. <strike>To make the "windowless" rendering version, [OSMesa](https://www.mesa3d.org/osmesa.html)</strike>
4. To read in VTK/VTU format meshes, install [VTK](https://www.vtk.org/) development files
Except for fltk, using your package manager is recommended for installing these packages if possible. On *MacOS*, [homebrew](https://brew.sh/) works for me.
Except for fltk, using your package manager is recommended for installing these packages if possible. On *MacOS*, [homebrew](https://brew.sh/) works for us.
## Compiling
Clone the repository and enter the codebase folder `meshalyzer`.
```
git clone https://git.opencarp.org/openCARP/meshalyzer.git
cd meshalyzer
```
We provide Makefiles and CMake files for compiling the code, choose the one that fits your workflow.
### Makefile
1. If VTK is installed, edit the **make.conf** to set the `VTK_INC` and `VTK_DIR` variables to point to the directories containing the VTK header files and libraries, respectively. Comment them out otherwise.
1. If you have an Apple machine, edit **make.conf** to make sure that
2. If you have an Apple machine, edit **make.conf** to make sure that
*HAVE_RETINA* is correct.
1. Type `make -j`
3. Type `make -j`
4. <strike>For the windowless version, type `make mesalyzer`</strike>
5. The compiled executables will be found in the directory.
6. Copy the binaries to a suitable location
### CMake
1. We suggest to run CMake by out-of-source builds, which means all the compiled code goes into a `_build` directory separate to the source code.
2. For the windowless version, type `make mesalyzer`
2. If VTK is installed, type `cmake -S. -B_build`. Otherwise type `cmake -S. -B_build -DUSE_VTK=OFF`
3. The compiled executables will be found in the directory.
3. Compile the code via `cmake --build _build`
3. Copy the binaries to a suitable location
4. In the main meshalyzer directory, you will find a link to the compiled executalbe.
### ccmake
For those who are familiar with [ccmake](https://cmake.org/cmake/help/v3.0/manual/ccmake.1.html):
1. `ccmake .`
2. configure, edit and generate
3. make -j
## Documentation
To compile (LaTeX required)
To compile the manual (LaTeX required)
1. `cd manual`
2. `pdflatex manual`
3. `pdflatex manual`
## MacOS
First of all, the [command line tools](https://developer.apple.com/download/more/?=command%20line%20tools) or [Xcode](https://developer.apple.com/xcode/) must be installed.
Next, install the [prerequisities](#prerequisities) using your package manager. We use [homebrew](https://brew.sh/) as an example.
```
brew install libpng
brew install fltk
brew install git
brew install cmake
```
Follow the steps in the [Compiling](#compiling) to build the code.
## Ubuntu/Debian:
For installing meshalyzer first you need to install the ["Prerequisites"](#prerequisities). In Ubuntu you can use:
```
apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
fluid \
freeglut3-dev \
g++ \
git \
libfltk1.3-dev \
libglew-dev \
libglu1-mesa-dev \
libpng-dev \
make
```
Follow the steps in [Compiling](#compiling) to build the code.
## Post-compilation
You can execute meshalyzer by typing `./meshalyzer`
See README.md for an example.
To make meshalyzer available from anywhere on your machine, you need to copy it to your *bin* directory, or
add the meshalyzer directory to **PATH** by editing your *.bashrc* (or *.bash_profile*).
Using the nano editor as an example, type the following command into the terminal:
```
nano ~/.bashrc
```
This will open an editor, scroll all the way down and add the following line, replacing '/home/somefolder/anotherfolder/meshalyzer' with the path of your meshalyzer folder:
```
export PATH=$PATH:/home/somefolder/anotherfolder/meshalyzer
```
press Control+X, then type Y and Enter to save the changes. Close all terminal instances!
open a terminal and type `meshalyzer`
......@@ -3,6 +3,10 @@
Meshalyzer is a graphical program for display time dependent data on 3D finite elment meshes. Of course, it can show static data on fewer dimensions as well.
It is developed to be compatible with the cardiac simulation environment [openCARP](http://www.opencarp.org).
<p align="center">
<img src="examples.png" width="1000" title="output examples" alt="examples">
</p>
It uses its own file formats which are simple and easily converted to/from more popular formats like VTK.
## Features
......@@ -18,6 +22,10 @@ It uses its own file formats which are simple and easily converted to/from more
* display auxiliary grids
* fly through model views
## Installation
Please follow steps in the [INSTALL](INSTALL.md) file.
## VTK formats
Support for reading the VTK formats, the legacy format (.vtk) as well the unstructured grid format (.vtu), has been included. You need to edit the **make.conf** manually to specify the directory containing the dynamically linked libraries (`VTK_LIBDIR`) and the directory with the VTK header files (`VTK_INCDIR`). They
......@@ -48,7 +56,7 @@ Under Linux and Windows, rendering has been improved to take advantage of modern
This will affect chiefly lighting and rendering speed. If you are running on an Applre machine there is one additional consideration.
If your screen is a Retine display, make sure that the following is set in *make.conf*:
HAVE_RETINE := 1
HAVE_RETINA := 1
If it is not a retina screen, set this variable to 0. You will know if this setting is correct because if not, either only 1/4 of the window will be used, or you will only see one quarter of the model.
......
......@@ -327,23 +327,20 @@ first_vertex_in_region1 last_vertex_in_region1
first_vertex_in_regionN last_vertex_in_regionN
\end{verbatim}
\subsection{Vertex Scalar Data}
Scalar data can be associated with each vertex.
It can be time dependent or time independent.
\subsection{Scalar Data}
Scalar data can be associated with each vertex or with each element.
although not all operations may be available for elementally defined data.
Data can be time dependent or time independent.
\subsubsection{Time Independent}
\subsubsection{ASCII format}
A data file typically has the extension \lit{.dat} or \lit{.out} but these extensions
are suggested and not required.
The file simply contains lines with one entry per line, the data value for the
vertex.
\subsubsection{Time Dependent}
Files with time dependent data are typically suffixed with \lit{.tdat} but this
extension is
not mandatory.
The file format follows that of the Static case, but is repeated for every time
instance.
Thus if there are N vertices and data is sampled at 10 points in time,
the file will comprise 10N lines with one value per line.
vertex or element.
There is no separation between time frames, so if there are N points or elements, and 10 times,
there will be 10N lines in the file.
The data is interpreted as vertex data if the number of lines in the file is a multiple of the
number of vertices, or the number of lines is not a multiple of the number of elements.
\subsubsection{IGB format}
\label{sec:IGB}
......@@ -351,7 +348,7 @@ IGB is a format developed at the University of Montreal and used in a
visualization program called flounder, for regularly spaced data.
The file is composed of a 1024-byte long header followed by binary data.
For irregular grids, the individual dimensions are meaningless but xdim$\times$ ydim$\times$ zdim should be equal to the number of vertices.
For irregular grids, the individual dimensions are meaningless but xdim$\times$ ydim$\times$ zdim should be equal to the number of vertices/elements.
The header is composed of strings of the format below, separated by
white space.
\centerline{ \texttt{KeyWord:value} }
......@@ -583,8 +580,8 @@ Set the environment variable \texttt{OMP\_NUM\_THREADS} to a value that works fo
\item[--frame=\emph{int}] first frame for PNG output (-1=do not set)
\item[--numframe=\emph{int}] number of frames to output to PNG images (default=1,-1=all)
\item[--size=\emph{int}] width and height of PNG in pixels (default=512)
\item[--compSurf=\emph{[[+]file]}] compute and write boundary surfaces to \lit{file}. If \lit{file} is not
specified, use the model name. Specify \lit{+} to NOT exit after writing file.
\item[--compSurf=\emph{[+][file]}] compute and write boundary surfaces to \lit{file}. If \lit{file} is not
specified, use the model name. Specify \lit{+} to NOT exit immediately after writing file.
To not write a file, specify \lit{file} as \lit{/dev/null}.
\end{description}
......@@ -651,7 +648,7 @@ Pressing the \lit{props} button will open a widget to specify
colour when it is not displaying data, to specify the output stride, size
and if a 3D effect is to be used.
\textbf{Note that the 3D effect is memory intensive and may cause a segmenation violation
for a large number of points}.
for a large number of objects}.
Different structures in different layers can be coloured differently.
Under \lit{Image/Randomly colour}, one can select which structure types gets randomly
......@@ -681,11 +678,10 @@ type for the currently selected shell (0 if \emph{All} is chosen).
\label{para:pickvertex}
The highlighted vertex can also be selected with the mouse.
\emph{Vertices must be displayed to activate this button.}
Clicking on the \lit{Pick Vertex} button will enter vertex picking mode
with the button turning red to indicate this.
A vertex can then be selected by clicking on it.
If you are having trouble selecting a vertex, zoom in and click on its center.
If you are having trouble making a selection, display vertices, zoom in and click on a vertex center.
The clicking area for a vertex is that of a vertex drawn with a size of 10 (non-3D).
Three attempts are allowed to click on a vertex before picking mode is turned off.
Note that the highlighted node can be changed by using the mouse wheel (as explained above).
......@@ -880,10 +876,12 @@ The opacity value selected under the surface colour applies when data is present
Lighting properties can also be set for individual surfaces.
\lit{Specular} and \lit{diffusive} are as explained in \S\ref{sec:lighting}.
\lit{shiny} controls the exponent of the specular light with a higher number making highlights sharper.
\lit{back light controls intensity of backward facing polygons when backlighting is ``lit} (\autoref{sec:lighting})
\lit{back light} controls intensity of backward facing polygons when backlighting is ``lit'' (\autoref{sec:lighting})
Surfaces can also be randomly coloured by Image/Randomly Color/Surfaces.
Surfaces are renamed with the \lit{Rename} button. You will be prompted if you
want to overwrite the surface file with the new label.
The normals of a surface can also be flipped by selecting \lit{flip/normals}.
\subsection{Image Menu}
......@@ -904,7 +902,6 @@ types chosen.
Select the background colour.
\subsubsection{Reverse draw order}
Draw the surfaces in the opposite order. This can be useful when surfaces
......
......@@ -5,6 +5,8 @@
* @version
* @date 2018-09-18
*/
#ifndef ASCII_READER_HPP
#define ASCII_READER_HPP
#include <stdio.h>
#include <errno.h>
......@@ -137,4 +139,4 @@ class AsciiReader {
}
};
#endif
......@@ -422,7 +422,7 @@ private:
*
* \throw 1 if any error in input
*/
AuxGrid::AuxGrid( const char *fn, AuxGrid *ag, float t0, float dt ):_dt(dt),_t0(t0)
AuxGrid::AuxGrid( const char *fn, TBmeshWin *tbmw, AuxGrid *ag, float t0, float dt ):_dt(dt),_t0(t0)
{
if( strstr( fn, ".pts_t" ) )
_indexer = new AuxGridIndexer(fn);
......@@ -490,6 +490,8 @@ AuxGrid::AuxGrid( const char *fn, AuxGrid *ag, float t0, float dt ):_dt(dt),_t0(
_rd_pts.ambient = 0.5;
_rd_pts.diffuse = 1.;
_rd_pts.specular = 1.;
_deadData = new DeadDataGUI( tbmw, &cs, VBO_Aux );
}
......@@ -633,6 +635,7 @@ AuxGrid::~AuxGrid()
if( _timeplot )
delete _timeplot;
_timeplot = NULL;
if( _deadData ){ delete _deadData; }
}
void AuxGrid :: optimize_cs( int tm )
......
......@@ -12,6 +12,9 @@
#include "Model.h"
#include "DrawingObjects.h"
#include "plottingwin.h"
#include "DeadDataGUI.h"
class TBmeshWin;
class AuxGridFetcher;
......@@ -44,8 +47,8 @@ class AuxGrid {
GLfloat _mat_prop[4] = { 0.7, 0.7, 80, 1. };
string _absfile;
public:
AuxGrid( const char *fn, AuxGrid *ag=NULL, float t0=0, float dt=1 );
virtual ~AuxGrid();
AuxGrid( const char *fn, TBmeshWin *, AuxGrid *ag=NULL, float t0=0, float dt=1 );
~AuxGrid();
Colourscale cs;
void draw( int, unsigned int, void* );
......@@ -76,6 +79,7 @@ class AuxGrid {
_rd_pts.set_material(s,_mat_prop );_rd_lns.set_material(s,_mat_prop); }
GLfloat *get_mat() { return _mat_prop; }
string file(){ return _absfile; }
DeadDataGUI* _deadData = NULL;
};
#endif
......@@ -9,9 +9,16 @@ class ClipSlider : public Fl_Value_Slider
{
public:
ClipSlider( int x, int y, int w, int h, const char *L=0 ):Fl_Value_Slider(x,y,w,h,L){}
int handle( int e ){if(e==FL_DRAG){do_callback(this,(long)1);}
if(e==FL_RELEASE){do_callback(this,(long)0);}
return Fl_Value_Slider::handle(e);}
int handle( int e ){if(e==FL_DRAG){
do_callback(this,(long)1);
} else if(e==FL_RELEASE){
do_callback(this,(long)0);
} else if(e==FL_KEYDOWN && (Fl::event_key()==FL_Up||Fl::event_key()==FL_Down)){
do_callback(this,(long)0);
}
return Fl_Value_Slider::handle(e);
}
};
#endif
......@@ -22,6 +22,7 @@ void Colourscale :: calibrate( double tmin, double tmax )
}
}
const float twilight[][3] = {
{ 0.95588623, 0.91961077, 0.95812116 }, { 0.94353853, 0.91268927, 0.94824212 }, { 0.93140447, 0.90573033, 0.93856712 },
{ 0.91947392, 0.89873478, 0.92912752 }, { 0.90772105, 0.89170929, 0.91995838 }, { 0.8960938, 0.8846711, 0.91109116 },
......@@ -476,6 +477,75 @@ int inferno[][3] = {
{ 248, 251, 154 }, { 249, 252, 157 }, { 250, 253, 161 }, { 252, 255, 164 }
};
int Kindlmann[][3] = {
{0,0,0}, {5,0,4}, {9,0,8}, {13,1,13},
{17,1,17}, {20,1,20}, {22,1,23}, {25,1,26},
{27,1,30}, {28,2,33}, {30,2,37}, {31,2,40},
{32,2,44}, {33,2,48}, {34,2,51}, {34,3,55},
{35,3,58}, {36,3,62}, {36,3,65}, {36,3,68},
{37,3,71}, {37,4,74}, {38,4,77}, {38,4,80},
{39,4,83}, {39,4,86}, {40,4,88}, {40,4,91},
{41,4,93}, {42,5,96}, {42,5,98}, {43,5,101},
{44,5,103}, {44,5,106}, {44,5,109}, {44,5,113},
{43,6,118}, {41,6,123}, {37,6,130}, {30,7,137},
{15,7,147}, {7,14,146}, {7,21,141}, {6,26,135},
{6,31,130}, {6,34,125}, {6,37,120}, {5,40,115},
{5,43,110}, {5,45,106}, {5,47,102}, {5,49,98},
{4,51,94}, {4,53,91}, {4,54,88}, {4,56,85},
{4,57,82}, {4,59,80}, {4,60,77}, {4,61,75},
{4,63,73}, {3,64,71}, {3,65,70}, {3,66,68},
{3,68,67}, {3,69,65}, {3,70,64}, {3,71,62},
{3,72,61}, {4,73,59}, {4,74,58}, {4,76,56},
{4,77,55}, {4,78,53}, {4,79,51}, {4,80,50},
{4,81,48}, {4,82,46}, {4,84,44}, {4,85,42},
{4,86,40}, {4,87,38}, {4,88,36}, {4,89,34},
{4,90,32}, {4,91,30}, {5,93,28}, {5,94,26},
{5,95,24}, {5,96,22}, {5,97,20}, {5,98,18},
{5,99,16}, {5,100,15}, {5,101,14}, {5,102,14},
{5,104,13}, {5,105,13}, {5,106,12}, {5,107,11},
{5,108,10}, {5,109,8}, {5,110,6}, {9,111,5},
{15,112,5}, {20,113,5}, {24,114,5}, {29,115,6},
{34,115,6}, {38,116,6}, {43,117,6}, {47,118,6},
{51,118,6}, {56,119,6}, {60,119,6}, {64,120,6},
{69,120,6}, {73,121,6}, {77,121,6}, {82,122,6},
{86,122,6}, {90,122,6}, {95,123,6}, {99,123,6},
{103,123,6}, {107,123,6}, {111,124,6}, {115,124,6},
{120,124,6}, {124,124,6}, {128,124,6}, {133,124,6},
{138,123,7}, {143,123,7}, {148,122,7}, {153,122,7},
{159,121,8}, {164,120,8}, {170,118,8}, {176,117,8},
{183,115,9}, {189,113,9}, {196,111,9}, {202,108,10},
{209,105,10}, {216,102,10}, {223,98,11}, {230,94,11},
{237,89,11}, {243,84,12}, {244,86,27}, {245,88,36},
{245,89,44}, {245,91,50}, {245,93,55}, {246,95,60},
{246,98,64}, {246,100,67}, {246,102,70}, {246,104,73},
{246,106,76}, {247,107,81}, {247,109,85}, {247,111,91},
{247,112,96}, {248,114,102}, {248,115,108}, {248,117,115},
{248,118,121}, {249,120,127}, {249,121,134}, {249,123,140},
{249,124,146}, {249,125,152}, {249,127,158}, {249,128,164},
{249,130,170}, {249,131,175}, {249,132,181}, {249,133,186},
{249,135,191}, {249,136,196}, {249,137,201}, {249,139,206},
{249,140,211}, {250,141,216}, {250,142,220}, {250,144,224},
{250,145,229}, {250,146,233}, {250,148,236}, {250,149,240},
{250,150,244}, {250,152,248}, {249,154,250}, {246,157,250},
{243,161,250}, {241,164,251}, {239,167,251}, {237,170,251},
{235,172,251}, {233,175,251}, {232,177,251}, {231,179,251},
{230,181,251}, {229,183,252}, {228,185,252}, {228,187,252},
{227,189,252}, {227,191,252}, {227,192,252}, {227,194,252},
{227,196,252}, {227,197,252}, {227,199,252}, {228,200,252},
{228,202,252}, {228,203,253}, {229,205,253}, {229,206,253},
{230,208,253}, {231,209,253}, {231,210,253}, {232,212,253},
{233,213,253}, {233,214,253}, {234,216,253}, {234,217,253},
{234,219,253}, {234,220,253}, {235,222,253}, {235,224,253},
{235,225,254}, {235,227,254}, {235,228,254}, {235,230,254},
{235,231,254}, {235,233,254}, {235,234,254}, {236,236,254},
{236,237,254}, {236,239,254}, {236,240,254}, {237,242,254},
{237,243,254}, {238,245,254}, {238,246,254}, {239,247,254},
{240,249,254}, {241,250,254}, {242,251,254}, {244,252,254},
{246,253,255}, {248,254,255}, {251,255,255}, {255,255,255}
};
#define INTERP(a,b,c) ( (int) (((float) (a) / (float) (b)) * (c)) )
......@@ -735,6 +805,13 @@ void Colourscale :: scale( CScale_t cs )
cmap[i][2] = ((float) twilight[INTERP(i,n-1,255)][2] );
}
break;
case CS_KINDLMANN:
for ( i = 0; i < n; i++ ) {
cmap[i][0] = ((float) Kindlmann[INTERP(i,n-1,255)][0] / 255. );
cmap[i][1] = ((float) Kindlmann[INTERP(i,n-1,255)][1] / 255. );
cmap[i][2] = ((float) Kindlmann[INTERP(i,n-1,255)][2] / 255. );
}
break;
}
}
#undef INTERP
......
......@@ -20,7 +20,8 @@
typedef enum {
CS_HOT, CS_GREY, CS_RGREY, CS_GGREY, CS_BGREY, CS_RAINBOW, CS_BL_RAINBOW,
CS_COLD_HOT, CS_CG, CS_MATLAB_REV, CS_MATLAB, CS_ACID, CS_P2G, CS_VIRIDIS,
CS_VIRIDIS_LIGHT, CS_MAGMA, CS_INFERNO, CS_DISTINCT, CS_PuOr, CS_TWILIGHT
CS_VIRIDIS_LIGHT, CS_MAGMA, CS_INFERNO, CS_DISTINCT, CS_PuOr, CS_TWILIGHT,
CS_KINDLMANN
} CScale_t;
typedef enum {
......
......@@ -35,7 +35,7 @@ Connection::buffer( RenderLines& rd, const GLfloat *col, Colourscale* cs,
rd.buffer_line( cpubuff, nseg );
}
free( cpubuff );
if( translucent ) rd.opaque(0);
rd.opaque(translucent ? 0 : -1);
rd.update_nodalbuff();
}
......@@ -71,10 +71,10 @@ bool Connection::read( const char *fname )
for ( int i=0; i<2*_n; i+=2 ) {
gzgets(in, buff, bufsize);
sscanf( buff, "%d %d", _node+i, _node+i+1 );
if( start_ln>=0 && _node[i]!=_node[i-1] ){
if( start_ln>=0 && i && _node[i]!=_node[i-1] ){
_lines.push_back( make_pair(start_ln, i/2-1) );
start_ln = -1;
} else if( start_ln<0 && _node[i]==_node[i-1] )
} else if( start_ln<0 && i && _node[i]==_node[i-1] )
start_ln = i/2-1;
}
gzclose(in);
......
......@@ -18,20 +18,21 @@ CutSurfaces::CutSurfaces(GLfloat *n)
/** add a Surface element to the surface
*
* \param se the element
* \param n the normal
* \param ni list for data interpolation
* \param se the element
* \param ni list for data interpolation
* \param sele element being cut
*
* \pre each element pointer points to a single element and not a list
*
* \note points are not shared between elements
*/
void
CutSurfaces :: addEle( SurfaceElement *se, Interpolator<DATA_TYPE> *ni )
CutSurfaces :: addEle( SurfaceElement *se, Interpolator<DATA_TYPE> *ni, int sele )
{
_ele.push_back(se);
_ptarr.push_back(const_cast<GLfloat *>(const_cast<PPoint*>(se->pt())->pt()));
_interp.push_back(ni);
_sele.push_back(sele);
}
......@@ -59,6 +60,7 @@ CutSurfaces::clear( GLfloat *n )
delete[] _interp[i];
}
_ele.clear();
_sele.clear();
_ptarr.clear();
_interp.clear();
_zlist.clear();
......
......@@ -20,7 +20,7 @@ class CutSurfaces : public Surfaces
~CutSurfaces();
void get_vert_norms( GLfloat *vn ){}
void determine_vert_norms( PPoint & ){}
void addEle( SurfaceElement *e, Interpolator<DATA_TYPE>* );
void addEle( SurfaceElement *e, Interpolator<DATA_TYPE>*, int=-1 );
DATA_TYPE interpolate( int e, DATA_TYPE *d, int p )
{
return _interp[e][p].interpolate( d );
......
......@@ -20,19 +20,22 @@ class DataAllInMem : public DataClass<T>
using DataClass<T> :: last_tm;
using DataClass<T> :: slice_size;
using DataClass<T> :: filename;
using DataClass<T> :: _elebased;
using DataClass<T> :: _ftype;
using DataClass<T> :: _dt;
using DataClass<T> :: _t0;
public:
DataAllInMem( const char *fn, int, bool );
DataAllInMem( const char *fn, int, int, bool=false );
~DataAllInMem( );
virtual T max(int); // maximum value at a time instance
virtual T max(); // maximum value
virtual T min(int); // minimum value at a time
virtual T min(); // minimum value
virtual T* slice(int); // return pointer to data slice
virtual T* slice(int=-1); // return pointer to data slice
virtual void time_series( int, T* ); // time series for a point
inline virtual void increment(int){}};
inline virtual void increment(int){}
};
template<class T>
......@@ -90,7 +93,6 @@ T DataAllInMem<T>::min( int tm )
}
template<class T>
T* DataAllInMem<T>::slice( int tm )
{
......@@ -113,11 +115,12 @@ void DataAllInMem<T>::time_series( int offset, T* buffer )
* Read all data into a single buffer
*
* \param fn the file name of the data file
* \param slsz slice size
* \param slsz number of points
* \param slsz number of elements
* \param base1 whether node numbering starts at one
*/
template<class T>
DataAllInMem<T>::DataAllInMem( const char *fn, int slsz, bool base1 )
DataAllInMem<T>::DataAllInMem( const char *fn, int npts, int neles, bool base1 )
{
bool IGBdata;
int j = 0;
......@@ -135,11 +138,10 @@ DataAllInMem<T>::DataAllInMem( const char *fn, int slsz, bool base1 )
_dt = 1;
_t0 = 0;
slice_size = slsz;
fileType ftype=FileTypeFinder( fn );
_ftype=FileTypeFinder( fn );
if ( ftype == FThdf5 ) {
if ( _ftype == FThdf5 ) {
#ifdef USE_HDF5
if(ch5_open( fname.substr(0, fname.find_last_of(":")).c_str(), &hin ) )
throw(1);
......@@ -163,20 +165,26 @@ DataAllInMem<T>::DataAllInMem( const char *fn, int slsz, bool base1 )
else if ( typeid(T) == typeid(long) ) scanstr = "%ld";
else exit(1);
if ( ftype == FTIGB ) {
if ( _ftype == FTIGB ) {
head = IGBheader( in, true );
if ( head.slice_sz() != slice_size ) {
maxtm = -1;
throw PointMismatch(slice_size,head.slice_sz());
if ( head.slice_sz() == npts ) {
slice_size = npts;
_elebased = false;
}else if ( head.slice_sz() == neles ) {
slice_size = npts;
_elebased = true;
} else {
maxtm = -1;
throw PointMismatch(npts, neles, head.slice_sz());
}
_dt = head.inc_t();
_t0 = head.org_t();
} else if ( ftype == FTfileSeqCG ) {
} else if ( _ftype == FTfileSeqCG ) {
CG_file_list( CGfiles, fn );
CGp = CGfiles.begin();
scanner = "%*f %*f";
scanner += scanstr;
} else if( ftype == FThdf5 ) {
} else if( _ftype == FThdf5 ) {
string gtype;
#ifdef USE_HDF5
if( parse_HDF5_grid( fn, gtype, index ) || gtype!="nodal" )
......@@ -196,7 +204,7 @@ DataAllInMem<T>::DataAllInMem( const char *fn, int slsz, bool base1 )
int i;
switch ( ftype ) {
switch ( _ftype ) {
case FTfileSeqCG:
gzclose( in );
in = gzopen( CGp->second.c_str(), "r" );
......@@ -236,19 +244,19 @@ DataAllInMem<T>::DataAllInMem( const char *fn, int slsz, bool base1 )
break;
}
if ( (ftype==FTIGB||ftype==FTascii) && i!=slice_size )
if ( (_ftype==FTIGB||_ftype==FTascii) && i!=slice_size )
break;
if ( ftype==FThdf5 )
if ( _ftype==FThdf5 )
break;
maxtm++;
if ( ftype==FTfileSeqCG && ++CGp==CGfiles.end() ) {
if ( _ftype==FTfileSeqCG && ++CGp==CGfiles.end() ) {
gzclose( in );
break;
}
if ( ftype==FTIGB && maxtm==head.t() )
if ( _ftype==FTIGB && maxtm==head.t() )
break;
} while (1);
......@@ -257,12 +265,12 @@ DataAllInMem<T>::DataAllInMem( const char *fn, int slsz, bool base1 )
if ( maxtm == -1 ) {
free( data );
if (ftype == FTfileSeqCG ) CGfiles.~map();
if (_ftype == FTfileSeqCG ) CGfiles.~map();
throw( 1 );
}
#ifdef USE_HDF5
if ( ftype==FThdf5 ) {
if ( _ftype==FThdf5 ) {
ch5_close( hin );
ch5s_nodal_free_grid_info( &info );
}
......
......@@ -25,9 +25,26 @@ class FrameMismatch
class PointMismatch
{
private:
bool single = true;
int expected1, got, expected2;
char msg[1024];
public:
int expected, got;
PointMismatch( int a, int b ){expected=a;got=b;}
PointMismatch( int a, int b ){expected1=a;got=b;}
PointMismatch( int a, int b, int c ){expected1=a;expected2=b;got=c;single=false;}
const char* print(){
if(single)sprintf( msg, "Expected %d but got %d", expected1, got);
else sprintf( msg, "Expected %d or %d but got %d", expected1, expected2, got);
return msg; }
};
class EmptyDataFile
{
private:
const char* msg = "No data in file" ;
public:
EmptyDataFile(){}
const char* print(){ return msg; }
};
......@@ -36,31 +53,35 @@ template<class T>
class DataClass
{
public:
virtual T max(int)=0; //!< maximum data value at a time
virtual T max()=0; //!< maximum data value at a time
virtual T min(int)=0; //!< minimum data value at a time
virtual T min()=0; //!< minimum data value at a time
virtual T* slice(int)=0; //!< pointer to time slice of data
virtual void time_series( int, T* )=0; //!< time series for a point
virtual void increment(int)=0; //!< time slice increment
int max_tm(){return maxtm;} //!< maximum allowable time
int slice_sz(){return slice_size;} //!< size of 1 time slice
void slice_sz(int a){slice_size=a;} //!< set size of slice
string file(){return filename;} //!< return the file
float t0(void){ return _t0; } //!< initial time read
float dt(void){ return _dt; } //!< time increment
virtual T max(int)=0; //!< maximum data value at a time
virtual T max()=0; //!< maximum data value at a time
virtual T min(int)=0; //!< minimum data value at a time
virtual T min()=0; //!< minimum data value at a time
virtual T* slice(int)=0; //!< pointer to time slice of data
virtual void time_series( int, T* )=0; //!< time series for a point
virtual void increment(int)=0; //!< time slice increment
int max_tm(){return maxtm;} //!< maximum allowable time
int slice_sz(){return slice_size;} //!< size of 1 time slice
void slice_sz(int a){slice_size=a;} //!< set size of slice
string file(){return filename;} //!< return the file name
float t0(void){ return _t0; } //!< initial time read
float dt(void){ return _dt; } //!< time increment
fileType filetype(void){ return _ftype; } //!< type of file
bool ele_based(void){ return _elebased; } //!< is data element based?
DataClass():data(NULL),maxtm(0),last_tm(-1),slice_size(0){}
DataClass(){}
virtual ~DataClass(){}
protected:
T* data; //!< data
int maxtm; //!< number of time slice
int last_tm; //!< last time that can be requested
int slice_size; //!< amountof data in one time slice
float _dt; //!< time increment
float _t0; //!< initial time read in
string filename; //!< file containing data
T* data=NULL; //!< data
int maxtm=0; //!< number of time slice
int last_tm=-1; //!< last time that can be requested
int slice_size=0; //!< amountof data in one time slice
float _dt=1; //!< time increment
float _t0=0; //!< initial time read in
string filename; //!< file containing data
fileType _ftype; //!< file type
bool _elebased=false; //!< element data read in?
};
#endif
#ifndef DATAONDEMAND_H
#define DATAONDEMAND_H
#include "IGBheader.h"
#include <map>
#include <string>
#include <exception>
#include <algorithm>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "DataClass.h"
#include "AsciiReader.hpp"
using namespace std;
static const size_t IGB_OFFSET = 1024;
/** Class for reading data requested from the file on disk */
template<class T>
class DataOnDemand: public DataClass<T>
{
public:
virtual T max(int t){return extremum(t,true);} //!< maximum data value at a time
virtual T max(){assert(0);} //!< maximum data value at a time
virtual T min(int t){return extremum(t,false);}//!< minimum data value at a time
virtual T min(){assert(0);} //!< minimum data value at a time
virtual T* slice(int=-1); //!< pointer to time slice of data
virtual void time_series( int, T* ); //!< time series for a point
virtual void increment(int){} //!< time slice increment
DataOnDemand(const char *fn, int, int=-1);
virtual ~DataOnDemand(){free(data);if(databuf)free(databuf);}
private:
IGBheader *hdr=NULL;
AsciiReader *asciiRdr=NULL;
char *databuf=NULL;
const char *_scanstr; //!< format code to read ascii file
const int bufsz=1024;
char buf[1024]; //!< storage tor read ascii line
T extremum(int,bool);
using DataClass<T> :: data;
using DataClass<T> :: maxtm;
using DataClass<T> :: last_tm;
using DataClass<T> :: slice_size;
using DataClass<T> :: filename;
using DataClass<T> :: _elebased;
using DataClass<T> :: _dt;
using DataClass<T> :: _t0;
using DataClass<T> :: _ftype;
};
template<class T>
DataOnDemand<T>::DataOnDemand( const char *fn, int npts, int neles )
{
if( neles<0) neles = npts;
bool IGBdata;
int j = 0;
FILE *in;
string scanner;
map<int,string> CGfiles;
map<int,string>::iterator CGp;
#ifdef USE_HDF5
hid_t hin;
struct ch5s_nodal_grid info;
unsigned int index;
#endif
filename = fn;
_ftype = FileTypeFinder( fn );
if ( _ftype == FThdf5 ) {
#ifdef USE_HDF5
if(ch5_open( fname.substr(0, filename.find_last_of(":")).c_str(), &hin ) )
throw(1);
#endif
} else {
if ( (in=fopen(fn, "r")) == NULL )
throw( 1 );
}
// ugly but I don't know what else to do besides specialization which is ugly
if ( typeid(T) == typeid(double) ) _scanstr = "%lf";
else if ( typeid(T) == typeid(float) ) _scanstr = "%f";
else if ( typeid(T) == typeid(int) ) _scanstr = "%d";
else if ( typeid(T) == typeid(short) ) _scanstr = "%hd";
else if ( typeid(T) == typeid(long) ) _scanstr = "%ld";
else exit(1);
// open file
if ( _ftype == FTIGB || _ftype == FTDynPt) {
hdr = new IGBheader( in, true );
if ( hdr->slice_sz() == npts ) {
slice_size = npts;
_elebased = false;
}else if ( hdr->slice_sz() == neles ) {
slice_size = neles;
_elebased = true;
} else {
maxtm = -1;
throw PointMismatch(npts, neles, hdr->slice_sz());
}
if ( _ftype==FTDynPt && hdr->num_components()!=3 ) {
maxtm = -1;
throw PointMismatch(hdr->num_components(),3);
}
struct stat statstr;
stat( filename.c_str(), &statstr );
maxtm = (statstr.st_size-1024)/slice_size/hdr->data_size()-1;
if( maxtm<0 ){
throw EmptyDataFile();
}
_dt = hdr->inc_t();
_t0 = hdr->org_t();
databuf = (char *)malloc( slice_size*hdr->data_size() );
} else if ( _ftype == FTfileSeqCG ) {
CG_file_list( CGfiles, fn );
CGp = CGfiles.begin();
scanner = "%*f %*f";
scanner += _scanstr;
} else if( _ftype == FThdf5 ) {
string gtype;
#ifdef USE_HDF5
if( parse_HDF5_grid( fn, gtype, index ) || gtype!="nodal" )
throw(1);
ch5s_nodal_grid_info( hin, index, &info );
_dt = info.time_delta;
maxtm = 0;
#endif
_t0 = 0;
} else if( _ftype == FTascii ) {
asciiRdr = new AsciiReader;
asciiRdr->read_file( filename );
if( asciiRdr->num_lines()<npts && asciiRdr->num_lines()<neles ){
maxtm = -1;
throw PointMismatch(npts, neles, asciiRdr->num_lines());
} else if( !neles || asciiRdr->num_lines()<neles ||
(asciiRdr->num_lines()>=npts && asciiRdr->num_lines()%neles) ) {
slice_size = npts;
_elebased = false;
} else {
slice_size = neles;
_elebased = true;
}
maxtm = asciiRdr->num_lines()/slice_size - 1;
}
size_t datasz = slice_size*sizeof(T)*(hdr?hdr->num_components():1);
data = (T *)malloc( datasz );
last_tm = 0;
// get one slice
switch( _ftype ) {
case FTIGB:
case FTDynPt:
hdr->read_data( data, 1, databuf );
break;
case FTascii:
for( int i=0; i<slice_size; i++ ) {
asciiRdr->get_line( i, buf, bufsz );
if( sscanf(buf, _scanstr, data+i ) != 1 ) {
maxtm = -1;
throw( 1 );
}
}
break;
#ifdef USE_HDF5
case FThdf5:
assert(0);
#endif
default:
assert(0);
}
}
/** return an extremum
*
* \param tm frame number
* \param max determine max?, min o.w.
*/
template<class T>
T DataOnDemand<T>::extremum( int tm, bool max )
{
if ( tm>maxtm ) return 0;
T* (*exfcn)(T*, T*);
if( max )
exfcn = max_element<T*>;
else
exfcn = min_element<T*>;
if( last_tm == tm )
return *exfcn( data, data+slice_size );
// analyze a temporary slice
T* sdata;
if( _ftype==FTIGB || _ftype==FTDynPt ) {
sdata = (T*)malloc(slice_size*hdr->data_size());
hdr->data_frame( tm );
hdr->read_data( sdata, 1, NULL );
} else if( _ftype == FTascii ) {
sdata = (T*)malloc(slice_size);
for( int i=0; i<slice_size; i++ ) {
asciiRdr->get_line( i+slice_size*tm, buf, bufsz );
sscanf( buf, _scanstr, sdata+i );
}
} else {
assert(0);
}
T maxdat = *exfcn( sdata, sdata+slice_size ) ;
free( sdata );
return maxdat;
}
template<class T>
T* DataOnDemand<T>::slice( int tm )
{
if ( tm>maxtm )
return NULL;
if( tm == last_tm || tm == -1 )
return data;
if( _ftype == FTIGB || _ftype == FTDynPt ) {
hdr->data_frame( tm );
hdr->read_data( data, 1, databuf );
} else if( _ftype == FTascii ) {
for( int i=0; i<slice_size; i++ ) {
asciiRdr->get_line( i+slice_size*tm, buf, bufsz );
sscanf( buf, _scanstr, data+i );
}
} else
assert(0);
last_tm = tm;
return data;
}
template<class T>
void DataOnDemand<T>::time_series( int offset, T* buffer )
{
FILE *in = (FILE *)hdr->fileptr();
if( _ftype == FTIGB ) {
size_t slsz = hdr->slice_sz()*hdr->data_size();
char datum[hdr->data_size()];
offset *=hdr->data_size();
for( size_t i=0; i<=maxtm; i++ ) {
fseek( in, IGB_OFFSET+offset+i*slsz, SEEK_SET );
fread( datum, hdr->data_size(), 1, in );
if ( hdr->endian() != hdr->systeme() ) hdr->swab( datum, 1 );
buffer[i] = hdr->convert_buffer_datum<T>( datum, 0 );
}
} else if( _ftype == FTascii ) {
for( int i=0; i<=maxtm; i ++ ) {
asciiRdr->get_line( offset+slice_size, buf, bufsz );
sscanf( buf, _scanstr, buffer+i );
}
}
}
#endif
......@@ -32,7 +32,10 @@ class DataReader
* slice; give the pointer location to slave->data When the data
* is found, set slave->v_bit to 1
*/
virtual ~DataReader(){};
#ifdef __APPLE__
virtual
#endif
~DataReader(){};
virtual void reader()=0;
/**
* \pre
......
......@@ -2,19 +2,22 @@
version 1.0400
header_name {.h}
code_name {.cxx}
decl {\#include "TBmeshWin.h"} {private local
decl {\#include "TBmeshWin.h"} {private global
}
decl {class TBmeshWin;} {public local
decl {class TBmeshWin;} {public global
}
decl {\#include "Colourscale.h"} {public global
}
class DeadDataGUI {open
} {
Function {DeadDataGUI( TBmeshWin *w)} {open
Function {DeadDataGUI( TBmeshWin *tb, Colourscale *c, unsigned int cond)} {open
} {
Fl_Window window {
label {Dead Data } open
xywh {1015 298 285 380} type Double hotspot visible
xywh {630 45 285 380} type Double hide hotspot
} {
Fl_Light_Button showDeadData {
label {Dead data}