CDrawable.C
//-----------------------------------------------------------------------------
// $Header: CDrawable.C,v 2.4 97/04/14 14:34:56 messer Exp $
//
// COOL Program Library
// Copyright (C) CERES collaboration, 1996
//
// Implementation of abstract class CDrawable.
//
//-----------------------------------------------------------------------------
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <math.h>
#include "CDrawable.h"
#include <X11/Xutil.h>
int CDrawable::refCount = 0;
int CDrawable::vogleInitialized = 0;
CDrawable::CDrawable()
{
xdisplay = 0; // can be overwritten by bindWindow()
xwindow = 0;
refCount++;
psMode = False;
zoomFactor = 0; // zero for nominal view
xzoom = 0;
yzoom = 0;
}
CDrawable::~CDrawable()
{
if (--refCount < 0) refCount = 0;
if (refCount == 0 && vogleInitialized) {
vexit();
vogleInitialized = False;
}
}
void CDrawable::bindWindow(const char*) {}
void CDrawable::setupColors()
{
//
// First some standard colors not already defined in vogle
//
mapcolor(Gray, int(0.7*MaxColors), int(0.7*MaxColors), int(0.7*MaxColors));
//
// Now fill the color table according to amplitude.
// Amplitude zero is drawn in black. The 12 most high
// amplitudes are set to pure red to prevent X11 from
// running out of non-allocated colormap cells. The eye
// cannot resolve all that shades of red anyhow.
// For the same reason hue values are converted to int
// in order to assign amplitudes with close hue values the
// same color.
// The color generation is according to an exponential
// amplitude distribution and starts at deep blue going
// through green, yellow and ends at pure red.
//
const int light = 50;
const int sat = 100;
int colorIndex = Gray+1;
int hue, hueOld = 0;
int red, green, blue;
colors.insert(Black);
for (int amplitude=1; amplitude < MaxColors; amplitude++) {
if (amplitude < MaxColors-12) {
hue = int(double(MaxColors)*(1.-log(amplitude+1)/log(MaxColors)));
if (hue == hueOld)
colors.insert(colors[amplitude-1]);
else {
hls2rgb(hue, light, sat, red, green, blue);
mapcolor(colorIndex, red, green, blue); // range 0-255
colors.insert(colorIndex);
colorIndex++;
}
hueOld = hue;
}
else
colors.insert(Red);
}
}
void CDrawable::display(const char* options)
{
//
// Check if xdisplay and window already exist.
// If not, open a new display and create new window.
//
if (xdisplay == 0)
xdisplay = XOpenDisplay((char*)0);
if (xwindow == 0)
xwindow = createNewWindow();
//
// Get the actual dimension of the window (device coordinates)
//
XWindowAttributes windowAttributes;
XGetWindowAttributes(xdisplay, xwindow, &windowAttributes);
width = windowAttributes.width;
height = windowAttributes.height;
//
// Tell vogle which window to draw in. If this is the (first? cv)
// call to vogle we have to initialize the package via
// vinit(). However this should be done only once for
// all instances of CDrawable.
//
if (!vogleInitialized) {
vo_xt_window(xdisplay, xwindow, width, height);
vinit("X11");
vogleInitialized = True;
}
else {
vo_xt_set_win(xdisplay, xwindow, width, height);
}
//
// Initialize drawing, define world coordinate system
// If zoomFactor > 1 we also have to calculate the new
// frame according to xzoom, yzoom and zoomFactor.
//
xdisplay = vo_xt_get_display();
xwindow = vo_xt_get_window();
vsetflush(False);
circleprecision(128);
if (colors.isEmpty()) setupColors();
viewport(-1,1,-1,1);
if (zoomFactor > 1) {
float xrange = (getRight()-getLeft())/zoomFactor;
float yrange = (getTop()-getBottom())/zoomFactor;
ortho2(xzoom-xrange/2., xzoom+xrange/2.,
yzoom-yrange/2., yzoom+yrange/2);
}
else
ortho2(getLeft(), getRight(), getBottom(), getTop());
//
// If no new options are given we use the previous settings
//
if (options != 0 && strcmp(options, "") != 0 )
lastOptions = options;
//
// Do the drawing provided by the derived class
//
if (zoomFactor > 1)
clipping(True);
else
clipping(False);
psMode = False;
draw(lastOptions.data());
vflush();
//
// Get rid of queued expose events
//
XEvent xevent;
while (XCheckTypedWindowEvent(xdisplay, xwindow, Expose, &xevent));
}
Window CDrawable::createNewWindow()
{
//
// Window manager hints
//
XSizeHints *size_hints = XAllocSizeHints();
XWMHints *wm_hints = XAllocWMHints();
XClassHint *class_hints = XAllocClassHint();
//
// Get screen size from display structure macro
//
int screen_num = DefaultScreen(xdisplay);
unsigned int display_width = DisplayWidth(xdisplay, screen_num);
unsigned int display_height = DisplayHeight(xdisplay, screen_num);
//
// Size of window
//
int x = 0;
int y = 0;
width = display_width/3;
height = int(width*(getTop()-getBottom())/(getRight()-getLeft()));
unsigned int border_width = 1;
//
// Create Window
//
Window win = XCreateSimpleWindow(xdisplay, RootWindow(xdisplay,screen_num),
x, y, width, height, border_width, BlackPixel(xdisplay,
screen_num), WhitePixel(xdisplay,screen_num));
//
// Set size hints for window manager. The window manager may
// override these settings.
//
size_hints->flags = PPosition | PSize | PMinSize;
size_hints->min_width = width;
size_hints->min_height = height;
//
// These calls store window_name and icon_name into
// XTextProperty structures and set their other
// fields properly.
//
XTextProperty windowName, iconName;
char *window_name = (char*) getName().data();
XStringListToTextProperty(&window_name, 1, &windowName);
XStringListToTextProperty(&window_name, 1, &iconName);
//
// Set the window manager hints
//
wm_hints->initial_state = NormalState;
wm_hints->input = True;
wm_hints->flags = StateHint | InputHint;
class_hints->res_name = 0;
class_hints->res_class = "Cool";
XSetWMProperties(xdisplay, win, &windowName, &iconName,
0, 0, size_hints, wm_hints, class_hints);
//
// Display window
//
XMapRaised(xdisplay, win);
return win;
}
void CDrawable::storePostScript(const char* filename)
{
//
// Setup PS driver
//
voutput((char*)filename); // redirect output to file
vnewdev("pcps"); // new PostScript device
//
// Define viewport, world coordinates and colors
//
viewport(-1,1,-1,1);
if (zoomFactor > 1) {
float xrange = (getRight()-getLeft())/zoomFactor;
float yrange = (getTop()-getBottom())/zoomFactor;
ortho2(xzoom-xrange/2., xzoom+xrange/2.,
yzoom-yrange/2., yzoom+yrange/2);
}
else
ortho2(getLeft(), getRight(), getBottom(), getTop());
setupColors(); // new colormap for PostScript device
//
// Perform the actual drawing, use previously defined option string
//
psMode = True;
clipping(True);
draw(lastOptions.data());
vflush();
//
// Back to old device
//
vo_xt_window(xdisplay, xwindow, width, height);
vinit("X11");
}
void CDrawable::printPostScript(const char* printer)
{
CString filename, device, command;
filename = tempnam("/tmp", "COOL"); // get temporary file name
device = printer;
command = "(lp -d" + device + " " + filename + "; rm -f " + filename + ")&";
storePostScript(filename.data());
system(command.data());
}
#define VALUE(n1, n2, h) ((h < 60) ? (n1 + (n2-n1)*h/60) : ((h<180) ? \
(n2) : ((h<240) ? (n1+(n2-n1)*(240-h)/60) : (n1))))
void CDrawable::hls2rgb(int hue, int light, int sat, int &red, int &green, int &blue)
{
double h = (double) hue;
double l = (double) light/100;
double s = (double) sat/100;
double m1, m2;
double r, g, b;
if (l<= 0.5)
m2 = l*(1 + s);
else
m2 = l + s - l*s;
m1 = 2*l - m2;
if ((s == 0) && (h == 0)) {
r = l; g = l; b = l;
}
else {
r = VALUE(m1, m2, (((h+120) > 360) ? (h-240) : (h+120)));
g = VALUE(m1, m2, h);
b = VALUE(m1, m2, (((h-120) < 0) ? (h+240) : (h-120)));
}
red = (int) (r * 255);
green = (int) (g * 255);
blue = (int) (b * 255);
}
void CDrawable::zoom()
{
float wx, wy;
locator(&wx, &wy);
if (wx > getRight() || wx < getLeft() ||
wy > getTop() || wy < getBottom()) return;
//
// Each zoom click inceases the zoom by a factor of 2
//
if (zoomFactor < 1) zoomFactor = 1;
zoomFactor *= 2;
xzoom = wx;
yzoom = wy;
display((char*)0);
}
void CDrawable::zoomOut()
{
//
// Zoom out leaving the current view center untouched.
//
zoomFactor /= 2;
if (zoomFactor < 1) zoomFactor = 1;
display((char*)0);
}
void CDrawable::resetZoom()
{
zoomFactor = 0;
display((char*)0);
}
void CDrawable::locatePoint()
{
float wx, wy;
locator(&wx, &wy);
if (wx > getRight() || wx < getLeft() ||
wy > getTop() || wy < getBottom()) return;
cout << '(' << wx << ',' << wy << ')' << endl;
cout.flush();
}