rebuild for using with libusefull_macros and run through serialsock

This commit is contained in:
Edward Emelianov 2022-01-23 14:40:05 +03:00
parent 722645aa16
commit 2e4cc8c1fe
47 changed files with 113732 additions and 1697 deletions

View File

@ -1,4 +1,4 @@
update=Чт 30 янв 2020 17:14:40 update=Сб 31 июл 2021 22:20:24
version=1 version=1
last_client=kicad last_client=kicad
[pcbnew] [pcbnew]

View File

@ -0,0 +1,26 @@
### Serial interface commands (ends with '\n'), small letter for only
local processing:
- **0...7** send message to Nth controller, not broadcast (after numb
er should be CAN command)
- **a** get raw ADC values
- **B** send dummy CAN messages to broadcast address
- **c** show coefficients for all thermosensors
- **D** send dummy CAN messages to master (0) address
- **Ee** end temperature scan
- **Ff** turn sensors off
- **g** get last CAN address
- **Hh** switch I2C to high speed (100kHz)
- **i** reinit CAN with new address (if changed)
- **Jj** get MCU temperature
- **Kk** get values of U and I
- **Ll** switch I2C to low speed (default, 10kHz)
- **Mm** change master id to 0 (**m**) / broadcast (**M**)
- **Oo** turn onboard diagnostic LEDs **O**n or **o**ff (both commands
are local!)
- **P** ping everyone over CAN
- **Rr** reinit I2C
- **Ss** start temperature scan
- **Tt** start single temperature measurement
- **u** check CAN bus status for errors
- **Vv** very low speed
- **Z** get sensors state over CAN

View File

@ -1,13 +1,14 @@
# run `make DEF=...` to add extra defines # run `make DEF=...` to add extra defines
PROGRAM := netdaemon PROGRAM := netdaemon
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all -pthread LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all -pthread
LDFLAGS += -lusefull_macros -lm
SRCS := $(wildcard *.c) SRCS := $(wildcard *.c)
DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111
#DEFINES += -DEBUG #DEFINES += -DEBUG
# baudrate for USB<->UART converter # baudrate for USB<->UART converter
DEFINES += -DBAUD_RATE=B115200 DEFINES += -DBAUD_RATE=B115200
OBJDIR := mk OBJDIR := mk
CFLAGS += -O2 -Wall -Werror -Wextra -Wno-trampolines CFLAGS += -O3 -Wall -Werror -Wextra -Wno-trampolines
OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o))
DEPS := $(OBJS:.o=.d) DEPS := $(OBJS:.o=.d)
CC = gcc CC = gcc
@ -16,7 +17,7 @@ all : $(OBJDIR) $(PROGRAM)
$(PROGRAM) : $(OBJS) $(PROGRAM) : $(OBJS)
@echo -e "\t\tLD $(PROGRAM)" @echo -e "\t\tLD $(PROGRAM)"
$(CC) $(LDFLAGS) $(OBJS) -o $(PROGRAM) $(CC) $(OBJS) $(LDFLAGS) -o $(PROGRAM)
$(OBJDIR): $(OBJDIR):
mkdir $(OBJDIR) mkdir $(OBJDIR)
@ -27,7 +28,7 @@ endif
$(OBJDIR)/%.o: %.c $(OBJDIR)/%.o: %.c
@echo -e "\t\tCC $<" @echo -e "\t\tCC $<"
$(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $< $(CC) $< -MD -c $(CFLAGS) $(DEFINES) -o $@
clean: clean:
@echo -e "\t\tCLEAN" @echo -e "\t\tCLEAN"

View File

@ -20,3 +20,9 @@ images storing directory, copy there script 'plot' and run service as
Every 15 minutes it will calculate average values of thermal data and plot three graphs: Every 15 minutes it will calculate average values of thermal data and plot three graphs:
T0.html with top temperatures, T1.html with bottom and Tgrad.html with their differences (T0-T1). T0.html with top temperatures, T1.html with bottom and Tgrad.html with their differences (T0-T1).
** Signals
SIGUSR1 - reread temperatures adjustment file
SIGUSR2 - dump to logfile all current temperature values and turn sensors off until next reading

View File

@ -1,127 +0,0 @@
/*
* daemon.c - functions for running in background like a daemon
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <dirent.h> // opendir
#include "checkfile.h"
#include "usefull_macros.h"
/**
* @brief readPSname - read process name from /proc/PID/cmdline
* @param pid - PID of interesting process
* @return filename or NULL if not found
* don't use this function twice for different names without copying
* its returning by strdup, because `name` contains in static array
*/
char *readPSname(pid_t pid){
static char name[PATH_MAX];
char *pp = name, byte, path[PATH_MAX];
FILE *file;
int cntr = 0;
size_t sz;
snprintf(path, PATH_MAX, PROC_BASE "/%d/cmdline", pid);
file = fopen(path, "r");
if(!file) return NULL; // there's no such file
do{ // read basename
sz = fread(&byte, 1, 1, file);
if(sz != 1) break;
if(byte != '/') *pp++ = byte;
else{
pp = name;
cntr = 0;
}
}while(byte && cntr++ < PATH_MAX-1);
name[cntr] = 0;
fclose(file);
return name;
}
static char *pidfilename_ = NULL; // store the name of pidfile here
/**
* check wether there is a same running process
* exit if there is a running process or error
* Checking have 3 steps:
* 1) lock executable file
* 2) check pidfile (if you run a copy?)
* 3) check /proc for executables with the same name (no/wrong pidfile)
* @param pidfilename - name of pidfile or NULL if none
* @return 0 if all OK or PID of first running process found
*/
pid_t check4running(char *pidfilename){
DIR *dir;
FILE *pidfile;
struct dirent *de;
struct stat s_buf;
pid_t pid = 0, self;
char *name, *myname;
self = getpid(); // get self PID
if(!(dir = opendir(PROC_BASE))){ // open /proc directory
ERR(PROC_BASE);
}
if(!(name = readPSname(self))){ // error reading self name
ERR("Can't read self name");
}
myname = strdup(name);
if(pidfilename && stat(pidfilename, &s_buf) == 0){ // pidfile exists
pidfile = fopen(pidfilename, "r");
if(pidfile){
if(fscanf(pidfile, "%d", &pid) > 0){ // read PID of (possibly) running process
if((name = readPSname(pid)) && strncmp(name, myname, 255) == 0){
fclose(pidfile);
goto procfound;
}
}
fclose(pidfile);
}
}
// There is no pidfile or it consists a wrong record
while((de = readdir(dir))){ // scan /proc
if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self
continue;
if((name = readPSname(pid)) && strncmp(name, myname, 255) == 0)
goto procfound;
}
pid = 0; // OK, not found -> create pid-file if need
if(pidfilename){
pidfile = fopen(pidfilename, "w");
if(!pidfile){
WARN("Can't open PID file");
}else{
fprintf(pidfile, "%d\n", self); // write self PID to pidfile
fclose(pidfile);
if(pidfilename_) FREE(pidfilename_);
pidfilename_ = strdup(pidfilename);
}
}
procfound:
closedir(dir);
free(myname);
return pid;
}
/**
* @brief unlink_pidfile - remove pidfile @ exit
*/
void unlink_pidfile(){
if(!pidfilename_) return;
unlink(pidfilename_);
FREE(pidfilename_);
}

View File

@ -1,39 +0,0 @@
/*
* This file is part of the Zphocus project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef CHECKFILE_H__
#define CHECKFILE_H__
#include <unistd.h> // getpid, unlink
#ifndef PROC_BASE
#define PROC_BASE "/proc"
#endif
#ifndef WEAK
#define WEAK __attribute__ ((weak))
#endif
// check that our process is exclusive
pid_t check4running(char *pidfilename);
// read name of process by its PID
char *readPSname(pid_t pid);
void unlink_pidfile();
#endif // CHECKFILE_H__

View File

@ -22,9 +22,10 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <math.h> #include <usefull_macros.h>
#include "cmdlnopts.h" #include "cmdlnopts.h"
#include "usefull_macros.h" #include "term.h"
/* /*
* here are global parameters initialisation * here are global parameters initialisation
@ -33,20 +34,15 @@ int help;
static glob_pars G; static glob_pars G;
// default values for Gdefault & help // default values for Gdefault & help
#define DEFAULT_COMDEV "/dev/ttyUSB0" #define DEFAULT_SOCKPATH "\0canbus"
#define DEFAULT_PORT "4444" #define DEFAULT_PORT "4444"
#define DEFAULT_PIDFILE "/tmp/TSYS01daemon.pid" #define DEFAULT_PIDFILE "/tmp/TSYS01daemon.pid"
// DEFAULTS // DEFAULTS
// default global parameters // default global parameters
glob_pars const Gdefault = { glob_pars const Gdefault = {
.device = DEFAULT_COMDEV, .sockname = DEFAULT_SOCKPATH,
.port = DEFAULT_PORT, .port = DEFAULT_PORT,
.terminal = 0,
.savepath = NULL,
.makegraphs = 0,
.rest_pars = NULL,
.rest_pars_num = 0,
.adjfilename = "tempadj.txt", .adjfilename = "tempadj.txt",
.pidfilename = DEFAULT_PIDFILE .pidfilename = DEFAULT_PIDFILE
}; };
@ -55,18 +51,18 @@ glob_pars const Gdefault = {
* Define command line options by filling structure: * Define command line options by filling structure:
* name has_arg flag val type argptr help * name has_arg flag val type argptr help
*/ */
myoption cmdlnopts[] = { static myoption cmdlnopts[] = {
// common options // common options
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
{"device", NEED_ARG, NULL, 'i', arg_string, APTR(&G.device), _("serial device name (default: " DEFAULT_COMDEV ")")}, {"name", NEED_ARG, NULL, 'n', arg_string, APTR(&G.sockname), _("server's UNIX socket name (default: \\0canbus)")},
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("network port to connect (default: " DEFAULT_PORT ")")}, {"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("network port to connect (default: " DEFAULT_PORT ")")},
{"terminal",NO_ARGS, NULL, 't', arg_int, APTR(&G.terminal), _("run as terminal")}, //{"terminal",NO_ARGS, NULL, 't', arg_int, APTR(&G.terminal), _("run as terminal")},
{"savepath",NEED_ARG, NULL, 's', arg_string, APTR(&G.savepath), _("path where files would be saved (if none - don't save)")}, {"savepath",NEED_ARG, NULL, 's', arg_string, APTR(&G.savepath), _("path where files would be saved (if none - don't save)")},
{"graphplot",NO_ARGS, NULL, 'g', arg_int, APTR(&G.makegraphs),_("make graphics with gnuplot")}, {"graphplot",NO_ARGS, NULL, 'g', arg_int, APTR(&G.makegraphs),_("make graphics with gnuplot")},
{"testadjfile",NO_ARGS, NULL, 'T', arg_int, APTR(&G.testadjfile),_("test format of file with T adjustements and force running proces to re-read it")}, {"testadjfile",NO_ARGS, NULL, 'T', arg_int, APTR(&G.testadjfile),_("test format of file with T adjustements")},
{"adjname", NEED_ARG, NULL, 'N', arg_string, APTR(&G.adjfilename),_("name of adjustements file (default: tempadj.txt)")}, {"adjname", NEED_ARG, NULL, 'N', arg_string, APTR(&G.adjfilename),_("name of adjustements file (default: tempadj.txt)")},
{"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfilename),_("name of PID file (default: " DEFAULT_PIDFILE ")")}, {"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfilename),_("name of PID file (default: " DEFAULT_PIDFILE ")")},
{"dumpoff", NO_ARGS, NULL, 'd', arg_string, APTR(&G.dumpoff), _("dump sensors data & turn all OFF until next request")}, //{"dumpoff", NO_ARGS, NULL, 'd', arg_string, APTR(&G.dumpoff), _("dump sensors data & turn all OFF until next request")},
end_option end_option
}; };

View File

@ -23,16 +23,13 @@
#ifndef __CMDLNOPTS_H__ #ifndef __CMDLNOPTS_H__
#define __CMDLNOPTS_H__ #define __CMDLNOPTS_H__
#include "parseargs.h"
#include "term.h"
/* /*
* here are some typedef's for global data * here are some typedef's for global data
*/ */
typedef struct{ typedef struct{
char *device; // serial device name char *sockname; // server's UNIX socket name
char *port; // port to connect char *port; // port to connect
int terminal; // run as terminal //int terminal; // run as terminal
char *savepath; // path where data & graphical files would be saved char *savepath; // path where data & graphical files would be saved
int makegraphs; // ==1 to make graphics with gnuplot int makegraphs; // ==1 to make graphics with gnuplot
int rest_pars_num; // number of rest parameters int rest_pars_num; // number of rest parameters
@ -40,7 +37,7 @@ typedef struct{
int testadjfile; // test format of file with adjustments int testadjfile; // test format of file with adjustments
char *adjfilename; // name of adjustements file char *adjfilename; // name of adjustements file
char *pidfilename; // name of PID file char *pidfilename; // name of PID file
int dumpoff; // dump sensors data & turn all OFF until next request //int dumpoff; // dump sensors data & turn all OFF until next request
} glob_pars; } glob_pars;

View File

@ -20,12 +20,15 @@
* MA 02110-1301, USA. * MA 02110-1301, USA.
* *
*/ */
#include <linux/limits.h> // PATH_MAX
#include <stdio.h> // file operations #include <stdio.h> // file operations
#include <string.h>
#include <time.h> #include <time.h>
#include <unistd.h> // access() to check file exists #include <unistd.h> // access() to check file exists
#include <linux/limits.h> // PATH_MAX #include <usefull_macros.h>
#include "usefull_macros.h"
#include "cmdlnopts.h" // glob_pars #include "cmdlnopts.h" // glob_pars
#include "sens_place.h"
extern glob_pars *G; extern glob_pars *G;
@ -94,7 +97,7 @@ static void gnuplot(char *path, char *fname){
DBG("Run %s", buf); DBG("Run %s", buf);
if(system(buf)){ if(system(buf)){
WARNX(_("Can't run `%s`"), buf); WARNX(_("Can't run `%s`"), buf);
}else LOG("created chart %s", fname); }else LOGMSG("created chart %s", fname);
} }
void plot(double data[2][NCHANNEL_MAX+1][NCTRLR_MAX+1], char *savepath){ void plot(double data[2][NCHANNEL_MAX+1][NCTRLR_MAX+1], char *savepath){

View File

@ -19,15 +19,17 @@
* MA 02110-1301, USA. * MA 02110-1301, USA.
*/ */
#include <signal.h> #include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/prctl.h> //prctl #include <sys/prctl.h> //prctl
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> // wait #include <sys/wait.h> // wait
#include <usefull_macros.h>
#include "checkfile.h" //#include "checkfile.h"
#include "cmdlnopts.h" #include "cmdlnopts.h"
#include "socket.h" #include "socket.h"
#include "term.h" #include "term.h"
#include "usefull_macros.h"
glob_pars *G; // non-static: some another files should know about it! glob_pars *G; // non-static: some another files should know about it!
static pid_t childpid = 0; // PID of child - to kill it if need static pid_t childpid = 0; // PID of child - to kill it if need
@ -35,29 +37,25 @@ static pid_t childpid = 0; // PID of child - to kill it if need
// default signals handler // default signals handler
void signals(int signo){ void signals(int signo){
restore_console(); restore_console();
restore_tty();
if(childpid){ if(childpid){
const char *s = signum_to_signame(signo); LOGERR("Exit with status %d", signo);
if(s) LOG("exit with status %d (or by signal %s)", signo, s);
else LOG("exit with status %d", signo);
} }
exit(signo); exit(signo);
} }
// report about some ignored signals // report about some ignored signals
static void repsig(int signo){ static void repsig(int signo){
const char *s = signum_to_signame(signo); WARNX("PID: %d, received signal %d (%s)", getpid(), signo, strsignal(signo));
WARNX("PID: %d, received signal %d, %s (%s)", getpid(), signo, s ? s : "", strsignal(signo));
} }
// SIGUSR1 handler - re-read Tadj file // SIGUSR1 handler - re-read Tadj file
static void refreshAdj(_U_ int signo){ static void refreshAdj(_U_ int signo){
DBG("refresh adj"); DBG("refresh adj");
if(childpid){ // I am a master if(childpid){ // I am a master
LOG("Force child %d to re-read adj-file", childpid); LOGMSG("Force child %d to re-read adj-file", childpid);
kill(childpid, SIGUSR1); kill(childpid, SIGUSR1);
}else{ // I am a child }else{ // I am a child
LOG("Re-read adj-file"); LOGMSG("Re-read adj-file");
read_adj_file(G->adjfilename); read_adj_file(G->adjfilename);
} }
} }
@ -65,38 +63,38 @@ static void refreshAdj(_U_ int signo){
static void logT(_U_ int signo){ static void logT(_U_ int signo){
for(int i = 0; i < 3; ++i){ for(int i = 0; i < 3; ++i){
const char *s = gotstr(i); const char *s = gotstr(i);
if(s && *s) LOG("Sensors group #%d:\n%s", i, s); if(s && *s) LOGMSG("Sensors group #%d:\n%s", i, s);
} }
LOG("Turn sensors off"); LOGMSG("Turn sensors off");
TurnOFF(); TurnOFF();
} }
int main(int argc, char **argv){ int main(int argc, char **argv){
initial_setup(); initial_setup();
char *self = strdup(argv[0]);
G = parse_args(argc, argv); G = parse_args(argc, argv);
if(G->makegraphs && !G->savepath){ if(G->makegraphs && !G->savepath){
ERRX(_("Point the path to graphical files")); ERRX(_("Point the path to graphical files"));
} }
pid_t runningproc = check4running(G->pidfilename); check4running(self, G->pidfilename);
if(G->dumpoff){ /*if(G->dumpoff){
if(runningproc) kill(runningproc, SIGUSR2); if(runningproc) kill(runningproc, SIGUSR2);
else ERRX("There's no running daemon"); else ERRX("There's no running daemon");
return 0; return 0;
} }*/
int raf = read_adj_file(G->adjfilename); int raf = read_adj_file(G->adjfilename);
if(G->testadjfile){ if(G->testadjfile){
if(raf == 0){ if(raf == 0){
green("Format of file %s is right\n", G->adjfilename); green("Format of file %s is right\n", G->adjfilename);
if(runningproc){ // fore running process to re-read it /*if(runningproc){ // fore running process to re-read it
kill(runningproc, SIGUSR1); kill(runningproc, SIGUSR1);
} }*/
return 0; return 0;
} }
return 1; return 1;
} }
if(runningproc) ERRX("Found running process, pid=%d.", runningproc);
if(G->rest_pars_num) if(G->rest_pars_num)
Cl_createlog(G->rest_pars[0]); OPENLOG(G->rest_pars[0], LOGLEVEL_DBG, 1);
// ignore almost all possible signals // ignore almost all possible signals
for(int sig = 0; sig < 256; ++sig) signal(sig, repsig); for(int sig = 0; sig < 256; ++sig) signal(sig, repsig);
signal(SIGTERM, signals); // kill (-15) - quit signal(SIGTERM, signals); // kill (-15) - quit
@ -108,35 +106,34 @@ int main(int argc, char **argv){
signal(SIGUSR1, refreshAdj); // refresh adjustements signal(SIGUSR1, refreshAdj); // refresh adjustements
signal(SIGUSR2, logT); // print all current temperatures into logfile and turn off sensors signal(SIGUSR2, logT); // print all current temperatures into logfile and turn off sensors
#ifndef EBUG #ifndef EBUG
if(!G->terminal){ if(daemon(1, 0)){
if(daemon(1, 0)){ ERR("daemon()");
ERR("daemon()"); }
} while(1){ // guard for dead processes
while(1){ // guard for dead processes childpid = fork();
childpid = fork(); if(childpid){
if(childpid){ LOGMSG("create child with PID %d\n", childpid);
LOG("create child with PID %d\n", childpid); DBG("Created child with PID %d\n", childpid);
DBG("Created child with PID %d\n", childpid); while(childpid != waitpid(childpid, NULL, 0));
while(childpid != waitpid(childpid, NULL, 0)); WARNX("Child %d died\n", childpid);
WARNX("Child %d died\n", childpid); sleep(10);
sleep(10); }else{
}else{ prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies
prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies break; // go out to normal functional
break; // go out to normal functional
}
} }
} }
#endif #endif
DBG("dev: %s", G->device); DBG("sockname: %s", G->sockname);
try_connect(G->device); if(!try_connect(G->sockname)) ERR("Can't connect to UNIX socket");
if(check_sensors()){ if(check_sensors()){
LOG("No CAN-controllers detected"); LOGWARN("No CAN-controllers detected");
if(!poll_sensors(0)){ // there's not main controller connected to given terminal if(!poll_sensors(0)){ // there's not main controller connected to given terminal
LOG("Opened device is not main controller"); LOGERR("Opened device is not main controller");
if(!G->terminal) signals(15); ERRX("Opened device is not main controller");
} }
} }
if(G->terminal) run_terminal(); //if(G->terminal) run_terminal();
else daemonize(G->port); //else
daemonize(G->port);
return 0; return 0;
} }

View File

@ -1,497 +0,0 @@
/* geany_encoding=koi8-r
* parseargs.c - parsing command line arguments & print help
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <stdio.h> // printf
#include <getopt.h> // getopt_long
#include <stdlib.h> // calloc, exit, strtoll
#include <assert.h> // assert
#include <string.h> // strdup, strchr, strlen
#include <strings.h>// strcasecmp
#include <limits.h> // INT_MAX & so on
#include <libintl.h>// gettext
#include <ctype.h> // isalpha
#include "parseargs.h"
#include "usefull_macros.h"
char *helpstring = "%s\n";
/**
* Change standard help header
* MAY consist ONE "%s" for progname
* @param str (i) - new format
*/
void change_helpstring(char *s){
int pcount = 0, scount = 0;
char *str = s;
// check `helpstring` and set it to default in case of error
for(; pcount < 2; str += 2){
if(!(str = strchr(str, '%'))) break;
if(str[1] != '%') pcount++; // increment '%' counter if it isn't "%%"
else{
str += 2; // pass next '%'
continue;
}
if(str[1] == 's') scount++; // increment "%s" counter
};
if(pcount > 1 || pcount != scount){ // amount of pcount and/or scount wrong
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÓÔÒÏËÉ ÐÏÍÏÝÉ"
ERRX(_("Wrong helpstring!"));
}
helpstring = s;
}
/**
* Carefull atoll/atoi
* @param num (o) - returning value (or NULL if you wish only check number) - allocated by user
* @param str (i) - string with number must not be NULL
* @param t (i) - T_INT for integer or T_LLONG for long long (if argtype would be wided, may add more)
* @return TRUE if conversion sone without errors, FALSE otherwise
*/
static bool myatoll(void *num, char *str, argtype t){
long long tmp, *llptr;
int *iptr;
char *endptr;
assert(str);
assert(num);
tmp = strtoll(str, &endptr, 0);
if(endptr == str || *str == '\0' || *endptr != '\0')
return FALSE;
switch(t){
case arg_longlong:
llptr = (long long*) num;
*llptr = tmp;
break;
case arg_int:
default:
if(tmp < INT_MIN || tmp > INT_MAX){
/// "ãÅÌÏÅ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ"
WARNX(_("Integer out of range"));
return FALSE;
}
iptr = (int*)num;
*iptr = (int)tmp;
}
return TRUE;
}
// the same as myatoll but for double
// There's no NAN & INF checking here (what if they would be needed?)
static bool myatod(void *num, const char *str, argtype t){
double tmp, *dptr;
float *fptr;
char *endptr;
assert(str);
tmp = strtod(str, &endptr);
if(endptr == str || *str == '\0' || *endptr != '\0')
return FALSE;
switch(t){
case arg_double:
dptr = (double *) num;
*dptr = tmp;
break;
case arg_float:
default:
fptr = (float *) num;
*fptr = (float)tmp;
break;
}
return TRUE;
}
/**
* Get index of current option in array options
* @param opt (i) - returning val of getopt_long
* @param options (i) - array of options
* @return index in array
*/
static int get_optind(int opt, myoption *options){
int oind;
myoption *opts = options;
assert(opts);
for(oind = 0; opts->name && opts->val != opt; oind++, opts++);
if(!opts->name || opts->val != opt) // no such parameter
showhelp(-1, options);
return oind;
}
/**
* reallocate new value in array of multiple repeating arguments
* @arg paptr - address of pointer to array (**void)
* @arg type - its type (for realloc)
* @return pointer to new (next) value
*/
void *get_aptr(void *paptr, argtype type){
int i = 1;
void **aptr = *((void***)paptr);
if(aptr){ // there's something in array
void **p = aptr;
while(*p++) ++i;
}
size_t sz = 0;
switch(type){
default:
case arg_none:
/// "îÅ ÍÏÇÕ ÉÓÐÏÌØÚÏ×ÁÔØ ÎÅÓËÏÌØËÏ ÐÁÒÁÍÅÔÒÏ× ÂÅÚ ÁÒÇÕÍÅÎÔÏ×!"
ERRX("Can't use multiple args with arg_none!");
break;
case arg_int:
sz = sizeof(int);
break;
case arg_longlong:
sz = sizeof(long long);
break;
case arg_double:
sz = sizeof(double);
break;
case arg_float:
sz = sizeof(float);
break;
case arg_string:
sz = 0;
break;
/* case arg_function:
sz = sizeof(argfn *);
break;*/
}
aptr = realloc(aptr, (i + 1) * sizeof(void*));
*((void***)paptr) = aptr;
aptr[i] = NULL;
if(sz){
aptr[i - 1] = malloc(sz);
}else
aptr[i - 1] = &aptr[i - 1];
return aptr[i - 1];
}
/**
* Parse command line arguments
* ! If arg is string, then value will be strdup'ed!
*
* @param argc (io) - address of argc of main(), return value of argc stay after `getopt`
* @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt`
* BE CAREFUL! if you wanna use full argc & argv, save their original values before
* calling this function
* @param options (i) - array of `myoption` for arguments parcing
*
* @exit: in case of error this function show help & make `exit(-1)`
*/
void parseargs(int *argc, char ***argv, myoption *options){
char *short_options, *soptr;
struct option *long_options, *loptr;
size_t optsize, i;
myoption *opts = options;
// check whether there is at least one options
assert(opts);
assert(opts[0].name);
// first we count how much values are in opts
for(optsize = 0; opts->name; optsize++, opts++);
// now we can allocate memory
short_options = calloc(optsize * 3 + 1, 1); // multiply by three for '::' in case of args in opts
long_options = calloc(optsize + 1, sizeof(struct option));
opts = options; loptr = long_options; soptr = short_options;
// in debug mode check the parameters are not repeated
#ifdef EBUG
char **longlist = MALLOC(char*, optsize);
char *shortlist = MALLOC(char, optsize);
#endif
// fill short/long parameters and make a simple checking
for(i = 0; i < optsize; i++, loptr++, opts++){
// check
assert(opts->name); // check name
#ifdef EBUG
longlist[i] = strdup(opts->name);
#endif
if(opts->has_arg){
assert(opts->type != arg_none); // check error with arg type
assert(opts->argptr); // check pointer
}
if(opts->type != arg_none) // if there is a flag without arg, check its pointer
assert(opts->argptr);
// fill long_options
// don't do memcmp: what if there would be different alignment?
loptr->name = opts->name;
loptr->has_arg = (opts->has_arg < MULT_PAR) ? opts->has_arg : 1;
loptr->flag = opts->flag;
loptr->val = opts->val;
// fill short options if they are:
if(!opts->flag && opts->val){
#ifdef EBUG
shortlist[i] = (char) opts->val;
#endif
*soptr++ = opts->val;
if(loptr->has_arg) // add ':' if option has required argument
*soptr++ = ':';
if(loptr->has_arg == 2) // add '::' if option has optional argument
*soptr++ = ':';
}
}
// sort all lists & check for repeating
#ifdef EBUG
int cmpstringp(const void *p1, const void *p2){
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
int cmpcharp(const void *p1, const void *p2){
return (int)(*(char * const)p1 - *(char *const)p2);
}
qsort(longlist, optsize, sizeof(char *), cmpstringp);
qsort(shortlist,optsize, sizeof(char), cmpcharp);
char *prevl = longlist[0], prevshrt = shortlist[0];
for(i = 1; i < optsize; ++i){
if(longlist[i]){
if(prevl){
if(strcmp(prevl, longlist[i]) == 0) ERRX("double long arguments: --%s", prevl);
}
prevl = longlist[i];
}
if(shortlist[i]){
if(prevshrt){
if(prevshrt == shortlist[i]) ERRX("double short arguments: -%c", prevshrt);
}
prevshrt = shortlist[i];
}
}
#endif
// now we have both long_options & short_options and can parse `getopt_long`
while(1){
int opt;
int oindex = 0, optind = 0; // oindex - number of option in argv, optind - number in options[]
if((opt = getopt_long(*argc, *argv, short_options, long_options, &oindex)) == -1) break;
if(opt == '?'){
opt = optopt;
optind = get_optind(opt, options);
if(options[optind].has_arg == NEED_ARG || options[optind].has_arg == MULT_PAR)
showhelp(optind, options); // need argument
}
else{
if(opt == 0 || oindex > 0) optind = oindex;
else optind = get_optind(opt, options);
}
opts = &options[optind];
// if(opt == 0 && opts->has_arg == NO_ARGS) continue; // only long option changing integer flag
// now check option
if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR)
if(!optarg) showhelp(optind, options); // need argument
void *aptr;
if(opts->has_arg == MULT_PAR){
aptr = get_aptr(opts->argptr, opts->type);
}else
aptr = opts->argptr;
bool result = TRUE;
// even if there is no argument, but argptr != NULL, think that optarg = "1"
if(!optarg) optarg = "1";
switch(opts->type){
default:
case arg_none:
if(opts->argptr) *((int*)aptr) += 1; // increment value
break;
case arg_int:
result = myatoll(aptr, optarg, arg_int);
break;
case arg_longlong:
result = myatoll(aptr, optarg, arg_longlong);
break;
case arg_double:
result = myatod(aptr, optarg, arg_double);
break;
case arg_float:
result = myatod(aptr, optarg, arg_float);
break;
case arg_string:
result = (*((void**)aptr) = (void*)strdup(optarg));
break;
case arg_function:
result = ((argfn)aptr)(optarg);
break;
}
if(!result){
showhelp(optind, options);
}
}
*argc -= optind;
*argv += optind;
}
/**
* compare function for qsort
* first - sort by short options; second - sort arguments without sort opts (by long options)
*/
static int argsort(const void *a1, const void *a2){
const myoption *o1 = (myoption*)a1, *o2 = (myoption*)a2;
const char *l1 = o1->name, *l2 = o2->name;
int s1 = o1->val, s2 = o2->val;
int *f1 = o1->flag, *f2 = o2->flag;
// check if both options has short arg
if(f1 == NULL && f2 == NULL && s1 && s2){ // both have short arg
return (s1 - s2);
}else if((f1 != NULL || !s1) && (f2 != NULL || !s2)){ // both don't have short arg - sort by long
return strcmp(l1, l2);
}else{ // only one have short arg -- return it
if(f2 || !s2) return -1; // a1 have short - it is 'lesser'
else return 1;
}
}
/**
* Show help information based on myoption->help values
* @param oindex (i) - if non-negative, show only help by myoption[oindex].help
* @param options (i) - array of `myoption`
*
* @exit: run `exit(-1)` !!!
*/
void showhelp(int oindex, myoption *options){
int max_opt_len = 0; // max len of options substring - for right indentation
const int bufsz = 255;
char buf[bufsz+1];
myoption *opts = options;
assert(opts);
assert(opts[0].name); // check whether there is at least one options
if(oindex > -1){ // print only one message
opts = &options[oindex];
printf(" ");
if(!opts->flag && isalpha(opts->val)) printf("-%c, ", opts->val);
printf("--%s", opts->name);
if(opts->has_arg == 1) printf("=arg");
else if(opts->has_arg == 2) printf("[=arg]");
printf(" %s\n", _(opts->help));
exit(-1);
}
// header, by default is just "progname\n"
printf("\n");
if(strstr(helpstring, "%s")) // print progname
printf(helpstring, __progname);
else // only text
printf("%s", helpstring);
printf("\n");
// count max_opt_len
do{
int L = strlen(opts->name);
if(max_opt_len < L) max_opt_len = L;
}while((++opts)->name);
max_opt_len += 14; // format: '-S , --long[=arg]' - get addition 13 symbols
opts = options;
// count amount of options
int N; for(N = 0; opts->name; ++N, ++opts);
if(N == 0) exit(-2);
// Now print all help (sorted)
opts = options;
qsort(opts, N, sizeof(myoption), argsort);
do{
int p = sprintf(buf, " "); // a little indent
if(!opts->flag && opts->val) // .val is short argument
p += snprintf(buf+p, bufsz-p, "-%c, ", opts->val);
p += snprintf(buf+p, bufsz-p, "--%s", opts->name);
if(opts->has_arg == 1) // required argument
p += snprintf(buf+p, bufsz-p, "=arg");
else if(opts->has_arg == 2) // optional argument
p += snprintf(buf+p, bufsz-p, "[=arg]");
assert(p < max_opt_len); // there would be magic if p >= max_opt_len
printf("%-*s%s\n", max_opt_len+1, buf, _(opts->help)); // write options & at least 2 spaces after
++opts;
}while(--N);
printf("\n\n");
exit(-1);
}
/**
* get suboptions from parameter string
* @param str - parameter string
* @param opt - pointer to suboptions structure
* @return TRUE if all OK
*/
bool get_suboption(char *str, mysuboption *opt){
int findsubopt(char *par, mysuboption *so){
int idx = 0;
if(!par) return -1;
while(so[idx].name){
if(strcasecmp(par, so[idx].name) == 0) return idx;
++idx;
}
return -1; // badarg
}
bool opt_setarg(mysuboption *so, int idx, char *val){
mysuboption *soptr = &so[idx];
bool result = FALSE;
void *aptr = soptr->argptr;
switch(soptr->type){
default:
case arg_none:
if(soptr->argptr) *((int*)aptr) += 1; // increment value
result = TRUE;
break;
case arg_int:
result = myatoll(aptr, val, arg_int);
break;
case arg_longlong:
result = myatoll(aptr, val, arg_longlong);
break;
case arg_double:
result = myatod(aptr, val, arg_double);
break;
case arg_float:
result = myatod(aptr, val, arg_float);
break;
case arg_string:
result = (*((void**)aptr) = (void*)strdup(val));
break;
case arg_function:
result = ((argfn)aptr)(val);
break;
}
return result;
}
char *tok;
bool ret = FALSE;
char *tmpbuf;
tok = strtok_r(str, ":,", &tmpbuf);
do{
char *val = strchr(tok, '=');
int noarg = 0;
if(val == NULL){ // no args
val = "1";
noarg = 1;
}else{
*val++ = '\0';
if(!*val || *val == ':' || *val == ','){ // no argument - delimeter after =
val = "1"; noarg = 1;
}
}
int idx = findsubopt(tok, opt);
if(idx < 0){
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ: %s"
WARNX(_("Wrong parameter: %s"), tok);
goto returning;
}
if(noarg && opt[idx].has_arg == NEED_ARG){
/// "%s: ÎÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ!"
WARNX(_("%s: argument needed!"), tok);
goto returning;
}
if(!opt_setarg(opt, idx, val)){
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÁÒÇÕÍÅÎÔ \"%s\" ÐÁÒÁÍÅÔÒÁ \"%s\""
WARNX(_("Wrong argument \"%s\" of parameter \"%s\""), val, tok);
goto returning;
}
}while((tok = strtok_r(NULL, ":,", &tmpbuf)));
ret = TRUE;
returning:
return ret;
}

View File

@ -1,124 +0,0 @@
/* geany_encoding=koi8-r
* parseargs.h - headers for parsing command line arguments
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#pragma once
#ifndef __PARSEARGS_H__
#define __PARSEARGS_H__
#include <stdbool.h>// bool
#include <stdlib.h>
#ifndef TRUE
#define TRUE true
#endif
#ifndef FALSE
#define FALSE false
#endif
// macro for argptr
#define APTR(x) ((void*)x)
// if argptr is a function:
typedef bool(*argfn)(void *arg);
/*
* type of getopt's argument
* WARNING!
* My function change value of flags by pointer, so if you want to use another type
* make a latter conversion, example:
* char charg;
* int iarg;
* myoption opts[] = {
* {"value", 1, NULL, 'v', arg_int, &iarg, "char val"}, ..., end_option};
* ..(parse args)..
* charg = (char) iarg;
*/
typedef enum {
arg_none = 0, // no arg
arg_int, // integer
arg_longlong, // long long
arg_double, // double
arg_float, // float
arg_string, // char *
arg_function // parse_args will run function `bool (*fn)(char *optarg, int N)`
} argtype;
/*
* Structure for getopt_long & help
* BE CAREFUL: .argptr is pointer to data or pointer to function,
* conversion depends on .type
*
* ATTENTION: string `help` prints through macro PRNT(), bu default it is gettext,
* but you can redefine it before `#include "parseargs.h"`
*
* if arg is string, then value wil be strdup'ed like that:
* char *str;
* myoption opts[] = {{"string", 1, NULL, 's', arg_string, &str, "string val"}, ..., end_option};
* *(opts[1].str) = strdup(optarg);
* in other cases argptr should be address of some variable (or pointer to allocated memory)
*
* NON-NULL argptr should be written inside macro APTR(argptr) or directly: (void*)argptr
*
* !!!LAST VALUE OF ARRAY SHOULD BE `end_option` or ZEROS !!!
*
*/
typedef enum{
NO_ARGS = 0, // first three are the same as in getopt_long
NEED_ARG = 1,
OPT_ARG = 2,
MULT_PAR
} hasarg;
typedef struct{
// these are from struct option:
const char *name; // long option's name
hasarg has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg, 4 - need arg & key can repeat (args are stored in null-terminated array)
int *flag; // NULL to return val, pointer to int - to set its value of val (function returns 0)
int val; // short opt name (if flag == NULL) or flag's value
// and these are mine:
argtype type; // type of argument
void *argptr; // pointer to variable to assign optarg value or function `bool (*fn)(char *optarg, int N)`
const char *help; // help string which would be shown in function `showhelp` or NULL
} myoption;
/*
* Suboptions structure, almost the same like myoption
* used in parse_subopts()
*/
typedef struct{
const char *name;
hasarg has_arg;
argtype type;
void *argptr;
} mysuboption;
// last string of array (all zeros)
#define end_option {0,0,0,0,0,0,0}
#define end_suboption {0,0,0,0}
extern const char *__progname;
void showhelp(int oindex, myoption *options);
void parseargs(int *argc, char ***argv, myoption *options);
void change_helpstring(char *s);
bool get_suboption(char *str, mysuboption *opt);
#endif // __PARSEARGS_H__

0
src/netdaemon/plot Normal file → Executable file
View File

15
src/netdaemon/scripts/getdata Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
# receive data from mirtemp & store it
export http_proxy=""
# don't call curl if there's no connection to server
curl --http0.9 http://mirtemp.sao.ru:4444/Tmean || exit 1
cd $1
DIR="$(date +%y.%m.%d)"
mkdir $DIR 2>/dev/null
FNAME="${DIR}/$(date +%H:%M)"
for Tdat in T0 T1 T2; do
curl --http0.9 http://mirtemp.sao.ru:4444/${Tdat} -o ${FNAME}_${Tdat}.dat > /dev/null 2>/dev/null
done

View File

@ -0,0 +1,12 @@
#!/bin/bash
#
# ./Archive_one_month <prefix>, like:
#
# ./Archive_one_month 21.03
if [ $# -ne 1 ]; then
echo -e "Usage: $0 <prefix>, like\n\t$0 21.03"
exit 1
fi
for x in $(seq 1 31); do D=$(printf "$1.%02d" $x); echo "tar $D"; tar -zcf ${D}.tgz $D; done

View File

@ -0,0 +1,9 @@
#!/bin/bash
if [ $# != 1 ]; then
echo "USAGE: $0 # (# is 0 or 1)"
echo -e "\t to print all extrems for given sensors group"
exit 1
fi
find . -name "*T${1}.dat" -exec ./findextrems {} \;

View File

@ -0,0 +1,13 @@
#!/bin/bash
if [ $# != 1 ]; then
echo "USAGE: $0 file.dat"
echo -e "\t to print extremal delta T"
exit 1
fi
MIN=$(cat $1 | awk '{print $4}' |sort -n | head -1)
MAX=$(cat $1 | awk '{print $4}' |sort -n | tail -1)
echo "$1: $(echo $MAX - $MIN | bc -l)"

View File

@ -0,0 +1,54 @@
#!/bin/bash
#
# run it like
# for f in *.dat; do ../plotALLjpg $f; done
#
OUT=tmpfile.txt
awk '{print $2 "\t" $3 "\t" $4}' $1 > $OUT
DATE=$(echo $1 | sed -e 's|/| |' -e 's|_.*||')
TX=$(echo $1 | sed 's|.*_\(.*\).dat|\1|') #'
TXO=$(echo $1 | sed 's|\.dat|.jpg|') #'
if [ $TX = "T0" ]; then Tname="TOP side"
else Tname="BOTTOM side"
fi
VAL=$(head -n1 $1 | awk '{print $4}')
echo -e "30\t30\t$VAL\n-30\t-30\t$VAL" >> $OUT
cat << EOF > gnutplt
#!/usr/bin/gnuplot
set contour
unset surface
set cntrparam order 4
set cntrparam bspline
#set cntrparam levels auto 6
#set cntrparam levels incremental -30,0.1,30
set view map
set size square
set xrange [-40:40]
set yrange [-40:40]
set dgrid3d 100,100,4
set table "contour.txt"
splot '$OUT' u 1:2:3
unset table
unset contour
set surface
set table "dgrid.txt"
splot '$OUT' u 1:2:3
unset table
reset
set terminal jpeg enhanced size 1024,768
set output "$TXO"
set size square
set xrange [-30:30]
set yrange [-30:30]
set xlabel "X, dm"
set ylabel "Y, dm"
set title "Mirror temperature $TX for $DATE ($Tname)"
set pm3d map
unset key
circle(x,y,z) = x**2+y**2 > 900 ? NaN : z
splot 'dgrid.txt' u 1:2:(circle(\$1,\$2,\$3)) w pm3d, 'contour.txt' u 1:2:(circle(\$1,\$2,\$3)) w l lc rgb "black", '$OUT' u 1:2:(circle(\$1,\$2,\$3)):3 with labels font ",8"
EOF
chmod 755 gnutplt
./gnutplt
rm -f gnutplt $OUT contour.txt dgrid.txt

View File

@ -0,0 +1,53 @@
#!/bin/bash
#
# run it like
# ./plot 19.12.25/11\:20_T0.dat
#
OUT=tmpfile.txt
awk '{print $2 "\t" $3 "\t" $4}' $1 > $OUT
DATE=$(echo $1 | sed -e 's|/| |' -e 's|_.*||')
TX=$(echo $1 | sed 's|.*_\(.*\).dat|\1|')
if [ $TX = "T0" ]; then Tname="TOP side"
else Tname="BOTTOM side"
fi
VAL=$(head -n1 $1 | awk '{print $4}')
echo -e "30\t30\t$VAL\n-30\t-30\t$VAL" >> $OUT
cat << EOF > gnutplt
#!/usr/bin/gnuplot
set contour
unset surface
set cntrparam order 4
set cntrparam bspline
#set cntrparam levels auto 6
#set cntrparam levels incremental -30,0.1,30
set view map
set size square
set xrange [-40:40]
set yrange [-40:40]
set dgrid3d 100,100,4
set table "contour.txt"
splot '$OUT' u 1:2:3
unset table
unset contour
set surface
set table "dgrid.txt"
splot '$OUT' u 1:2:3
unset table
reset
set terminal jpeg enhanced size 1024,768
set output "$TX.jpg"
set size square
set xrange [-30:30]
set yrange [-30:30]
set xlabel "X, dm"
set ylabel "Y, dm"
set title "Mirror temperature $TX for $DATE ($Tname)"
set pm3d map
unset key
circle(x,y,z) = x**2+y**2 > 900 ? NaN : z
splot 'dgrid.txt' u 1:2:(circle(\$1,\$2,\$3)) w pm3d, 'contour.txt' u 1:2:(circle(\$1,\$2,\$3)) w l lc rgb "black", '$OUT' u 1:2:(circle(\$1,\$2,\$3)):3 with labels font ",8"
EOF
chmod 755 gnutplt
./gnutplt
rm -f gnutplt $OUT contour.txt dgrid.txt

View File

@ -16,10 +16,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <math.h> // fabs
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <usefull_macros.h>
#include "sens_place.h" #include "sens_place.h"
#include "stdbool.h"
#include "usefull_macros.h"
#include "math.h" // fabs
/** /**
Sensor place Dt X Y Z Sensor place Dt X Y Z
@ -294,13 +297,13 @@ cont:
if(fabs(Tadj[i]) > DBL_EPSILON){ if(fabs(Tadj[i]) > DBL_EPSILON){
printf("\tTadj[%02d] = %g\n", i, Tadj[i]); printf("\tTadj[%02d] = %g\n", i, Tadj[i]);
} }
if(fabs(sensors[i].Tadj - Tadj[i]) > 0.001) LOG("Tadj[%d] = %g", i, Tadj[i]); if(fabs(sensors[i].Tadj - Tadj[i]) > 0.001) LOGMSG("Tadj[%d] = %g", i, Tadj[i]);
sensors[i].Tadj = Tadj[i]; sensors[i].Tadj = Tadj[i];
} }
return 0; return 0;
reperr: reperr:
red("Error in string %d:\n", strnum); red("Error in string %d:\n", strnum);
printf("%s\n", adjf); printf("%s\n", adjf);
LOG("Erroneous adjustment file %s in line %d", fname, strnum); LOGWARN("Erroneous adjustment file %s in line %d", fname, strnum);
return 1; return 1;
} }

View File

@ -20,20 +20,22 @@
* MA 02110-1301, USA. * MA 02110-1301, USA.
* *
*/ */
#include "usefull_macros.h" #include <arpa/inet.h> // inet_ntop
#include <limits.h> // INT_xxx
#include <netdb.h> // addrinfo
#include <pthread.h>
#include <signal.h> // pthread_kill
#include <stdio.h>
#include <string.h>
#include <sys/syscall.h> // syscall
#include <unistd.h> // daemon
#include <usefull_macros.h>
#include "cmdlnopts.h" // glob_pars
#include "gnuplot.h" // plot graphs
#include "sens_place.h" // sensors coordinates
#include "socket.h" #include "socket.h"
#include "term.h" #include "term.h"
#include <netdb.h> // addrinfo
#include <arpa/inet.h> // inet_ntop
#include <pthread.h>
#include <limits.h> // INT_xxx
#include <signal.h> // pthread_kill
#include <unistd.h> // daemon
#include <sys/syscall.h> // syscall
#include "sens_place.h" // sensors coordinates
#include "gnuplot.h" // plot graphs
#include "cmdlnopts.h" // glob_pars
#define BUFLEN (10240) #define BUFLEN (10240)
// Max amount of connections // Max amount of connections
@ -191,14 +193,14 @@ static void *handle_socket(void *asock){
} }
pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex);
if(!send_data(sock, webquery, Nsens)){ if(!send_data(sock, webquery, Nsens)){
LOG("can't send data, some error occured"); LOGWARN("can't send data, some error occured");
} }
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
}else if(strncmp("Tmean", found, 5) == 0){ // send user meanT }else if(strncmp("Tmean", found, 5) == 0){ // send user meanT
L = snprintf(tbuf, 128, "%.2f\n", meanT); L = snprintf(tbuf, 128, "%.2f\n", meanT);
if(L != write(sock, tbuf, L)) WARN("write()"); if(L != write(sock, tbuf, L)) WARN("write()");
}else if(strncmp("ReBoOt", found, 6) == 0){ }else if(strncmp("ReBoOt", found, 6) == 0){
LOG("Reboot command from %s", C.host); LOGWARN("Reboot command from %s", C.host);
L = write(sock, "Reboot system\n", 14); L = write(sock, "Reboot system\n", 14);
if(0 != system("sudo reboot")) WARN("Can't reboot"); if(0 != system("sudo reboot")) WARN("Can't reboot");
// here can be more parsers // here can be more parsers
@ -234,7 +236,7 @@ static void *server(void *asock){
struct in_addr ipAddr = pV4Addr->sin_addr; struct in_addr ipAddr = pV4Addr->sin_addr;
char str[INET_ADDRSTRLEN]; char str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &ipAddr, str, INET_ADDRSTRLEN); inet_ntop(AF_INET, &ipAddr, str, INET_ADDRSTRLEN);
//LOG("get connection from %s", str); LOGMSG("get connection from %s", str);
red("Got connection from %s\n", str); red("Got connection from %s\n", str);
pthread_t handler_thread; pthread_t handler_thread;
conn C = {.sockn = newsock}; conn C = {.sockn = newsock};
@ -246,7 +248,7 @@ static void *server(void *asock){
pthread_detach(handler_thread); // don't care about thread state pthread_detach(handler_thread); // don't care about thread state
} }
} }
LOG("server(): UNREACHABLE CODE REACHED!"); LOGERR("server(): UNREACHABLE CODE REACHED!");
} }
typedef double Item; typedef double Item;
@ -368,7 +370,7 @@ static void daemon_(int sock){
if(pthread_create(&sock_thread, NULL, server, (void*) &sock)){ if(pthread_create(&sock_thread, NULL, server, (void*) &sock)){
ERR("pthread_create()"); ERR("pthread_create()");
} }
double tgot = 0., tlastoff = dtime(); double tgot = 0.;//, tlastoff = dtime();
do{ do{
if(pthread_kill(sock_thread, 0) == ESRCH){ // died if(pthread_kill(sock_thread, 0) == ESRCH){ // died
WARNX("Sockets thread died"); WARNX("Sockets thread died");
@ -381,12 +383,12 @@ static void daemon_(int sock){
if(TurnOff){ if(TurnOff){
TurnOff = FALSE; TurnOff = FALSE;
turn_all_off(); turn_all_off();
tlastoff = dtime(); //tlastoff = dtime();
} }/*
if(dtime() - tlastoff > T_OFF_INTERVAL){ if(dtime() - tlastoff > T_OFF_INTERVAL){
turn_all_off(); turn_all_off();
tlastoff = dtime(); tlastoff = dtime();
} }*/
if(dtime() - tgot < T_INTERVAL) continue; if(dtime() - tgot < T_INTERVAL) continue;
// get data // get data
int i; int i;
@ -428,7 +430,7 @@ static void daemon_(int sock){
memcpy(strT, bufs, sizeof(strT)); memcpy(strT, bufs, sizeof(strT));
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
}while(1); }while(1);
LOG("daemon_(): UNREACHABLE CODE REACHED!"); LOGERR("daemon_(): UNREACHABLE CODE REACHED!");
} }
/** /**
@ -474,7 +476,7 @@ void daemonize(char *port){
freeaddrinfo(res); freeaddrinfo(res);
daemon_(sock); daemon_(sock);
close(sock); close(sock);
LOG("daemonize(): UNREACHABLE CODE REACHED!"); LOGERR("daemonize(): UNREACHABLE CODE REACHED!");
signals(0); signals(0);
} }

View File

@ -1 +1,6 @@
#No dT (Treal = T - dT) #No dT (Treal = T - dT)
251 -0.7
320 0.12
330 0.15
360 0.15
520 0.05

View File

@ -18,11 +18,23 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA. * MA 02110-1301, USA.
*/ */
#include "usefull_macros.h" #include <arpa/inet.h>
#include "term.h"
#include <strings.h> // strncasecmp
#include <time.h> // time(NULL)
#include <limits.h> // INT_MAX, INT_MIN #include <limits.h> // INT_MAX, INT_MIN
#include <netdb.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <strings.h> // strncasecmp
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h> // unix socket
#include <time.h> // time(NULL)
#include <usefull_macros.h>
#include "term.h"
#define BUFLEN 1024 #define BUFLEN 1024
@ -30,76 +42,79 @@
time_t tmeasured[2][NCHANNEL_MAX+1][NCTRLR_MAX+1]; time_t tmeasured[2][NCHANNEL_MAX+1][NCTRLR_MAX+1];
// last temperatures read: [Ngroup][Nsensor][Ncontroller] // last temperatures read: [Ngroup][Nsensor][Ncontroller]
double t_last[2][NCHANNEL_MAX+1][NCTRLR_MAX+1]; double t_last[2][NCHANNEL_MAX+1][NCTRLR_MAX+1];
static int sock = -1; // server UNIX-socket fd
/** /**
* read strings from terminal (ending with '\n') with timeout * wait for answer from socket
* @param L - its length * @param sock - socket fd
* @return 0 in case of error or timeout, 1 in case of socket ready
*/
static int waittoread(int sock){
fd_set fds;
struct timeval timeout;
int rc;
timeout.tv_sec = 0;
timeout.tv_usec = 100;
FD_ZERO(&fds);
FD_SET(sock, &fds);
do{
rc = select(sock+1, &fds, NULL, NULL, &timeout);
if(rc < 0){
if(errno != EINTR){
WARN("select()");
return 0;
}
continue;
}
break;
}while(1);
if(FD_ISSET(sock, &fds)){
//DBG("FD_ISSET");
return 1;
}
return 0;
}
/**
* read string from server, remove trailing '\n'
* @return NULL if nothing was read or pointer to static buffer * @return NULL if nothing was read or pointer to static buffer
*/ */
static char *read_string(){ static char *read_string(){
size_t r = 0, l;
static char buf[BUFLEN]; static char buf[BUFLEN];
int LL = BUFLEN - 1; if(!waittoread(sock)) return NULL;
char *ptr = NULL; int n = read(sock, buf, BUFLEN-1);
static char *optr = NULL; if(n == 0){
if(optr && *optr){ LOGERR("UNIX-socket server disconnected");
ptr = optr; ERRX("Server disconnected");
optr = strchr(optr, '\n');
if(optr) ++optr;
//DBG("got data, roll to next; ptr=%s\noptr=%s",ptr,optr);
return ptr;
} }
ptr = buf; buf[n] = 0;
double d0 = dtime(); if(buf[n-1] == '\n') buf[n-1] = 0;
do{ LOGDBG("SERIAL: %s", buf);
if((l = read_tty(ptr, LL))){ DBG("SERIAL: %s", buf);
r += l; LL -= l; ptr += l; return buf;
if(ptr[-1] == '\n') break;
d0 = dtime();
}
}while(dtime() - d0 < WAIT_TMOUT && LL);
if(r){
buf[r] = 0;
//DBG("r=%zd, got string: %s", r, buf);
optr = strchr(buf, '\n');
if(optr) ++optr;
return buf;
}
return NULL;
} }
/** /**
* Try to connect to `device` at BAUD_RATE speed * Try to connect to UNIX socket `path`
* @return connection speed if success or 0 * @return FALSE if failed
*/ */
void try_connect(char *device){ int try_connect(char *path){
if(!device) return; if(!path) return FALSE;
char tmpbuf[4096]; struct sockaddr_un saddr = {0};
fflush(stdout); saddr.sun_family = AF_UNIX;
tty_init(device); strncpy(saddr.sun_path, path, 106); // if sun_path[0] == 0 we don't create a file
read_tty(tmpbuf, 4096); // clear rbuf if((sock = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0){ // or SOCK_STREAM?
LOG("Connected to %s", device); WARN("socket()");
} LOGERR("socket()");
return FALSE;
/**
* run terminal emulation: send user's commands and show answers
*/
void run_terminal(){
green(_("Work in terminal mode without echo\n"));
int rb;
char buf[BUFLEN];
size_t l;
setup_con();
while(1){
if((l = read_tty(buf, BUFLEN - 1))){
buf[l] = 0;
printf("%s", buf);
}
if((rb = read_console())){
buf[0] = (char) rb;
write_tty(buf, 1);
}
} }
if(connect(sock, &saddr, sizeof(saddr)) == -1){
WARN("connect()");
LOGERR("connect()");
return FALSE;
}
LOGMSG("Connected to server");
return TRUE;
} }
/** /**
@ -172,18 +187,22 @@ static int send_cmd(int N, char cmd){
// clear all incomint data // clear all incomint data
while(read_string()); while(read_string());
DBG("send cmd %s", buf); DBG("send cmd %s", buf);
if(write_tty(buf, n)) return 1; if(n != send(sock, buf, n, 0)) return 1;
if(N == 0) return 0; if(N == 0) return 0;
if((rtn = read_string())){ double t0 = dtime();
DBG("read_string: %s", rtn); while(dtime() - t0 < T_POLLING_TMOUT){
if(*rtn == cmd) return 0; if((rtn = read_string())){
DBG("read_string: %s", rtn);
if(*rtn == cmd) return 0;
}
} }
DBG("No answer");
return 1; return 1;
} }
/** /**
* Poll sensor for new dataportion * Poll sensor for new dataportion
* @param N - number of controller (1..7) * @param N - number of controller (0..7)
* @return: 0 if no data received or controller is absent, number of data points if valid data received * @return: 0 if no data received or controller is absent, number of data points if valid data received
*/ */
int poll_sensors(int N){ int poll_sensors(int N){
@ -229,8 +248,12 @@ int check_sensors(){
for(i = 1; i <= NCTRLR_MAX; ++i){ for(i = 1; i <= NCTRLR_MAX; ++i){
//red("i = %d\n", i); //red("i = %d\n", i);
double t0 = dtime(); double t0 = dtime();
if(send_cmd(i, CMD_PING)){
usleep(100000);
--i;
continue;
}
while(dtime() - t0 < POLLING_TMOUT){ while(dtime() - t0 < POLLING_TMOUT){
if(send_cmd(i, CMD_PING)) continue;
if((ans = read_string())){ if((ans = read_string())){
//DBG("got: %s", ans); //DBG("got: %s", ans);
if(0 == strncmp(ans, ANS_PONG, sizeof(ANS_PONG)-1)){ if(0 == strncmp(ans, ANS_PONG, sizeof(ANS_PONG)-1)){
@ -238,7 +261,7 @@ int check_sensors(){
if(i == ans[sizeof(ANS_PONG)-1] - '0'){ if(i == ans[sizeof(ANS_PONG)-1] - '0'){
++found; ++found;
green(_("Found controller #%d\n"), i); green(_("Found controller #%d\n"), i);
LOG("Found controller #%d", i); LOGMSG("Found controller #%d", i);
break; break;
} }
} }

View File

@ -22,6 +22,8 @@
#ifndef __TERM_H__ #ifndef __TERM_H__
#define __TERM_H__ #define __TERM_H__
#include <time.h>
#include "sens_place.h" // NCTRLR #include "sens_place.h" // NCTRLR
// Terminal timeout (seconds) // Terminal timeout (seconds)
@ -56,8 +58,7 @@ typedef enum{
TRANS_TIMEOUT // no data in WAIT_TMOUT TRANS_TIMEOUT // no data in WAIT_TMOUT
} trans_status; } trans_status;
void run_terminal(); int try_connect(char *path);
void try_connect(char *device);
int poll_sensors(int N); int poll_sensors(int N);
int check_sensors(); int check_sensors();
void turn_all_off(); void turn_all_off();

View File

@ -1,593 +0,0 @@
/*
* usefull_macros.h - a set of usefull functions: memory, color etc
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "usefull_macros.h"
#include <time.h>
#include <linux/limits.h> // PATH_MAX
#include <signal.h>
/**
* function for different purposes that need to know time intervals
* @return double value: time in seconds
*/
double dtime(){
double t;
struct timeval tv;
gettimeofday(&tv, NULL);
t = tv.tv_sec + ((double)tv.tv_usec)/1e6;
return t;
}
/******************************************************************************\
* Coloured terminal
\******************************************************************************/
int globErr = 0; // errno for WARN/ERR
// pointers to coloured output printf
int (*red)(const char *fmt, ...);
int (*green)(const char *fmt, ...);
int (*_WARN)(const char *fmt, ...);
/*
* format red / green messages
* name: r_pr_, g_pr_
* @param fmt ... - printf-like format
* @return number of printed symbols
*/
int r_pr_(const char *fmt, ...){
va_list ar; int i;
printf(RED);
va_start(ar, fmt);
i = vprintf(fmt, ar);
va_end(ar);
printf(OLDCOLOR);
return i;
}
int g_pr_(const char *fmt, ...){
va_list ar; int i;
printf(GREEN);
va_start(ar, fmt);
i = vprintf(fmt, ar);
va_end(ar);
printf(OLDCOLOR);
return i;
}
/*
* print red error/warning messages (if output is a tty)
* @param fmt ... - printf-like format
* @return number of printed symbols
*/
int r_WARN(const char *fmt, ...){
va_list ar; int i = 1;
fprintf(stderr, RED);
va_start(ar, fmt);
if(globErr){
errno = globErr;
vwarn(fmt, ar);
errno = 0;
}else
i = vfprintf(stderr, fmt, ar);
va_end(ar);
i++;
fprintf(stderr, OLDCOLOR "\n");
return i;
}
static const char stars[] = "****************************************";
/*
* notty variants of coloured printf
* name: s_WARN, r_pr_notty
* @param fmt ... - printf-like format
* @return number of printed symbols
*/
int s_WARN(const char *fmt, ...){
va_list ar; int i;
i = fprintf(stderr, "\n%s\n", stars);
va_start(ar, fmt);
if(globErr){
errno = globErr;
vwarn(fmt, ar);
errno = 0;
}else
i = +vfprintf(stderr, fmt, ar);
va_end(ar);
i += fprintf(stderr, "\n%s\n", stars);
i += fprintf(stderr, "\n");
return i;
}
int r_pr_notty(const char *fmt, ...){
va_list ar; int i;
i = printf("\n%s\n", stars);
va_start(ar, fmt);
i += vprintf(fmt, ar);
va_end(ar);
i += printf("\n%s\n", stars);
return i;
}
/**
* Run this function in the beginning of main() to setup locale & coloured output
*/
void initial_setup(){
// setup coloured output
if(isatty(STDOUT_FILENO)){ // make color output in tty
red = r_pr_; green = g_pr_;
}else{ // no colors in case of pipe
red = r_pr_notty; green = printf;
}
if(isatty(STDERR_FILENO)) _WARN = r_WARN;
else _WARN = s_WARN;
// Setup locale
setlocale(LC_ALL, "");
setlocale(LC_NUMERIC, "C");
#if defined GETTEXT_PACKAGE && defined LOCALEDIR
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
textdomain(GETTEXT_PACKAGE);
#endif
}
/******************************************************************************\
* Memory
\******************************************************************************/
/*
* safe memory allocation for macro ALLOC
* @param N - number of elements to allocate
* @param S - size of single element (typically sizeof)
* @return pointer to allocated memory area
*/
void *my_alloc(size_t N, size_t S){
void *p = calloc(N, S);
if(!p) ERR("malloc");
//assert(p);
return p;
}
/**
* Mmap file to a memory area
*
* @param filename (i) - name of file to mmap
* @return stuct with mmap'ed file or die
*/
mmapbuf *My_mmap(char *filename){
int fd;
char *ptr;
size_t Mlen;
struct stat statbuf;
/// "îÅ ÚÁÄÁÎÏ ÉÍÑ ÆÁÊÌÁ!"
if(!filename){
WARNX(_("No filename given!"));
return NULL;
}
if((fd = open(filename, O_RDONLY)) < 0){
/// "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s ÄÌÑ ÞÔÅÎÉÑ"
WARN(_("Can't open %s for reading"), filename);
return NULL;
}
if(fstat (fd, &statbuf) < 0){
/// "îÅ ÍÏÇÕ ×ÙÐÏÌÎÉÔØ stat %s"
WARN(_("Can't stat %s"), filename);
close(fd);
return NULL;
}
Mlen = statbuf.st_size;
if((ptr = mmap (0, Mlen, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED){
/// "ïÛÉÂËÁ mmap"
WARN(_("Mmap error for input"));
close(fd);
return NULL;
}
/// "îÅ ÍÏÇÕ ÚÁËÒÙÔØ mmap'ÎÕÔÙÊ ÆÁÊÌ"
if(close(fd)) WARN(_("Can't close mmap'ed file"));
mmapbuf *ret = MALLOC(mmapbuf, 1);
ret->data = ptr;
ret->len = Mlen;
return ret;
}
void My_munmap(mmapbuf *b){
if(munmap(b->data, b->len)){
/// "îÅ ÍÏÇÕ munmap"
WARN(_("Can't munmap"));
}
FREE(b);
}
/******************************************************************************\
* Terminal in no-echo mode
\******************************************************************************/
static struct termios oldt, newt; // terminal flags
static int console_changed = 0;
// run on exit:
void restore_console(){
if(console_changed)
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
console_changed = 0;
}
// initial setup:
void setup_con(){
if(console_changed) return;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){
/// "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"
WARN(_("Can't setup console"));
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
signals(0); //quit?
}
console_changed = 1;
}
/**
* Read character from console without echo
* @return char readed
*/
int read_console(){
int rb;
struct timeval tv;
int retval;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO, &rfds);
tv.tv_sec = 0; tv.tv_usec = 10000;
retval = select(1, &rfds, NULL, NULL, &tv);
if(!retval) rb = 0;
else {
if(FD_ISSET(STDIN_FILENO, &rfds)) rb = getchar();
else rb = 0;
}
return rb;
}
/**
* getchar() without echo
* wait until at least one character pressed
* @return character readed
*/
int mygetchar(){ // getchar() without need of pressing ENTER
int ret;
do ret = read_console();
while(ret == 0);
return ret;
}
/******************************************************************************\
* TTY with select()
\******************************************************************************/
static struct termio oldtty, tty; // TTY flags
static int comfd = -1; // TTY fd
// run on exit:
void restore_tty(){
if(comfd == -1) return;
ioctl(comfd, TCSANOW, &oldtty ); // return TTY to previous state
close(comfd);
comfd = -1;
}
#ifndef BAUD_RATE
#define BAUD_RATE B9600
#endif
// init:
void tty_init(char *comdev){
DBG("\nOpen port %s ...", comdev);
do{
comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK);
}while (comfd == -1 && errno == EINTR);
if(comfd < 0){
WARN("Can't use port %s",comdev);
signals(-1); // quit?
}
// make exclusive open
if(ioctl(comfd, TIOCEXCL)){
WARN(_("Can't do exclusive open"));
close(comfd);
signals(2);
}
DBG(" OK\nGet current settings... ");
if(ioctl(comfd,TCGETA,&oldtty) < 0){ // Get settings
/// "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÎÁÓÔÒÏÊËÉ"
WARN(_("Can't get settings"));
signals(-1);
}
tty = oldtty;
tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
tty.c_oflag = 0;
tty.c_cflag = BAUD_RATE|CS8|CREAD|CLOCAL; // 9.6k, 8N1, RW, ignore line ctrl
tty.c_cc[VMIN] = 0; // non-canonical mode
tty.c_cc[VTIME] = 5;
if(ioctl(comfd,TCSETA,&tty) < 0){
/// "îÅ ÍÏÇÕ ÕÓÔÁÎÏ×ÉÔØ ÎÁÓÔÒÏÊËÉ"
WARN(_("Can't set settings"));
signals(-1);
}
DBG(" OK");
}
/**
* Read data from TTY
* @param buff (o) - buffer for data read
* @param length - buffer len
* @return amount of bytes read
*/
size_t read_tty(char *buff, size_t length){
ssize_t L = 0, l;
char *ptr = buff;
fd_set rfds;
struct timeval tv;
int retval;
do{
l = 0;
FD_ZERO(&rfds);
FD_SET(comfd, &rfds);
// wait for 100ms
tv.tv_sec = 0; tv.tv_usec = 100000;
retval = select(comfd + 1, &rfds, NULL, NULL, &tv);
if (retval < 1) break; // retval == 0 if there's no data, retval == 1 for EINTR
if(FD_ISSET(comfd, &rfds)){
if((l = read(comfd, ptr, length)) < 1){
//return 0;
ERR("USB disconnected!");
}
ptr += l; L += l;
length -= l;
}
}while(l);
return (size_t)L;
}
int write_tty(char *buff, size_t length){
ssize_t L = write(comfd, buff, length);
if((size_t)L != length){
/// "ïÛÉÂËÁ ÚÁÐÉÓÉ!"
ERR("Write error");
}
return 0;
}
/**
* Safely convert data from string to double
*
* @param num (o) - double number read from string
* @param str (i) - input string
* @return 1 if success, 0 if fails
*/
int str2double(double *num, const char *str){
double res;
char *endptr;
if(!str) return 0;
res = strtod(str, &endptr);
if(endptr == str || *str == '\0' || *endptr != '\0'){
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÞÉÓÌÁ double!"
WARNX("Wrong double number format!");
return FALSE;
}
if(num) *num = res; // you may run it like myatod(NULL, str) to test wether str is double number
return TRUE;
}
//////////// logging
static Cl_log log = {0};
/**
* @brief Cl_createlog - create log file: init mutex, test file open ability
* @param log - log structure
* @return 0 if all OK
*/
int Cl_createlog(char *logname){
if(log.logpath){
FREE(log.logpath);
pthread_mutex_destroy(&log.mutex);
}
FILE *logfd = fopen(logname, "a");
if(!logfd){
WARN("Can't open log file");
return 2;
}
log.logpath = strdup(logname);
fclose(logfd);
if(pthread_mutex_init(&log.mutex, NULL)){
WARN("Can't init log mutes");
return 3;
}
return 0;
}
/**
* @brief Cl_putlog - put message to log file with/without timestamp
* @param timest - ==1 to put timestamp
* @param log - pointer to log structure
* @param lvl - message loglevel (if lvl > loglevel, message won't be printed)
* @param fmt - format and the rest part of message
* @return amount of symbols saved in file
*/
int Cl_putlogt(const char *fmt, ...){
if(pthread_mutex_lock(&log.mutex)){
WARN("Can't lock log mutex");
return 0;
}
int i = 0;
FILE *logfd = fopen(log.logpath, "a");
if(!logfd) goto rtn;
char strtm[128];
time_t t = time(NULL);
struct tm *curtm = localtime(&t);
strftime(strtm, 128, "%Y/%m/%d-%H:%M:%S", curtm);
i = fprintf(logfd, "%s\t", strtm);
va_list ar;
va_start(ar, fmt);
i += vfprintf(logfd, fmt, ar);
va_end(ar);
i += fprintf(logfd, "\n");
fclose(logfd);
rtn:
pthread_mutex_unlock(&log.mutex);
return i;
}
/*
* Copyright (c) 1988, 1993, 1994, 2017
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* 2017-10-14 Niklas Hamb?chen <mail@nh2.me>
* - Extracted signal names mapping from kill.c
*
* Copyright (C) 2014 Sami Kerola <kerolasa@iki.fi>
* Copyright (C) 2014 Karel Zak <kzak@redhat.com>
* Copyright (C) 2017 Niklas Hamb?chen <mail@nh2.me>
*/
// https://github.com/karelzak/util-linux/blob/master/lib/signames.c
static const struct ul_signal_name {
const char *name;
int val;
} ul_signames[] = {
/* POSIX signals */
{ "HUP", SIGHUP }, /* 1 */
{ "INT", SIGINT }, /* 2 */
{ "QUIT", SIGQUIT }, /* 3 */
{ "ILL", SIGILL }, /* 4 */
#ifdef SIGTRAP
{ "TRAP", SIGTRAP }, /* 5 */
#endif
{ "ABRT", SIGABRT }, /* 6 */
#ifdef SIGIOT
{ "IOT", SIGIOT }, /* 6, same as SIGABRT */
#endif
#ifdef SIGEMT
{ "EMT", SIGEMT }, /* 7 (mips,alpha,sparc*) */
#endif
#ifdef SIGBUS
{ "BUS", SIGBUS }, /* 7 (arm,i386,m68k,ppc), 10 (mips,alpha,sparc*) */
#endif
{ "FPE", SIGFPE }, /* 8 */
{ "KILL", SIGKILL }, /* 9 */
{ "USR1", SIGUSR1 }, /* 10 (arm,i386,m68k,ppc), 30 (alpha,sparc*), 16 (mips) */
{ "SEGV", SIGSEGV }, /* 11 */
{ "USR2", SIGUSR2 }, /* 12 (arm,i386,m68k,ppc), 31 (alpha,sparc*), 17 (mips) */
{ "PIPE", SIGPIPE }, /* 13 */
{ "ALRM", SIGALRM }, /* 14 */
{ "TERM", SIGTERM }, /* 15 */
#ifdef SIGSTKFLT
{ "STKFLT", SIGSTKFLT }, /* 16 (arm,i386,m68k,ppc) */
#endif
{ "CHLD", SIGCHLD }, /* 17 (arm,i386,m68k,ppc), 20 (alpha,sparc*), 18 (mips) */
#ifdef SIGCLD
{ "CLD", SIGCLD }, /* same as SIGCHLD (mips) */
#endif
{ "CONT", SIGCONT }, /* 18 (arm,i386,m68k,ppc), 19 (alpha,sparc*), 25 (mips) */
{ "STOP", SIGSTOP }, /* 19 (arm,i386,m68k,ppc), 17 (alpha,sparc*), 23 (mips) */
{ "TSTP", SIGTSTP }, /* 20 (arm,i386,m68k,ppc), 18 (alpha,sparc*), 24 (mips) */
{ "TTIN", SIGTTIN }, /* 21 (arm,i386,m68k,ppc,alpha,sparc*), 26 (mips) */
{ "TTOU", SIGTTOU }, /* 22 (arm,i386,m68k,ppc,alpha,sparc*), 27 (mips) */
#ifdef SIGURG
{ "URG", SIGURG }, /* 23 (arm,i386,m68k,ppc), 16 (alpha,sparc*), 21 (mips) */
#endif
#ifdef SIGXCPU
{ "XCPU", SIGXCPU }, /* 24 (arm,i386,m68k,ppc,alpha,sparc*), 30 (mips) */
#endif
#ifdef SIGXFSZ
{ "XFSZ", SIGXFSZ }, /* 25 (arm,i386,m68k,ppc,alpha,sparc*), 31 (mips) */
#endif
#ifdef SIGVTALRM
{ "VTALRM", SIGVTALRM }, /* 26 (arm,i386,m68k,ppc,alpha,sparc*), 28 (mips) */
#endif
#ifdef SIGPROF
{ "PROF", SIGPROF }, /* 27 (arm,i386,m68k,ppc,alpha,sparc*), 29 (mips) */
#endif
#ifdef SIGWINCH
{ "WINCH", SIGWINCH }, /* 28 (arm,i386,m68k,ppc,alpha,sparc*), 20 (mips) */
#endif
#ifdef SIGIO
{ "IO", SIGIO }, /* 29 (arm,i386,m68k,ppc), 23 (alpha,sparc*), 22 (mips) */
#endif
#ifdef SIGPOLL
{ "POLL", SIGPOLL }, /* same as SIGIO */
#endif
#ifdef SIGINFO
{ "INFO", SIGINFO }, /* 29 (alpha) */
#endif
#ifdef SIGLOST
{ "LOST", SIGLOST }, /* 29 (arm,i386,m68k,ppc,sparc*) */
#endif
#ifdef SIGPWR
{ "PWR", SIGPWR }, /* 30 (arm,i386,m68k,ppc), 29 (alpha,sparc*), 19 (mips) */
#endif
#ifdef SIGUNUSED
{ "UNUSED", SIGUNUSED }, /* 31 (arm,i386,m68k,ppc) */
#endif
#ifdef SIGSYS
{ "SYS", SIGSYS }, /* 31 (mips,alpha,sparc*) */
#endif
};
const char *signum_to_signame(int signum){
size_t n;
static char buf[32];
if(signum < 1) return NULL;
#if defined SIGRTMIN && defined SIGRTMAX
if(signum >= SIGRTMIN && signum <= SIGRTMAX){
snprintf(buf, 32, "SIGRT%d", signum - SIGRTMIN);
return buf;
}
#endif
for (n = 0; n < ARRAY_SIZE(ul_signames); n++) {
if(ul_signames[n].val == signum){
snprintf(buf, 32, "SIG%s", ul_signames[n].name);
return buf;
}
}
return NULL;
}

View File

@ -1,148 +0,0 @@
/*
* usefull_macros.h - a set of usefull macros: memory, color etc
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#pragma once
#ifndef __USEFULL_MACROS_H__
#define __USEFULL_MACROS_H__
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <err.h>
#include <locale.h>
#if defined GETTEXT_PACKAGE && defined LOCALEDIR
/*
* GETTEXT
*/
#include <libintl.h>
#define _(String) gettext(String)
#define gettext_noop(String) String
#define N_(String) gettext_noop(String)
#else
#define _(String) (String)
#define N_(String) (String)
#endif
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <termio.h>
#include <termios.h>
#include <sys/time.h>
#include <sys/types.h>
// unused arguments with -Wall -Werror
#define _U_ __attribute__((__unused__))
/*
* Coloured messages output
*/
#define RED "\033[1;31;40m"
#define GREEN "\033[1;32;40m"
#define OLDCOLOR "\033[0;0;0m"
#ifndef FALSE
#define FALSE (0)
#endif
#ifndef TRUE
#define TRUE (1)
#endif
/*
* ERROR/WARNING messages
*/
extern int globErr;
extern void signals(int sig);
#define ERR(...) do{globErr=errno; Cl_putlogt(__VA_ARGS__); _WARN(__VA_ARGS__); signals(9);}while(0)
#define ERRX(...) do{globErr=0; Cl_putlogt(__VA_ARGS__); _WARN(__VA_ARGS__); signals(9);}while(0)
#define WARN(...) do{globErr=errno; Cl_putlogt(__VA_ARGS__); _WARN(__VA_ARGS__);}while(0)
#define WARNX(...) do{globErr=0; Cl_putlogt(__VA_ARGS__); _WARN(__VA_ARGS__);}while(0)
#define LOG(...) do{Cl_putlogt(__VA_ARGS__); }while(0)
/*
* print function name, debug messages
* debug mode, -DEBUG
*/
#ifdef EBUG
#define FNAME() printf("\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__)
#define DBG(...) do{printf("%s (%s, line %d): ", __func__, __FILE__, __LINE__); \
printf(__VA_ARGS__); printf("\n");} while(0)
#else
#define FNAME() do{}while(0)
#define DBG(...) do{}while(0)
#endif //EBUG
/*
* Memory allocation
*/
#define ALLOC(type, var, size) type * var = ((type *)my_alloc(size, sizeof(type)))
#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type)))
#define FREE(ptr) do{if(ptr){free(ptr); ptr = NULL;}}while(0)
#ifndef DBL_EPSILON
#define DBL_EPSILON (2.2204460492503131e-16)
#endif
double dtime();
// functions for color output in tty & no-color in pipes
extern int (*red)(const char *fmt, ...);
extern int (*_WARN)(const char *fmt, ...);
extern int (*green)(const char *fmt, ...);
void * my_alloc(size_t N, size_t S);
void initial_setup();
// mmap file
typedef struct{
char *data;
size_t len;
} mmapbuf;
mmapbuf *My_mmap(char *filename);
void My_munmap(mmapbuf *b);
void restore_console();
void setup_con();
int read_console();
int mygetchar();
void restore_tty();
void tty_init(char *comdev);
size_t read_tty(char *buff, size_t length);
int write_tty(char *buff, size_t length);
int str2double(double *num, const char *str);
typedef struct{
char *logpath; // full path to logfile
pthread_mutex_t mutex; // log mutex
} Cl_log;
int Cl_createlog(char *logname);
int Cl_putlogt(const char *fmt, ...);
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
const char *signum_to_signame(int signum);
#endif // __USEFULL_MACROS_H__

1021
src/netdaemon/www/T.html Normal file

File diff suppressed because it is too large Load Diff

45
src/netdaemon/www/T0 Normal file
View File

@ -0,0 +1,45 @@
19 -7 -2.85
17 -22 -2.99
15 -13 -2.92
22 -17 -2.80
25 -10 -2.81
27 -4 -2.73
27 4 -2.66
25 10 -2.58
10 25 -2.66
17 22 -2.69
-10 -25 -3.02
-4 -27 -3.11
-3 -20 -3.09
-10 -17 -3.03
-15 -13 -2.94
-19 -7 -2.84
-20 0 -2.78
-19 7 -2.84
-15 13 -2.83
-10 17 -2.76
-3 20 -2.75
3 20 -2.79
10 17 -2.76
9 9 -2.74
3 13 -2.73
-9 9 -2.76
-3 13 -2.70
-13 3 -2.77
-13 -3 -2.82
-9 -9 -2.86
-3 -13 -2.94
3 -13 -2.90
3 -20 -3.03
4 -27 -3.08
10 -17 -3.02
10 -25 -2.97
9 -9 -2.85
3 -5 -2.80
-3 -5 -2.81
-6 0 -2.75
-3 5 -2.78
3 5 -2.66
6 0 -2.73
13 -3 -2.73
13 3 -2.67

55395
src/netdaemon/www/T0.html Normal file

File diff suppressed because it is too large Load Diff

14
src/netdaemon/www/T1 Normal file
View File

@ -0,0 +1,14 @@
17 -10 -2.46
24 -14 -2.01
-17 -10 -2.50
-17 10 -2.44
0 20 -2.38
11 7 -2.40
0 13 -2.36
-11 7 -2.41
-11 -7 -2.47
0 -13 -2.56
0 -20 -2.56
0 -27 -2.34
11 -7 -2.48
0 -6 -2.52

54820
src/netdaemon/www/T1.html Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=koi8-r">
<title>Mirror temperatures main controller</title>
</head>
<body>
<h1>çÒÁÆÉËÉ ÔÅÍÐÅÒÁÔÕÒ:</h1>
<ul>
<li><a href="T0.html">×ÅÒÈÎÉÊ ÓÌÏÊ ÚÅÒËÁÌÁ</a>;</li>
<li><a href="T1.html">ÎÉÖÎÑÑ ÐÏ×ÅÒÈÎÏÓÔØ</a>;</li>
<!--li><a href="Tgrad.html">ÇÒÁÄÉÅÎÔ</a> (×ÅÒÈ ÍÉÎÕÓ ÎÉÚ).</li-->
</ul>
<div style="position: absolute; top: 1px; right: 1px;">
\copyright åÍÅÌØÑÎÏ× ü.÷., eddy@sao.ru
</div>
</body>
</html>

108
src/netdaemon/www/js/README Normal file
View File

@ -0,0 +1,108 @@
Notes on using the gnuplot canvas terminal driver to create web pages
=====================================================================
1) Using UTF-8 characters in your plots
I expect that eventually web browsers will learn to draw text onto the HTML
canvas element using their native font-handling code. But until then we
have to refer to an external character drawing library.
The gnuplot package includes two versions of a script to draw characters on
the canvas. The first one, canvastext.js, was written by Jim Studt. It only
knows about the 7-bit ascii characters. The second one, canvasmath.js, is
an expanded version of Jim Studt's script that I wrote to handle UTF8.
It contains glyphs for the first two unicode code pages (latin-1), the greek
alphabet, and select math and physics symbols. You can use this to replace
canvastext.js if you like, or refer to it explicitly in plots that need
non-ascii characters.
2) Browser dependencies
As of this time (May 2009) the HTML canvas element is supported by the
latest versions of Opera, Safari, Firefox, and Konqueror. However, each
of these has quirks.
For instance, only Firefox makes it easy to click and drag with the middle
or right mouse buttons; the other browsers try to pop up various menus instead.
We try to override this, but it doesn't always work.
Conversely, Opera and Safari make it easy to use hot keys ('e' for refresh,
'r' to toggle the ruler, etc), but I have not managed to get this to work
in Firefox.
If you run into problems, please try several browsers before concluding that it
is a gnuplot problem. If you figure out a work-around for one of these browser
quirks, please tell me so that we can try to incorporate into gnuplot output.
3) Creating a basic web page with a single mouseable plot
The canvas terminal driver itself will create a basic html document containing
a mousable plot. The command options to do this are
set term canvas standalone mousing jsdir "http://myserver"
set output 'myplot.html'
This document contains
- a reference to style sheet gnuplot_mouse.css
- a reference to support script gnuplot_mouse.js
- a javascript function named 'gnuplot_canvas'
- a canvas element named 'gnuplot_canvas' that will be drawn in by the
javascript function of the same name
- an html table containing the readout for mouse coordinates, as well
as clickable icons for additional mousing operations
The *.css and *.js references point back to whatever source URL you specified
in the jsdir option to the 'set term' command. For example:
<link type="text/css" href="http://myserver/gnuplot_mouse.css" rel="stylesheet">
In order for viewers to use your plot document, they must be able to access the
*.css and *.js files via the URL embedded in the document.
4) Creating a web page with multiple mouseable plots
In order to embed multiple plots in a single document, you must
provide your own html framework. You can use the one created by the canvas
driver in standalone mode as a starting point, including the references to
gnuplot_mouse.css and gnuplot_mouse.js. However, instead of a single
javascript routine named gnuplot_canvas() that always draws the same plot,
you must provide a wrapper routine with the same name that connects the
mousing code to whichever plot is currently active. Here is an example:
- create the individual plots as separate javascript files
set term canvas name 'plot1'
set output 'plot1.js'
plot something
set term canvas name 'plot2'
set output 'plot2.js'
plot something_else
- create your html wrapper, including a script block such as the one below.
You must use these specific variable names, as they are referenced by the
javascript code produced by the canvas terminal.
<script type="text/javascript">
var canvas, ctx;
var grid_lines = true;
var zoomed = false;
var active_plot_name = "gnuplot_canvas";
var active_plot = dummyplot;
function dummyplot() {};
function gnuplot_canvas( plot ) { active_plot(); };
</script>
- add one or more mousing output tables. As a model, you can use either the one
in a standalone plot or the file .../demo/html/mousebox.template.
The table id and text span ids in the "mousebox" table must match the ones
by your individual plots in order for mousing readout to work.
- each of the individual plots in the document should be explicitly called when
the document is loaded. For example, for each plot there could be a block
of html similar to the following:
<canvas id="plot1" width="600" height="400" tabindex="0"></canvas>
<script src="plot1.js"></script>
<script>
if (window.attachEvent) {window.attachEvent('onload', plot1);}
else if (window.addEventListener) {window.addEventListener('load', plot1, false);}
</script>
Alternatively, the onload attributes could be set in the html <BODY> element
- Ethan A Merritt (sfeam@users.sourceforge.net)
May 2009

View File

@ -0,0 +1,413 @@
/*
* $Id: canvasmath.js,v 1.11 2016/08/07 00:06:31 sfeam Exp $
*/
// The canvastext.js code was released to the public domain by Jim Studt, 2007.
// He may keep some sort of up to date copy at http://www.federated.com/~jim/canvastext/
// Mar 2009 Ethan A Merritt (EAM) Modify code to work in non-ascii environments.
// Add Latin-1, Hershey simplex Greek, and math symbols with unicode indexing.
// Apr 2012 Ethan A Merritt Composite widths, tab, newline
// Nov 2012 Ethan A Merritt Additional UTF8 mathematical operators
//
var CanvasTextFunctions = { };
CanvasTextFunctions.letters = {
' ': { width: 16, points: [] },
'!': { width: 10, points: [[5,21],[5,7],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] },
'"': { width: 16, points: [[4,21],[4,14],[-1,-1],[12,21],[12,14]] },
'#': { width: 21, points: [[11,25],[4,-7],[-1,-1],[17,25],[10,-7],[-1,-1],[4,12],[18,12],[-1,-1],[3,6],[17,6]] },
'$': { width: 20, points: [[8,25],[8,-4],[-1,-1],[12,25],[12,-4],[-1,-1],[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] },
'%': { width: 24, points: [[21,21],[3,0],[-1,-1],[8,21],[10,19],[10,17],[9,15],[7,14],[5,14],[3,16],[3,18],[4,20],[6,21],[8,21],[10,20],[13,19],[16,19],[19,20],[21,21],[-1,-1],[17,7],[15,6],[14,4],[14,2],[16,0],[18,0],[20,1],[21,3],[21,5],[19,7],[17,7]] },
'&': { width: 26, points: [[23,12],[23,13],[22,14],[21,14],[20,13],[19,11],[17,6],[15,3],[13,1],[11,0],[7,0],[5,1],[4,2],[3,4],[3,6],[4,8],[5,9],[12,13],[13,14],[14,16],[14,18],[13,20],[11,21],[9,20],[8,18],[8,16],[9,13],[11,10],[16,3],[18,1],[20,0],[22,0],[23,1],[23,2]] },
'\'': { width: 10, points: [[5,19],[4,20],[5,21],[6,20],[6,18],[5,16],[4,15]] },
'(': { width: 14, points: [[11,25],[9,23],[7,20],[5,16],[4,11],[4,7],[5,2],[7,-2],[9,-5],[11,-7]] },
')': { width: 14, points: [[3,25],[5,23],[7,20],[9,16],[10,11],[10,7],[9,2],[7,-2],[5,-5],[3,-7]] },
'*': { width: 16, points: [[8,21],[8,9],[-1,-1],[3,18],[13,12],[-1,-1],[13,18],[3,12]] },
'+': { width: 20, points: [[10,15],[10,3],[-1,-1],[4,9],[16,9]] },
',': { width: 10, points: [[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] },
'-': { width: 20, points: [[4,9],[16,9]] },
'.': { width: 10, points: [[5,2],[4,1],[5,0],[6,1],[5,2]] },
'/': { width: 20, points: [[19,25],[1,-7]] },
'0': { width: 20, points: [[9,21],[6,20],[4,17],[3,12],[3,9],[4,4],[6,1],[9,0],[11,0],[14,1],[16,4],[17,9],[17,12],[16,17],[14,20],[11,21],[9,21]] },
'1': { width: 20, points: [[6,17],[8,18],[11,21],[11,0]] },
'2': { width: 20, points: [[4,16],[4,17],[5,19],[6,20],[8,21],[12,21],[14,20],[15,19],[16,17],[16,15],[15,13],[13,10],[3,0],[17,0]] },
'3': { width: 20, points: [[5,21],[16,21],[10,13],[13,13],[15,12],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] },
'4': { width: 20, points: [[13,21],[3,7],[18,7],[-1,-1],[13,21],[13,0]] },
'5': { width: 20, points: [[15,21],[5,21],[4,12],[5,13],[8,14],[11,14],[14,13],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] },
'6': { width: 20, points: [[16,18],[15,20],[12,21],[10,21],[7,20],[5,17],[4,12],[4,7],[5,3],[7,1],[10,0],[11,0],[14,1],[16,3],[17,6],[17,7],[16,10],[14,12],[11,13],[10,13],[7,12],[5,10],[4,7]] },
'7': { width: 20, points: [[17,21],[7,0],[-1,-1],[3,21],[17,21]] },
'8': { width: 20, points: [[8,21],[5,20],[4,18],[4,16],[5,14],[7,13],[11,12],[14,11],[16,9],[17,7],[17,4],[16,2],[15,1],[12,0],[8,0],[5,1],[4,2],[3,4],[3,7],[4,9],[6,11],[9,12],[13,13],[15,14],[16,16],[16,18],[15,20],[12,21],[8,21]] },
'9': { width: 20, points: [[16,14],[15,11],[13,9],[10,8],[9,8],[6,9],[4,11],[3,14],[3,15],[4,18],[6,20],[9,21],[10,21],[13,20],[15,18],[16,14],[16,9],[15,4],[13,1],[10,0],[8,0],[5,1],[4,3]] },
':': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] },
';': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] },
'<': { width: 24, points: [[20,18],[4,9],[20,0]] },
'=': { width: 24, points: [[3,12],[20,12],[-1,-1],[3,6],[20,6]] },
'>': { width: 24, points: [[4,18],[20,9],[4,0]] },
'?': { width: 18, points: [[3,16],[3,17],[4,19],[5,20],[7,21],[11,21],[13,20],[14,19],[15,17],[15,15],[14,13],[13,12],[9,10],[9,7],[-1,-1],[9,2],[8,1],[9,0],[10,1],[9,2]] },
'@': { width: 27, points: [[18,13],[17,15],[15,16],[12,16],[10,15],[9,14],[8,11],[8,8],[9,6],[11,5],[14,5],[16,6],[17,8],[-1,-1],[12,16],[10,14],[9,11],[9,8],[10,6],[11,5],[-1,-1],[18,16],[17,8],[17,6],[19,5],[21,5],[23,7],[24,10],[24,12],[23,15],[22,17],[20,19],[18,20],[15,21],[12,21],[9,20],[7,19],[5,17],[4,15],[3,12],[3,9],[4,6],[5,4],[7,2],[9,1],[12,0],[15,0],[18,1],[20,2],[21,3],[-1,-1],[19,16],[18,8],[18,6],[19,5]] },
'A': { width: 18, points: [[9,21],[1,0],[-1,-1],[9,21],[17,0],[-1,-1],[4,7],[14,7]] },
'B': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[-1,-1],[4,11],[13,11],[16,10],[17,9],[18,7],[18,4],[17,2],[16,1],[13,0],[4,0]] },
'C': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5]] },
'D': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[11,21],[14,20],[16,18],[17,16],[18,13],[18,8],[17,5],[16,3],[14,1],[11,0],[4,0]] },
'E': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11],[-1,-1],[4,0],[17,0]] },
'F': { width: 18, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11]] },
'G': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[18,8],[-1,-1],[13,8],[18,8]] },
'H': { width: 22, points: [[4,21],[4,0],[-1,-1],[18,21],[18,0],[-1,-1],[4,11],[18,11]] },
'I': { width: 8, points: [[4,21],[4,0]] },
'J': { width: 16, points: [[12,21],[12,5],[11,2],[10,1],[8,0],[6,0],[4,1],[3,2],[2,5],[2,7]] },
'K': { width: 21, points: [[4,21],[4,0],[-1,-1],[18,21],[4,7],[-1,-1],[9,12],[18,0]] },
'L': { width: 17, points: [[4,21],[4,0],[-1,-1],[4,0],[16,0]] },
'M': { width: 24, points: [[4,21],[4,0],[-1,-1],[4,21],[12,0],[-1,-1],[20,21],[12,0],[-1,-1],[20,21],[20,0]] },
'N': { width: 22, points: [[4,21],[4,0],[-1,-1],[4,21],[18,0],[-1,-1],[18,21],[18,0]] },
'O': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21]] },
'P': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,14],[17,12],[16,11],[13,10],[4,10]] },
'Q': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21],[-1,-1],[12,4],[18,-2]] },
'R': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[4,11],[-1,-1],[11,11],[18,0]] },
'S': { width: 20, points: [[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] },
'T': { width: 16, points: [[8,21],[8,0],[-1,-1],[1,21],[15,21]] },
'U': { width: 22, points: [[4,21],[4,6],[5,3],[7,1],[10,0],[12,0],[15,1],[17,3],[18,6],[18,21]] },
'V': { width: 18, points: [[1,21],[9,0],[-1,-1],[17,21],[9,0]] },
'W': { width: 24, points: [[2,21],[7,0],[-1,-1],[12,21],[7,0],[-1,-1],[12,21],[17,0],[-1,-1],[22,21],[17,0]] },
'X': { width: 20, points: [[3,21],[17,0],[-1,-1],[17,21],[3,0]] },
'Y': { width: 18, points: [[1,21],[9,11],[9,0],[-1,-1],[17,21],[9,11]] },
'Z': { width: 20, points: [[17,21],[3,0],[-1,-1],[3,21],[17,21],[-1,-1],[3,0],[17,0]] },
'[': { width: 14, points: [[4,25],[4,-7],[-1,-1],[5,25],[5,-7],[-1,-1],[4,25],[11,25],[-1,-1],[4,-7],[11,-7]] },
'\\': { width: 14, points: [[0,21],[14,-3]] },
']': { width: 14, points: [[9,25],[9,-7],[-1,-1],[10,25],[10,-7],[-1,-1],[3,25],[10,25],[-1,-1],[3,-7],[10,-7]] },
'^': { width: 16, points: [[5,15],[8,19],[11,15],[-1,-1],[2,12],[8,18],[14,12]] },
'_': { width: 16, points: [[0,-2],[16,-2]] },
'`': { width: 10, points: [[6,21],[5,20],[4,18],[4,16],[5,15],[6,16],[5,17]] },
'a': { width: 19, points: [[15,14],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'b': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] },
'c': { width: 18, points: [[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'd': { width: 19, points: [[15,21],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'e': { width: 18, points: [[3,8],[15,8],[15,10],[14,12],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'f': { width: 12, points: [[10,21],[8,21],[6,20],[5,17],[5,0],[-1,-1],[2,14],[9,14]] },
'g': { width: 19, points: [[15,14],[15,-2],[14,-5],[13,-6],[11,-7],[8,-7],[6,-6],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'h': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] },
'i': { width: 8, points: [[3,21],[4,20],[5,21],[4,22],[3,21],[-1,-1],[4,14],[4,0]] },
'j': { width: 10, points: [[5,21],[6,20],[7,21],[6,22],[5,21],[-1,-1],[6,14],[6,-3],[5,-6],[3,-7],[1,-7]] },
'k': { width: 17, points: [[4,21],[4,0],[-1,-1],[14,14],[4,4],[-1,-1],[8,8],[15,0]] },
'l': { width: 8, points: [[4,21],[4,0]] },
'm': { width: 30, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0],[-1,-1],[15,10],[18,13],[20,14],[23,14],[25,13],[26,10],[26,0]] },
'n': { width: 19, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] },
'o': { width: 19, points: [[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3],[16,6],[16,8],[15,11],[13,13],[11,14],[8,14]] },
'p': { width: 19, points: [[4,14],[4,-7],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] },
'q': { width: 19, points: [[15,14],[15,-7],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'r': { width: 13, points: [[4,14],[4,0],[-1,-1],[4,8],[5,11],[7,13],[9,14],[12,14]] },
's': { width: 17, points: [[14,11],[13,13],[10,14],[7,14],[4,13],[3,11],[4,9],[6,8],[11,7],[13,6],[14,4],[14,3],[13,1],[10,0],[7,0],[4,1],[3,3]] },
't': { width: 12, points: [[5,21],[5,4],[6,1],[8,0],[10,0],[-1,-1],[2,14],[9,14]] },
'u': { width: 19, points: [[4,14],[4,4],[5,1],[7,0],[10,0],[12,1],[15,4],[-1,-1],[15,14],[15,0]] },
'v': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0]] },
'w': { width: 22, points: [[3,14],[7,0],[-1,-1],[11,14],[7,0],[-1,-1],[11,14],[15,0],[-1,-1],[19,14],[15,0]] },
'x': { width: 17, points: [[3,14],[14,0],[-1,-1],[14,14],[3,0]] },
'y': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0],[6,-4],[4,-6],[2,-7],[1,-7]] },
'z': { width: 17, points: [[14,14],[3,0],[-1,-1],[3,14],[14,14],[-1,-1],[3,0],[14,0]] },
'{': { width: 14, points: [[9,25],[7,24],[6,23],[5,21],[5,19],[6,17],[7,16],[8,14],[8,12],[6,10],[-1,-1],[7,24],[6,22],[6,20],[7,18],[8,17],[9,15],[9,13],[8,11],[4,9],[8,7],[9,5],[9,3],[8,1],[7,0],[6,-2],[6,-4],[7,-6],[-1,-1],[6,8],[8,6],[8,4],[7,2],[6,1],[5,-1],[5,-3],[6,-5],[7,-6],[9,-7]] },
'|': { width: 8, points: [[4,25],[4,-7]] },
'}': { width: 14, points: [[5,25],[7,24],[8,23],[9,21],[9,19],[8,17],[7,16],[6,14],[6,12],[8,10],[-1,-1],[7,24],[8,22],[8,20],[7,18],[6,17],[5,15],[5,13],[6,11],[10,9],[6,7],[5,5],[5,3],[6,1],[7,0],[8,-2],[8,-4],[7,-6],[-1,-1],[8,8],[6,6],[6,4],[7,2],[8,1],[9,-1],[9,-3],[8,-5],[7,-6],[5,-7]] },
'~': { width: 24, points: [[3,6],[3,8],[4,11],[6,12],[8,12],[10,11],[14,8],[16,7],[18,7],[20,8],[21,10],[-1,-1],[3,8],[4,10],[6,11],[8,11],[10,10],[14,7],[16,6],[18,6],[20,7],[21,10],[21,12]] },
// latin-1 as diacritical + base character
192: { width: 0, points: [[4,28],[10,24],[-99,'A']] },
193: { width: 0, points: [[8,24],[14,28],[-99,'A']] },
194: { width: 0, points: [[5,24],[9,27],[13,24],[-99,'A']] },
195: { width: 0, points: [[4,24],[5,26],[6,27],[8,27], [10,24],[12,24],[13,25],[14,27],[-99,'A']] },
196: { width: 0, points: [[6,25],[6,26],[7,26],[7,25],[-1,-1],[12,25],[12,26],[13,26],[13,25],[-99,'A']] },
197: { width: 0, points: [[8,27],[6,25],[6,23],[8,21],[10,21],[12,23],[12,25],[10,27],[8,27],[-99,'A']]},
198: { width: 11, points: [[3,0],[14,21],[-1,-1],[7,8],[14,8],[-99,'E']] },
199: { width: 0, points: [[10,0],[10,-3],[11,-3],[12,-4],[12,-6],[11,-7],[8,-7],[-99,'C']] },
200: { width: 0, points: [[8,28],[14,24],[-99,'E']] },
201: { width: 0, points: [[6,24],[12,28],[-99,'E']] },
202: { width: 0, points: [[5,24],[9,27],[13,24],[-99,'E']] },
203: { width: 0, points: [[6,25],[6,26],[7,26],[7,25],[-1,-1],[12,25],[12,26],[13,26],[13,25],[-99,'E']] },
204: { width: 0, points: [[-99,'I']] },
205: { width: 0, points: [[-99,'I']] },
206: { width: 0, points: [[-99,'I']] },
207: { width: 0, points: [[-99,'I']] },
209: { width: 0, points: [[6,24],[7,26],[8,27],[10,27], [12,24],[14,24],[15,25],[16,27],[-99,'N']] },
210: { width: 0, points: [[8,28],[14,24],[-99,'O']] },
211: { width: 0, points: [[6,24],[12,28],[-99,'O']] },
212: { width: 0, points: [[7,24],[11,27],[15,24],[-99,'O']] },
213: { width: 0, points: [[6,24],[7,26],[8,27],[10,27], [12,24],[14,24],[15,25],[16,27],[-99,'O']] },
214: { width: 0, points: [[8,25],[8,26],[9,26],[9,25],[-1,-1],[14,25],[14,26],[15,26],[15,25],[-99,'O']] },
216: { width: 0, points: [[3,0],[19,21],[-99,'O']] },
217: { width: 0, points: [[8,28],[14,24],[-99,'U']] },
218: { width: 0, points: [[6,24],[12,28],[-99,'U']] },
219: { width: 0, points: [[7,24],[11,27],[15,24],[-99,'U']] },
220: { width: 0, points: [[8,25],[8,26],[9,26],[9,25],[-1,-1],[14,25],[14,26],[15,26],[15,25],[-99,'U']] },
224: { width: 0, points: [[8,21],[14,17],[-99,'a']] },
225: { width: 0, points: [[6,17],[12,21],[-99,'a']] },
226: { width: 0, points: [[5,17],[9,20],[13,17],[-99,'a']] },
227: { width: 0, points: [[4,17],[5,19],[6,20],[8,20], [10,17],[12,17],[13,18],[14,20],[-99,'a']] },
228: { width: 0, points: [[6,18],[6,19],[7,19],[7,18],[-1,-1],[12,18],[12,19],[13,19],[13,18],[-99,'a']] },
229: { width: 0, points: [[9,21],[7,19],[7,18],[9,16],[10,16],[12,18],[12,19],[10,21],[8,21],[-99,'a']] },
231: { width: 0, points: [[10,0],[10,-3],[11,-3],[12,-4],[12,-6],[11,-7],[8,-7],[-99,'c']] },
232: { width: 0, points: [[8,21],[14,17],[-99,'e']] },
233: { width: 0, points: [[6,17],[12,21],[-99,'e']] },
234: { width: 0, points: [[5,17],[9,20],[13,17],[-99,'e']] },
235: { width: 0, points: [[6,18],[6,19],[7,19],[7,18],[-1,-1],[12,18],[12,19],[13,19],[13,18],[-99,'e']] },
236: { width: 8, points: [[4,14],[4,0],[-1,-1],[1,21],[4,18]] },
237: { width: 8, points: [[4,14],[4,0],[-1,-1],[7,21],[4,18]] },
238: { width: 8, points: [[4,14],[4,0],[-1,-1],[1,17],[4,20],[7,17]] },
239: { width: 10, points: [[5,14],[5,0],[-1,-1],[2,18],[2,19],[3,19],[3,18],[-1,-1],[7,18],[7,19],[8,19],[8,18]] },
241: { width: 0, points: [[5,17],[6,19],[7,20],[9,20], [11,17],[13,17],[14,18],[15,20],[-99,'n']] },
242: { width: 0, points: [[8,21],[14,17],[-99,'o']] },
243: { width: 0, points: [[6,17],[12,21],[-99,'o']] },
244: { width: 0, points: [[5,17],[9,20],[13,17],[-99,'o']] },
245: { width: 0, points: [[4,17],[5,19],[6,20],[8,20], [10,17],[12,17],[13,18],[14,20],[-99,'o']] },
246: { width: 0, points: [[6,18],[6,19],[7,19],[7,18],[-1,-1],[12,18],[12,19],[13,19],[13,18],[-99,'o']] },
247: { width: 20, points: [[4,9],[16,9],[-1,-1],[10,14],[10,13],[11,13],[11,14],[-1,-1],[10,4],[10,5],[11,5],[11,4]] },
248: { width: 0, points: [[3,0],[17,14],[-99,'o']] },
249: { width: 0, points: [[8,21],[14,17],[-99,'u']] },
250: { width: 0, points: [[6,17],[12,21],[-99,'u']] },
251: { width: 0, points: [[5,17],[9,20],[13,17],[-99,'u']] },
252: { width: 0, points: [[6,18],[6,19],[7,19],[7,18],[-1,-1],[12,18],[12,19],[13,19],[13,18],[-99,'u']] },
253: { width: 0, points: [[12,21],[6,17],[-99,'y']] },
255: { width: 0, points: [[6,18],[6,19],[7,19],[7,18],[-1,-1],[12,18],[12,19],[13,19],[13,18],[-99,'y']] },
// Hershey simplex greek font
913: { width: 18, points: [[9,22],[1,1],[-1,-1],[9,22],[17,1],[-1,-1],[4,8],[14,8]] },
914: { width: 21, points: [[4,22],[4,1],[-1,-1],[4,22],[13,22],[16,21],[17,20],[18,18],[18,16],[17,14],[16,13],[13,12],[-1,-1],[4,12],[13,12],[16,11],[17,10],[18,8],[18,5],[17,3],[16,2],[13,1],[4,1]] },
915: { width: 17, points: [[4,22],[4,1],[-1,-1],[4,22],[16,22]] },
916: { width: 18, points: [[9,22],[1,1],[-1,-1],[9,22],[17,1],[-1,-1],[1,1],[17,1]] },
917: { width: 19, points: [[4,22],[4,1],[-1,-1],[4,22],[17,22],[-1,-1],[4,12],[12,12],[-1,-1],[4,1],[17,1]] },
918: { width: 20, points: [[17,22],[3,1],[-1,-1],[3,22],[17,22],[-1,-1],[3,1],[17,1]] },
919: { width: 22, points: [[4,22],[4,1],[-1,-1],[18,22],[18,1],[-1,-1],[4,12],[18,12]] },
920: { width: 22, points: [[9,22],[7,21],[5,19],[4,17],[3,14],[3,9],[4,6],[5,4],[7,2],[9,1],[13,1],[15,2],[17,4],[18,6],[19,9],[19,14],[18,17],[17,19],[15,21],[13,22],[9,22],[-1,-1],[8,12],[14,12]] },
921: { width: 8, points: [[4,22],[4,1]] },
922: { width: 21, points: [[4,22],[4,1],[-1,-1],[18,22],[4,8],[-1,-1],[9,13],[18,1]] },
923: { width: 18, points: [[9,22],[1,1],[-1,-1],[9,22],[17,1]] },
924: { width: 24, points: [[4,22],[4,1],[-1,-1],[4,22],[12,1],[-1,-1],[20,22],[12,1],[-1,-1],[20,22],[20,1]] },
925: { width: 22, points: [[4,22],[4,1],[-1,-1],[4,22],[18,1],[-1,-1],[18,22],[18,1]] },
926: { width: 18, points: [[2,22],[16,22],[-1,-1],[6,12],[12,12],[-1,-1],[2,1],[16,1]] },
927: { width: 22, points: [[9,22],[7,21],[5,19],[4,17],[3,14],[3,9],[4,6],[5,4],[7,2],[9,1],[13,1],[15,2],[17,4],[18,6],[19,9],[19,14],[18,17],[17,19],[15,21],[13,22],[9,22]] },
928: { width: 22, points: [[4,22],[4,1],[-1,-1],[18,22],[18,1],[-1,-1],[4,22],[18,22]] },
929: { width: 21, points: [[4,22],[4,1],[-1,-1],[4,22],[13,22],[16,21],[17,20],[18,18],[18,15],[17,13],[16,12],[13,11],[4,11]] },
931: { width: 18, points: [[2,22],[9,12],[2,1],[-1,-1],[2,22],[16,22],[-1,-1],[2,1],[16,1]] },
932: { width: 16, points: [[8,22],[8,1],[-1,-1],[1,22],[15,22]] },
933: { width: 18, points: [[2,17],[2,19],[3,21],[4,22],[6,22],[7,21],[8,19],[9,15],[9,1],[-1,-1],[16,17],[16,19],[15,21],[14,22],[12,22],[11,21],[10,19],[9,15]] },
934: { width: 20, points: [[10,22],[10,1],[-1,-1],[8,17],[5,16],[4,15],[3,13],[3,10],[4,8],[5,7],[8,6],[12,6],[15,7],[16,8],[17,10],[17,13],[16,15],[15,16],[12,17],[8,17]] },
935: { width: 20, points: [[3,22],[17,1],[-1,-1],[3,1],[17,22]] },
936: { width: 22, points: [[11,22],[11,1],[-1,-1],[2,16],[3,16],[4,15],[5,11],[6,9],[7,8],[10,7],[12,7],[15,8],[16,9],[17,11],[18,15],[19,16],[20,16]] },
937: { width: 20, points: [[3,1],[7,1],[4,8],[3,12],[3,16],[4,19],[6,21],[9,22],[11,22],[14,21],[16,19],[17,16],[17,12],[16,8],[13,1],[17,1]] },
945: { width: 21, points: [[9,15],[7,14],[5,12],[4,10],[3,7],[3,4],[4,2],[6,1],[8,1],[10,2],[13,5],[15,8],[17,12],[18,15],[-1,-1],[9,15],[11,15],[12,14],[13,12],[15,4],[16,2],[17,1],[18,1]] },
946: { width: 19, points: [[12,22],[10,21],[8,19],[6,15],[5,12],[4,8],[3,2],[2,-6],[-1,-1],[12,22],[14,22],[16,20],[16,17],[15,15],[14,14],[12,13],[9,13],[-1,-1],[9,13],[11,12],[13,10],[14,8],[14,5],[13,3],[12,2],[10,1],[8,1],[6,2],[5,3],[4,6]] },
947: { width: 19, points: [[1,12],[3,14],[5,15],[6,15],[8,14],[9,13],[10,10],[10,6],[9,1],[-1,-1],[17,15],[16,12],[15,10],[9,1],[7,-3],[6,-6]] },
948: { width: 18, points: [[11,15],[8,15],[6,14],[4,12],[3,9],[3,6],[4,3],[5,2],[7,1],[9,1],[11,2],[13,4],[14,7],[14,10],[13,13],[11,15],[9,17],[8,19],[8,21],[9,22],[11,22],[13,21],[15,19]] },
949: { width: 16, points: [[13,13],[12,14],[10,15],[7,15],[5,14],[5,12],[6,10],[9,9],[-1,-1],[9,9],[5,8],[3,6],[3,4],[4,2],[6,1],[9,1],[11,2],[13,4]] },
950: { width: 15, points: [[10,22],[8,21],[7,20],[7,19],[8,18],[11,17],[14,17],[-1,-1],[14,17],[10,15],[7,13],[4,10],[3,7],[3,5],[4,3],[6,1],[9,-1],[10,-3],[10,-5],[9,-6],[7,-6],[6,-4]] },
951: { width: 20, points: [[1,11],[2,13],[4,15],[6,15],[7,14],[7,12],[6,8],[4,1],[-1,-1],[6,8],[8,12],[10,14],[12,15],[14,15],[16,13],[16,10],[15,5],[12,-6]] },
952: { width: 17, points: [[10,22],[8,21],[6,18],[5,16],[4,13],[3,8],[3,4],[4,2],[5,1],[7,1],[9,2],[11,5],[12,7],[13,10],[14,15],[14,19],[13,21],[12,22],[10,22],[-1,-1],[4,12],[13,12]] },
953: { width: 11, points: [[6,15],[4,8],[3,4],[3,2],[4,1],[6,1],[8,3],[9,5]] },
954: { width: 18, points: [[6,15],[2,1],[-1,-1],[16,14],[15,15],[14,15],[12,14],[8,10],[6,9],[5,9],[-1,-1],[5,9],[7,8],[8,7],[10,2],[11,1],[12,1],[13,2]] },
955: { width: 16, points: [[1,22],[3,22],[5,21],[6,20],[14,1],[-1,-1],[8,15],[2,1]] },
956: { width: 21, points: [[7,15],[1,-6],[-1,-1],[6,11],[5,6],[5,3],[7,1],[9,1],[11,2],[13,4],[15,8],[-1,-1],[17,15],[15,8],[14,4],[14,2],[15,1],[17,1],[19,3],[20,5]] },
957: { width: 18, points: [[3,15],[6,15],[5,9],[4,4],[3,1],[-1,-1],[16,15],[15,12],[14,10],[12,7],[9,4],[6,2],[3,1]] },
958: { width: 16, points: [[10,22],[8,21],[7,20],[7,19],[8,18],[11,17],[14,17],[-1,-1],[11,17],[8,16],[6,15],[5,13],[5,11],[7,9],[10,8],[12,8],[-1,-1],[10,8],[6,7],[4,6],[3,4],[3,2],[5,0],[9,-2],[10,-3],[10,-5],[8,-6],[6,-6]] },
959: { width: 17, points: [[8,15],[6,14],[4,12],[3,9],[3,6],[4,3],[5,2],[7,1],[9,1],[11,2],[13,4],[14,7],[14,10],[13,13],[12,14],[10,15],[8,15]] },
960: { width: 18, points: [[6,15],[2,1],[-1,-1],[16,14],[15,15],[14,15],[12,14],[8,10],[6,9],[5,9],[-1,-1],[5,9],[7,8],[8,7],[10,2],[11,1],[12,1],[13,2]] },
960: { width: 22, points: [[9,15],[5,1],[-1,-1],[14,15],[15,9],[16,4],[17,1],[-1,-1],[2,12],[4,14],[7,15],[20,15]] },
961: { width: 18, points: [[4,9],[4,6],[5,3],[6,2],[8,1],[10,1],[12,2],[14,4],[15,7],[15,10],[14,13],[13,14],[11,15],[9,15],[7,14],[5,12],[4,9],[0,-6]] },
962: { width: 18, points: [[15,13],[14,14],[11,15],[8,15],[5,14],[4,13],[3,11],[3,9],[4,7],[6,5],[10,2],[11,0],[11,-2],[10,-3],[8,-3]] },
963: { width: 18, points: [[3,15],[6,15],[5,9],[4,4],[3,1],[-1,-1],[16,15],[15,12],[14,10],[12,7],[9,4],[6,2],[3,1]] },
963: { width: 20, points: [[18,15],[8,15],[6,14],[4,12],[3,9],[3,6],[4,3],[5,2],[7,1],[9,1],[11,2],[13,4],[14,7],[14,10],[13,13],[12,14],[10,15]] },
964: { width: 20, points: [[11,15],[8,1],[-1,-1],[2,12],[4,14],[7,15],[18,15]] },
965: { width: 20, points: [[1,11],[2,13],[4,15],[6,15],[7,14],[7,12],[5,6],[5,3],[7,1],[9,1],[12,2],[14,4],[16,8],[17,12],[17,15]] },
966: { width: 22, points: [[8,14],[6,13],[4,11],[3,8],[3,5],[4,3],[5,2],[7,1],[10,1],[13,2],[16,4],[18,7],[19,10],[19,13],[17,15],[15,15],[13,13],[11,9],[9,4],[6,-6]] },
967: { width: 18, points: [[2,15],[4,15],[6,13],[12,-4],[14,-6],[16,-6],[-1,-1],[17,15],[16,13],[14,10],[4,-1],[2,-4],[1,-6]] },
968: { width: 23, points: [[16,22],[8,-6],[-1,-1],[1,11],[2,13],[4,15],[6,15],[7,14],[7,12],[6,7],[6,4],[7,2],[9,1],[11,1],[14,2],[16,4],[18,7],[20,12],[21,15]] },
969: { width: 23, points: [[8,15],[6,14],[4,11],[3,8],[3,5],[4,2],[5,1],[7,1],[9,2],[11,5],[-1,-1],[12,9],[11,5],[12,2],[13,1],[15,1],[17,2],[19,5],[20,8],[20,11],[19,14],[18,15]] },
977: { width: 21, points: [[1,11],[2,13],[4,15],[6,15],[7,14],[7,12],[6,7],[6,4],[7,2],[8,1],[10,1],[12,2],[14,5],[15,7],[16,10],[17,15],[17,18],[16,21],[14,22],[12,22],[11,20],[11,18],[12,15],[14,12],[16,10],[19,8]] },
981: { width: 20, points: [[14,22],[6,-6],[-1,-1],[9,15],[6,14],[4,12],[3,9],[3,6],[4,4],[6,2],[9,1],[11,1],[14,2],[16,4],[17,7],[17,10],[16,12],[14,14],[11,15],[9,15]] },
982: { width: 23, points: [[9,14],[6,13],[4,11],[3,8],[3,5],[4,2],[5,1],[7,1],[9,2],[11,5],[-1,-1],[12,9],[11,5],[12,2],[13,1],[15,1],[17,2],[19,5],[20,8],[20,11],[19,14],[18,15],[-1,-1],[2,14],[4,15],[20,15]] },
986: { width: 18, points: [[15,13],[14,14],[11,15],[8,15],[5,14],[4,13],[3,11],[3,9],[4,7],[6,5],[10,2],[11,0],[11,-2],[10,-3],[8,-3]] },
// Hershey math symbols
8501: { width: 20, points: [[3,19],[4,17],[16,5],[17,3],[17,1],[-1,-1],[4,16],[16,4],[-1,-1],[3,19],[3,17],[4,15],[16,3],[17,1],[-1,-1],[8,12],[4,8],[3,6],[3,4],[4,2],[3,1],[-1,-1],[3,6],[5,2],[-1,-1],[4,8],[4,6],[5,4],[5,2],[3,1],[-1,-1],[11,9],[16,14],[-1,-1],[14,19],[14,16],[15,14],[17,14],[17,16],[15,17],[14,19],[-1,-1],[14,19],[15,16],[17,14]] },
8592: { width: 26, points: [[7,12],[4,10],[7,8],[-1,-1],[10,15],[5,10],[10,5],[-1,-1],[5,10],[22,10]] },
8593: { width: 16, points: [[6,16],[8,19],[10,16],[-1,-1],[3,13],[8,18],[13,13],[-1,-1],[8,18],[8,1]] },
8594: { width: 26, points: [[19,12],[22,10],[19,8],[-1,-1],[16,15],[21,10],[16,5],[-1,-1],[4,10],[21,10]] },
8595: { width: 16, points: [[6,4],[8,1],[10,4],[-1,-1],[3,7],[8,2],[13,7],[-1,-1],[8,19],[8,2]] },
8706: { width: 19, points: [[15,10],[14,13],[13,14],[11,15],[9,15],[6,14],[4,11],[3,8],[3,5],[4,3],[5,2],[7,1],[9,1],[12,2],[14,4],[15,7],[16,12],[16,17],[15,20],[14,21],[12,22],[9,22],[7,21],[6,20],[6,19],[7,19],[7,20],[-1,-1],[9,15],[7,14],[5,11],[4,8],[4,4],[5,2],[-1,-1],[9,1],[11,2],[13,4],[14,7],[15,12],[15,17],[14,20],[12,22]] },
8710: { width: 20, points: [[2,1],[10,22],[-1,-1],[17,1],[10,20],[-1,-1],[18,1],[10,22],[-1,-1],[2,1],[18,1],[-1,-1],[3,2],[17,2]] },
8711: { width: 20, points: [[2,22],[10,1],[-1,-1],[3,22],[10,3],[-1,-1],[18,22],[10,1],[-1,-1],[2,22],[18,22],[-1,-1],[3,21],[17,21]] },
8712: { width: 24, points: [[20,18],[13,18],[9,17],[7,16],[5,14],[4,11],[4,9],[5,6],[7,4],[9,3],[13,2],[20,2],[-1,-1],[4,10],[16,10]] },
8713: { width: 24, points: [[20,18],[13,18],[9,17],[7,16],[5,14],[4,11],[4,9],[5,6],[7,4],[9,3],[13,2],[20,2],[-1,-1],[4,10],[16,10],[-1,-1],[18,19],[6,1]] },
8719: { width: 34, points: [[7,26],[7,-6],[-1,-1],[8,26],[8,-6],[-1,-1],[26,26],[26,-6],[-1,-1],[27,26],[27,-6],[-1,-1],[3,26],[31,26],[-1,-1],[3,-6],[12,-6],[-1,-1],[22,-6],[31,-6]] },
8721: { width: 31, points: [[5,26],[15,12],[4,-6],[-1,-1],[4,26],[14,12],[-1,-1],[3,26],[14,11],[-1,-1],[3,26],[26,26],[28,19],[25,26],[-1,-1],[5,-5],[26,-5],[-1,-1],[4,-6],[26,-6],[28,1],[25,-6]] },
8728: { width: 16, points: [[7,13],[5,11],[5,9],[7,7],[9,7],[11,9],[11,11],[9,13],[7,13]] },
8729: { width: 16, points: [[7,13],[5,11],[5,9],[7,7],[9,7],[11,9],[11,11],[9,13],[7,13],[-1,-1],[7,12],[6,11],[6,9],[7,8],[9,8],[10,9],[10,11],[9,12],[7,12],[-1,-1],[8,11],[7,10],[8,9],[9,10],[8,11]] },
8730: { width: 22, points: [[3,15],[7,15],[13,3],[-1,-1],[6,15],[13,1],[-1,-1],[22,26],[13,1]] },
8733: { width: 25, points: [[21,5],[19,5],[17,6],[15,8],[12,12],[11,13],[9,14],[7,14],[5,13],[4,11],[4,9],[5,7],[7,6],[9,6],[11,7],[12,8],[15,12],[17,14],[19,15],[21,15]] },
8734: { width: 25, points: [[22,9],[21,7],[19,6],[17,6],[15,7],[14,8],[11,12],[10,13],[8,14],[6,14],[4,13],[3,11],[3,9],[4,7],[6,6],[8,6],[10,7],[11,8],[14,12],[15,13],[17,14],[19,14],[21,13],[22,11],[22,9]] },
8745: { width: 24, points: [[4,2],[4,9],[5,13],[6,15],[8,17],[11,18],[13,18],[16,17],[18,15],[19,13],[20,9],[20,2]] },
8746: { width: 24, points: [[4,18],[4,11],[5,7],[6,5],[8,3],[11,2],[13,2],[16,3],[18,5],[19,7],[20,11],[20,18]] },
8747: { width: 24, points: [[21,25],[20,24],[21,23],[22,24],[22,25],[21,26],[19,26],[17,25],[15,23],[14,21],[13,18],[12,14],[10,2],[9,-2],[8,-4],[-1,-1],[16,24],[15,22],[14,18],[12,6],[11,2],[10,-1],[9,-3],[7,-5],[5,-6],[3,-6],[2,-5],[2,-4],[3,-3],[4,-4],[3,-5]] },
8771: { width: 24, points: [[3,10],[3,11],[4,13],[6,14],[8,14],[10,13],[14,11],[16,10],[18,10],[20,11],[22,13],[22,14],[-1,-1],[3,6],[22,6]] },
8776: { width: 24, points: [[3,10],[3,11],[4,13],[6,14],[8,14],[10,13],[14,11],[16,10],[18,10],[20,11],[22,13],[22,14],[-1,-1],[3,4],[3,5],[4,7],[6,8],[8,8],[10,7],[14,5],[16,4],[18,4],[20,5],[22,7],[22,8]] },
8800: { width: 24, points: [[18,19],[6,1],[-1,-1],[3,12],[20,12],[-1,-1],[3,6],[20,6]] },
8804: { width: 24, points: [[20,4],[4,11],[20,18],[-1,-1],[4,0],[20,0]] },
8805: { width: 24, points: [[4,4],[20,11],[4,18],[-1,-1],[4,0],[20,0]] },
8834: { width: 24, points: [[20,18],[13,18],[9,17],[7,16],[5,14],[4,11],[4,9],[5,6],[7,4],[9,3],[13,2],[20,2]] },
8835: { width: 24, points: [[4,18],[11,18],[15,17],[17,16],[19,14],[20,11],[20,9],[19,6],[17,4],[15,3],[11,2],[4,2]] },
8838: { width: 24, points: [[20,20],[13,20],[9,19],[7,18],[5,16],[4,13],[4,11],[5,8],[7,6],[9,5],[13,4],[20,4],[-1,-1],[4,0],[20,0]] },
8839: { width: 24, points: [[4,20],[11,20],[15,19],[17,18],[19,16],[20,13],[20,11],[19,8],[17,6],[15,5],[11,4],[4,4],[-1,-1],[4,0],[20,0]] },
8853: { width: 20, points: [[9,17],[6,16],[4,14],[3,11],[3,9],[4,6],[6,4],[9,3],[11,3],[14,4],[16,6],[17,9],[17,11],[16,14],[14,16],[11,17],[9,17],[-1,-1],[5,10],[15,10],[-1,-1],[10,5],[10,15]] },
8855: { width: 20, points: [[9,17],[6,16],[4,14],[3,11],[3,9],[4,6],[6,4],[9,3],[11,3],[14,4],[16,6],[17,9],[17,11],[16,14],[14,16],[11,17],[9,17],[-1,-1],[6,6],[14,14],[-1,-1],[14,6],[6,14]] },
8857: { width: 20, points: [[9,17],[6,16],[4,14],[3,11],[3,9],[4,6],[6,4],[9,3],[11,3],[14,4],[16,6],[17,9],[17,11],[16,14],[14,16],[11,17],[9,17],[-1,-1],[10,11],[9,10],[10,9],[11,10],[10,11]] },
9737: { width: 27, points: [[12,22],[9,21],[6,19],[4,16],[3,13],[3,10],[4,7],[6,4],[9,2],[12,1],[15,1],[18,2],[21,4],[23,7],[24,10],[24,13],[23,16],[21,19],[18,21],[15,22],[12,22],[-1,-1],[13,13],[12,12],[12,11],[13,10],[14,10],[15,11],[15,12],[14,13],[13,13],[-1,-1],[13,12],[13,11],[14,11],[14,12],[13,12]] },
// Miscellaneous glyphs (EAM)
176: { width: 14, points: [[6,22],[4,21],[3,19],[3,17],[4,15],[6,14],[8,14],[10,15],[11,17],[11,19],[10,21],[8,22],[6,22]] },
177: { width: 20, points: [[10,15],[10,5],[-1,-1],[4,10],[16,10],[-1,-1],[4,2],[16,2]] },
181: { width: 0, points: [[-99,956]] },
183: { width: 10, points: [[5,11],[4,10],[5,9],[6,10],[5,11]] },
188: { width: 23, points: [[4,0],[16,20],[-1,-1],[2,18],[4,20],[4,10],[-1,-1],[21,3],[12,3],[17,12],[18,12],[18,-1]] },
189: { width: 23, points: [[4,0],[16,20],[-1,-1],[2,18],[4,20],[4,10],[-1,-1],[14,8],[16,10],[18,10],[20,8],[20,6],[14,1],[14,0],[21,0]] },
215: { width: 20, points: [[4,16],[16,4],[-1,-1],[4,4],[16,16]] },
402: { width: 15, points: [[15,21],[14,20],[15,19],[16,20],[16,21],[15,22],[13,22],[11,21],[10,20],[9,18],[8,15],[5,1],[4,-3],[3,-5],[-1,-1],[13,22],[11,20],[10,18],[9,14],[7,5],[6,1],[5,-2],[4,-4],[3,-5],[1,-6],[-1,-6],[-2,-5],[-2,-4],[-1,-3],[0,-4],[-1,-5],[-1,-1],[4,15],[14,15]] },
8242: { width: 8, points: [[5,24],[3,16],[-1,-1],[3,16],[6,24]] },
8243: { width: 11, points: [[4,24],[2,16],[-1,-1],[2,16],[5,24],[-1,-1],[9,24],[7,16],[-1,-1],[7,16],[10,24]] },
8463: { width: 21, points: [[9,22],[3,1],[-1,-1],[5,7],[7,11],[9,13],[11,14],[13,14],[15,14],[16,12],[16,10],[14,5],[14,2],[15,1],[19,3],[-1,-1],[7,21],[9,22],[-1,-1],[4,15],[14,20]] },
8491: { width: 0, points: [[-99,197]] },
8495: { width: 18, points: [[4,6],[8,7],[11,8],[14,10],[15,12],[14,14],[12,15],[9,15],[6,14],[4,11],[3,8],[3,5],[4,3],[5,2],[7,1],[9,1],[12,2],[14,4],[-1,-1],[9,15],[7,14],[5,11],[4,8],[4,4],[5,2]] },
8722: { width: 20, points: [[2,9],[18,9]] },
// Used to indicate missing glyph
129: { width: 18, points: [[2,-2],[2,21],[16,21],[16,-2],[2,-2]] }
};
CanvasTextFunctions.letter = function (ch)
{
glyph = CanvasTextFunctions.letters[ch];
// EAM - Draw an empty box for undefined glyphs
if (glyph == undefined)
glyph = CanvasTextFunctions.letters[129];
return glyph;
}
CanvasTextFunctions.ascent = function( font, size)
{
return size;
}
CanvasTextFunctions.descent = function( font, size)
{
return 7.0*size/25.0;
}
CanvasTextFunctions.measure = function( font, size, str)
{
var total = 0;
var len = str.length;
for ( i = 0; i < len; i++) {
var index = str.charAt(i);
// EAM deal with non-ascii characaters
if (index > '~')
index = str.charCodeAt(i);
var c = CanvasTextFunctions.letter(index);
if (!c) break;
total += c.width * size / 25.0;
if (c.width == 0) {
for ( j = 0; j < c.points.length; j++) {
var a = c.points[j];
if (a[0] == -99)
total += CanvasTextFunctions.letter(a[1]).width * size / 25.0;
}
}
}
return total;
}
CanvasTextFunctions.draw = function(ctx,font,size,x,y,str)
{
var total = 0;
var len = str.length;
var mag = size / 25.0;
var composite = 0;
var xorig = x;
ctx.save();
ctx.lineCap = "round";
ctx.lineWidth = 2.0 * mag;
for (var i = 0; i < len; i++) {
var index = str.charAt(i);
// EAM tab and newline
if (index == ' ') {
var tabstop = 48 * mag;
x = tabstop * Math.ceil(x/tabstop);
continue;
} else if (index < ' ') {
x = xorig; y += size;
continue;
}
// EAM deal with non-ascii characaters
if (index > '~')
index = str.charCodeAt(i);
// EAM allow composing characters
if (composite != 0) {
index = composite;
composite = 0;
}
var c = CanvasTextFunctions.letter(index);
if ( !c) continue;
ctx.beginPath();
var penUp = 1;
var needStroke = 0;
for (var j = 0; j < c.points.length; j++) {
var a = c.points[j];
if ( a[0] == -1 && a[1] == -1) {
penUp = 1;
continue;
}
if ( a[0] == -99 ) {
composite = a[1];
i--;
break;
}
if ( penUp) {
ctx.moveTo( x + a[0]*mag, y - a[1]*mag);
penUp = false;
} else {
ctx.lineTo( x + a[0]*mag, y - a[1]*mag);
}
}
ctx.stroke();
x += c.width*mag;
}
ctx.restore();
return total;
}
CanvasTextFunctions.enable = function( ctx)
{
ctx.drawText = function(font,size,x,y,text) { return CanvasTextFunctions.draw( ctx, font,size,x,y,text); };
ctx.measureText = function(font,size,text) { return CanvasTextFunctions.measure( font,size,text); };
ctx.fontAscent = function(font,size) { return CanvasTextFunctions.ascent(font,size); }
ctx.fontDescent = function(font,size) { return CanvasTextFunctions.descent(font,size); }
ctx.drawTextRight = function(font,size,x,y,text) {
var w = CanvasTextFunctions.measure(font,size,text);
return CanvasTextFunctions.draw( ctx, font,size,x-w,y,text);
};
ctx.drawTextCenter = function(font,size,x,y,text) {
var w = CanvasTextFunctions.measure(font,size,text);
return CanvasTextFunctions.draw( ctx, font,size,x-w/2,y,text);
};
}

View File

@ -0,0 +1,211 @@
/*
* $Id: canvastext.js,v 1.3 2009/02/22 19:14:05 sfeam Exp $
*/
// The canvastext.js code was released to the public domain by Jim Studt, 2007.
// He may keep some sort of up to date copy at http://www.federated.com/~jim/canvastext/
// Feb 2009 Ethan A Merritt (EAM) Modify code to work in non-ascii environments.
//
var CanvasTextFunctions = { };
CanvasTextFunctions.letters = {
' ': { width: 16, points: [] },
'!': { width: 10, points: [[5,21],[5,7],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] },
'"': { width: 16, points: [[4,21],[4,14],[-1,-1],[12,21],[12,14]] },
'#': { width: 21, points: [[11,25],[4,-7],[-1,-1],[17,25],[10,-7],[-1,-1],[4,12],[18,12],[-1,-1],[3,6],[17,6]] },
'$': { width: 20, points: [[8,25],[8,-4],[-1,-1],[12,25],[12,-4],[-1,-1],[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] },
'%': { width: 24, points: [[21,21],[3,0],[-1,-1],[8,21],[10,19],[10,17],[9,15],[7,14],[5,14],[3,16],[3,18],[4,20],[6,21],[8,21],[10,20],[13,19],[16,19],[19,20],[21,21],[-1,-1],[17,7],[15,6],[14,4],[14,2],[16,0],[18,0],[20,1],[21,3],[21,5],[19,7],[17,7]] },
'&': { width: 26, points: [[23,12],[23,13],[22,14],[21,14],[20,13],[19,11],[17,6],[15,3],[13,1],[11,0],[7,0],[5,1],[4,2],[3,4],[3,6],[4,8],[5,9],[12,13],[13,14],[14,16],[14,18],[13,20],[11,21],[9,20],[8,18],[8,16],[9,13],[11,10],[16,3],[18,1],[20,0],[22,0],[23,1],[23,2]] },
'\'': { width: 10, points: [[5,19],[4,20],[5,21],[6,20],[6,18],[5,16],[4,15]] },
'(': { width: 14, points: [[11,25],[9,23],[7,20],[5,16],[4,11],[4,7],[5,2],[7,-2],[9,-5],[11,-7]] },
')': { width: 14, points: [[3,25],[5,23],[7,20],[9,16],[10,11],[10,7],[9,2],[7,-2],[5,-5],[3,-7]] },
'*': { width: 16, points: [[8,21],[8,9],[-1,-1],[3,18],[13,12],[-1,-1],[13,18],[3,12]] },
'+': { width: 26, points: [[13,18],[13,0],[-1,-1],[4,9],[22,9]] },
',': { width: 10, points: [[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] },
'-': { width: 26, points: [[4,9],[22,9]] },
'.': { width: 10, points: [[5,2],[4,1],[5,0],[6,1],[5,2]] },
'/': { width: 22, points: [[20,25],[2,-7]] },
'0': { width: 20, points: [[9,21],[6,20],[4,17],[3,12],[3,9],[4,4],[6,1],[9,0],[11,0],[14,1],[16,4],[17,9],[17,12],[16,17],[14,20],[11,21],[9,21]] },
'1': { width: 20, points: [[6,17],[8,18],[11,21],[11,0]] },
'2': { width: 20, points: [[4,16],[4,17],[5,19],[6,20],[8,21],[12,21],[14,20],[15,19],[16,17],[16,15],[15,13],[13,10],[3,0],[17,0]] },
'3': { width: 20, points: [[5,21],[16,21],[10,13],[13,13],[15,12],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] },
'4': { width: 20, points: [[13,21],[3,7],[18,7],[-1,-1],[13,21],[13,0]] },
'5': { width: 20, points: [[15,21],[5,21],[4,12],[5,13],[8,14],[11,14],[14,13],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] },
'6': { width: 20, points: [[16,18],[15,20],[12,21],[10,21],[7,20],[5,17],[4,12],[4,7],[5,3],[7,1],[10,0],[11,0],[14,1],[16,3],[17,6],[17,7],[16,10],[14,12],[11,13],[10,13],[7,12],[5,10],[4,7]] },
'7': { width: 20, points: [[17,21],[7,0],[-1,-1],[3,21],[17,21]] },
'8': { width: 20, points: [[8,21],[5,20],[4,18],[4,16],[5,14],[7,13],[11,12],[14,11],[16,9],[17,7],[17,4],[16,2],[15,1],[12,0],[8,0],[5,1],[4,2],[3,4],[3,7],[4,9],[6,11],[9,12],[13,13],[15,14],[16,16],[16,18],[15,20],[12,21],[8,21]] },
'9': { width: 20, points: [[16,14],[15,11],[13,9],[10,8],[9,8],[6,9],[4,11],[3,14],[3,15],[4,18],[6,20],[9,21],[10,21],[13,20],[15,18],[16,14],[16,9],[15,4],[13,1],[10,0],[8,0],[5,1],[4,3]] },
':': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] },
';': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] },
'<': { width: 24, points: [[20,18],[4,9],[20,0]] },
'=': { width: 26, points: [[4,12],[22,12],[-1,-1],[4,6],[22,6]] },
'>': { width: 24, points: [[4,18],[20,9],[4,0]] },
'?': { width: 18, points: [[3,16],[3,17],[4,19],[5,20],[7,21],[11,21],[13,20],[14,19],[15,17],[15,15],[14,13],[13,12],[9,10],[9,7],[-1,-1],[9,2],[8,1],[9,0],[10,1],[9,2]] },
'@': { width: 27, points: [[18,13],[17,15],[15,16],[12,16],[10,15],[9,14],[8,11],[8,8],[9,6],[11,5],[14,5],[16,6],[17,8],[-1,-1],[12,16],[10,14],[9,11],[9,8],[10,6],[11,5],[-1,-1],[18,16],[17,8],[17,6],[19,5],[21,5],[23,7],[24,10],[24,12],[23,15],[22,17],[20,19],[18,20],[15,21],[12,21],[9,20],[7,19],[5,17],[4,15],[3,12],[3,9],[4,6],[5,4],[7,2],[9,1],[12,0],[15,0],[18,1],[20,2],[21,3],[-1,-1],[19,16],[18,8],[18,6],[19,5]] },
'A': { width: 18, points: [[9,21],[1,0],[-1,-1],[9,21],[17,0],[-1,-1],[4,7],[14,7]] },
'B': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[-1,-1],[4,11],[13,11],[16,10],[17,9],[18,7],[18,4],[17,2],[16,1],[13,0],[4,0]] },
'C': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5]] },
'D': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[11,21],[14,20],[16,18],[17,16],[18,13],[18,8],[17,5],[16,3],[14,1],[11,0],[4,0]] },
'E': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11],[-1,-1],[4,0],[17,0]] },
'F': { width: 18, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11]] },
'G': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[18,8],[-1,-1],[13,8],[18,8]] },
'H': { width: 22, points: [[4,21],[4,0],[-1,-1],[18,21],[18,0],[-1,-1],[4,11],[18,11]] },
'I': { width: 8, points: [[4,21],[4,0]] },
'J': { width: 16, points: [[12,21],[12,5],[11,2],[10,1],[8,0],[6,0],[4,1],[3,2],[2,5],[2,7]] },
'K': { width: 21, points: [[4,21],[4,0],[-1,-1],[18,21],[4,7],[-1,-1],[9,12],[18,0]] },
'L': { width: 17, points: [[4,21],[4,0],[-1,-1],[4,0],[16,0]] },
'M': { width: 24, points: [[4,21],[4,0],[-1,-1],[4,21],[12,0],[-1,-1],[20,21],[12,0],[-1,-1],[20,21],[20,0]] },
'N': { width: 22, points: [[4,21],[4,0],[-1,-1],[4,21],[18,0],[-1,-1],[18,21],[18,0]] },
'O': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21]] },
'P': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,14],[17,12],[16,11],[13,10],[4,10]] },
'Q': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21],[-1,-1],[12,4],[18,-2]] },
'R': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[4,11],[-1,-1],[11,11],[18,0]] },
'S': { width: 20, points: [[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] },
'T': { width: 16, points: [[8,21],[8,0],[-1,-1],[1,21],[15,21]] },
'U': { width: 22, points: [[4,21],[4,6],[5,3],[7,1],[10,0],[12,0],[15,1],[17,3],[18,6],[18,21]] },
'V': { width: 18, points: [[1,21],[9,0],[-1,-1],[17,21],[9,0]] },
'W': { width: 24, points: [[2,21],[7,0],[-1,-1],[12,21],[7,0],[-1,-1],[12,21],[17,0],[-1,-1],[22,21],[17,0]] },
'X': { width: 20, points: [[3,21],[17,0],[-1,-1],[17,21],[3,0]] },
'Y': { width: 18, points: [[1,21],[9,11],[9,0],[-1,-1],[17,21],[9,11]] },
'Z': { width: 20, points: [[17,21],[3,0],[-1,-1],[3,21],[17,21],[-1,-1],[3,0],[17,0]] },
'[': { width: 14, points: [[4,25],[4,-7],[-1,-1],[5,25],[5,-7],[-1,-1],[4,25],[11,25],[-1,-1],[4,-7],[11,-7]] },
'\\': { width: 14, points: [[0,21],[14,-3]] },
']': { width: 14, points: [[9,25],[9,-7],[-1,-1],[10,25],[10,-7],[-1,-1],[3,25],[10,25],[-1,-1],[3,-7],[10,-7]] },
'^': { width: 16, points: [[5,15],[8,19],[11,15],[-1,-1],[2,12],[8,18],[14,12]] },
'_': { width: 16, points: [[0,-2],[16,-2]] },
'`': { width: 10, points: [[6,21],[5,20],[4,18],[4,16],[5,15],[6,16],[5,17]] },
'a': { width: 19, points: [[15,14],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'b': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] },
'c': { width: 18, points: [[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'd': { width: 19, points: [[15,21],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'e': { width: 18, points: [[3,8],[15,8],[15,10],[14,12],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'f': { width: 12, points: [[10,21],[8,21],[6,20],[5,17],[5,0],[-1,-1],[2,14],[9,14]] },
'g': { width: 19, points: [[15,14],[15,-2],[14,-5],[13,-6],[11,-7],[8,-7],[6,-6],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'h': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] },
'i': { width: 8, points: [[3,21],[4,20],[5,21],[4,22],[3,21],[-1,-1],[4,14],[4,0]] },
'j': { width: 10, points: [[5,21],[6,20],[7,21],[6,22],[5,21],[-1,-1],[6,14],[6,-3],[5,-6],[3,-7],[1,-7]] },
'k': { width: 17, points: [[4,21],[4,0],[-1,-1],[14,14],[4,4],[-1,-1],[8,8],[15,0]] },
'l': { width: 8, points: [[4,21],[4,0]] },
'm': { width: 30, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0],[-1,-1],[15,10],[18,13],[20,14],[23,14],[25,13],[26,10],[26,0]] },
'n': { width: 19, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] },
'o': { width: 19, points: [[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3],[16,6],[16,8],[15,11],[13,13],[11,14],[8,14]] },
'p': { width: 19, points: [[4,14],[4,-7],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] },
'q': { width: 19, points: [[15,14],[15,-7],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
'r': { width: 13, points: [[4,14],[4,0],[-1,-1],[4,8],[5,11],[7,13],[9,14],[12,14]] },
's': { width: 17, points: [[14,11],[13,13],[10,14],[7,14],[4,13],[3,11],[4,9],[6,8],[11,7],[13,6],[14,4],[14,3],[13,1],[10,0],[7,0],[4,1],[3,3]] },
't': { width: 12, points: [[5,21],[5,4],[6,1],[8,0],[10,0],[-1,-1],[2,14],[9,14]] },
'u': { width: 19, points: [[4,14],[4,4],[5,1],[7,0],[10,0],[12,1],[15,4],[-1,-1],[15,14],[15,0]] },
'v': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0]] },
'w': { width: 22, points: [[3,14],[7,0],[-1,-1],[11,14],[7,0],[-1,-1],[11,14],[15,0],[-1,-1],[19,14],[15,0]] },
'x': { width: 17, points: [[3,14],[14,0],[-1,-1],[14,14],[3,0]] },
'y': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0],[6,-4],[4,-6],[2,-7],[1,-7]] },
'z': { width: 17, points: [[14,14],[3,0],[-1,-1],[3,14],[14,14],[-1,-1],[3,0],[14,0]] },
'{': { width: 14, points: [[9,25],[7,24],[6,23],[5,21],[5,19],[6,17],[7,16],[8,14],[8,12],[6,10],[-1,-1],[7,24],[6,22],[6,20],[7,18],[8,17],[9,15],[9,13],[8,11],[4,9],[8,7],[9,5],[9,3],[8,1],[7,0],[6,-2],[6,-4],[7,-6],[-1,-1],[6,8],[8,6],[8,4],[7,2],[6,1],[5,-1],[5,-3],[6,-5],[7,-6],[9,-7]] },
'|': { width: 8, points: [[4,25],[4,-7]] },
'}': { width: 14, points: [[5,25],[7,24],[8,23],[9,21],[9,19],[8,17],[7,16],[6,14],[6,12],[8,10],[-1,-1],[7,24],[8,22],[8,20],[7,18],[6,17],[5,15],[5,13],[6,11],[10,9],[6,7],[5,5],[5,3],[6,1],[7,0],[8,-2],[8,-4],[7,-6],[-1,-1],[8,8],[6,6],[6,4],[7,2],[8,1],[9,-1],[9,-3],[8,-5],[7,-6],[5,-7]] },
'~': { width: 24, points: [[3,6],[3,8],[4,11],[6,12],[8,12],[10,11],[14,8],[16,7],[18,7],[20,8],[21,10],[-1,-1],[3,8],[4,10],[6,11],[8,11],[10,10],[14,7],[16,6],[18,6],[20,7],[21,10],[21,12]] },
// Miscellaneous glyphs (EAM)
129: { width: 18, points: [[2,-2],[2,21],[16,21],[16,-2],[2,-2]] },
176: { width: 8, points: [[3,22],[1,20],[1,18],[3,16],[5,16],[7,18],[7,20],[5,22],[3,22]]},
197: { width: 18, points: [[9,18],[1,0],[-1,-1],[9,18],[17,0],[-1,-1],[4,7],[14,7],[-1,-1], [8,22],[6,20],[6,18],[8,16],[10,16],[12,18],[12,20],[10,22],[8,22]]},
215: { width: 26, points: [[6,17],[20,3],[-1,-1],[6,3],[20,17]] }
};
CanvasTextFunctions.letter = function (ch)
{
glyph = CanvasTextFunctions.letters[ch];
// EAM - Draw an empty box for undefined glyphs
if (glyph == undefined)
glyph = CanvasTextFunctions.letters[129];
return glyph;
}
CanvasTextFunctions.ascent = function( font, size)
{
return size;
}
CanvasTextFunctions.descent = function( font, size)
{
return 7.0*size/25.0;
}
CanvasTextFunctions.measure = function( font, size, str)
{
var total = 0;
var len = str.length;
for ( i = 0; i < len; i++) {
var index = str.charAt(i);
// EAM deal with non-ascii characaters
if (index > '~')
index = str.charCodeAt(i);
var c = CanvasTextFunctions.letter(index);
if (c) total += c.width * size / 25.0;
}
return total;
}
CanvasTextFunctions.draw = function(ctx,font,size,x,y,str)
{
var total = 0;
var len = str.length;
var mag = size / 25.0;
ctx.save();
ctx.lineCap = "round";
ctx.lineWidth = 2.0 * mag;
for ( i = 0; i < len; i++) {
var index = str.charAt(i);
// EAM deal with non-ascii characaters
if (index > '~')
index = str.charCodeAt(i);
var c = CanvasTextFunctions.letter(index);
if ( !c) continue;
ctx.beginPath();
var penUp = 1;
var needStroke = 0;
for ( j = 0; j < c.points.length; j++) {
var a = c.points[j];
if ( a[0] == -1 && a[1] == -1) {
penUp = 1;
continue;
}
if ( penUp) {
ctx.moveTo( x + a[0]*mag, y - a[1]*mag);
penUp = false;
} else {
ctx.lineTo( x + a[0]*mag, y - a[1]*mag);
}
}
ctx.stroke();
x += c.width*mag;
}
ctx.restore();
return total;
}
CanvasTextFunctions.enable = function( ctx)
{
ctx.drawText = function(font,size,x,y,text) { return CanvasTextFunctions.draw( ctx, font,size,x,y,text); };
ctx.measureText = function(font,size,text) { return CanvasTextFunctions.measure( font,size,text); };
ctx.fontAscent = function(font,size) { return CanvasTextFunctions.ascent(font,size); }
ctx.fontDescent = function(font,size) { return CanvasTextFunctions.descent(font,size); }
ctx.drawTextRight = function(font,size,x,y,text) {
var w = CanvasTextFunctions.measure(font,size,text);
return CanvasTextFunctions.draw( ctx, font,size,x-w,y,text);
};
ctx.drawTextCenter = function(font,size,x,y,text) {
var w = CanvasTextFunctions.measure(font,size,text);
return CanvasTextFunctions.draw( ctx, font,size,x-w/2,y,text);
};
}

View File

@ -0,0 +1,170 @@
/*
* $Id: gnuplot_common.js,v 1.10 2012/05/03 20:35:22 sfeam Exp $
*/
// Shared routines for gnuplot's HTML5 canvas terminal driver.
var gnuplot = { };
gnuplot.common_version = "3 May 2012";
gnuplot.L = function (x,y) {
if (gnuplot.zoomed) {
var zoom = gnuplot.zoomXY(x/10.0,y/10.0);
ctx.lineTo(zoom.x,zoom.y);
} else
ctx.lineTo(x/10.0,y/10.0);
}
gnuplot.M = function (x,y) {
if (gnuplot.zoomed) {
var zoom = gnuplot.zoomXY(x/10.0,y/10.0);
ctx.moveTo(zoom.x,zoom.y);
} else
ctx.moveTo(x/10.0,y/10.0);
}
gnuplot.R = function (x,y,w,h) {
if (gnuplot.zoomed) {
var dx, dy, dw, dh;
var zoom = gnuplot.zoomXY(x/10.0,y/10.0);
if (zoom.x >= gnuplot.plot_xmax) return;
if (zoom.y >= gnuplot.plot_ybot) return;
dx = zoom.x; dy = zoom.y;
zoom = gnuplot.zoomXY((x+w)/10.,(y+h)/10.);
if (zoom.xraw <= gnuplot.plot_xmin) return;
if (zoom.yraw <= gnuplot.plot_ytop) return;
dw = zoom.x - dx; dh = zoom.y -dy;
ctx.fillRect(dx, dy, dw, dh);
} else
ctx.fillRect(x/10.0, y/10.0, w/10.0, h/10.0);
}
gnuplot.T = function (x,y,fontsize,justify,string) {
var xx = x/10.0; var yy = y/10.0;
if (gnuplot.zoomed) {
var zoom = gnuplot.zoomXY(xx,yy);
if (zoom.clip) return;
xx = zoom.x; yy = zoom.y;
if (gnuplot.plot_xmin < xx && xx < gnuplot.plot_xmax && gnuplot.plot_ybot > yy && yy > gnuplot.plot_ytop)
if ((typeof(gnuplot.zoom_text) != "undefined") && (gnuplot.zoom_text == true))
fontsize = Math.sqrt(gnuplot.zoomW(fontsize)*gnuplot.zoomH(fontsize));
}
if (justify=="") ctx.drawText("sans", fontsize, xx, yy, string);
else if (justify=="Right") ctx.drawTextRight("sans", fontsize, xx, yy, string);
else if (justify=="Center") ctx.drawTextCenter("sans", fontsize, xx, yy, string);
}
gnuplot.TR = function (x,y,angle,fontsize,justify,string) {
var xx = x/10.0; var yy = y/10.0;
if (gnuplot.zoomed) {
var zoom = gnuplot.zoomXY(xx,yy);
if (zoom.clip) return;
xx = zoom.x; yy = zoom.y;
if (gnuplot.plot_xmin < xx && xx < gnuplot.plot_xmax && gnuplot.plot_ybot > yy && yy > gnuplot.plot_ytop)
if ((typeof(gnuplot.zoom_text) != "undefined") && (gnuplot.zoom_text == true))
fontsize = Math.sqrt(gnuplot.zoomW(fontsize)*gnuplot.zoomH(fontsize));
}
ctx.save();
ctx.translate(xx,yy);
ctx.rotate(angle * Math.PI / 180);
if (justify=="") ctx.drawText("sans", fontsize, 0, 0, string);
else if (justify=="Right") ctx.drawTextRight("sans", fontsize, 0, 0, string);
else if (justify=="Center") ctx.drawTextCenter("sans", fontsize, 0, 0, string);
ctx.restore();
}
gnuplot.bp = function (x,y) // begin polygon
{ ctx.beginPath(); gnuplot.M(x,y); }
gnuplot.cfp = function () // close and fill polygon
{ ctx.closePath(); ctx.fill(); }
gnuplot.cfsp = function () // close and fill polygon with stroke color
{ ctx.closePath(); ctx.fillStyle = ctx.strokeStyle; ctx.stroke(); ctx.fill(); }
gnuplot.Dot = function (x,y) {
var xx = x; var yy = y;
if (gnuplot.zoomed) {zoom = gnuplot.zoomXY(xx,yy); xx = zoom.x; yy = zoom.y; if (zoom.clip) return;}
ctx.strokeRect(xx,yy,0.5,0.5);
}
gnuplot.Pt = function (N,x,y,w) {
var xx = x; var yy = y;
if (gnuplot.zoomed) {var zoom = gnuplot.zoomXY(xx,yy); xx = zoom.x; yy = zoom.y; if (zoom.clip) return;}
if (w==0) return;
switch (N)
{
case 0:
ctx.beginPath();
ctx.moveTo(xx-w,yy); ctx.lineTo(xx+w,yy);
ctx.moveTo(xx,yy-w); ctx.lineTo(xx,yy+w);
ctx.stroke();
break;
case 1:
var ww = w * 3/4;
ctx.beginPath();
ctx.moveTo(xx-ww,yy-ww); ctx.lineTo(xx+ww,yy+ww);
ctx.moveTo(xx+ww,yy-ww); ctx.lineTo(xx-ww,yy+ww);
ctx.stroke();
break;
case 2:
gnuplot.Pt(0,x,y,w); gnuplot.Pt(1,x,y,w);
break;
case 3:
ctx.strokeRect(xx-w/2,yy-w/2,w,w);
break;
case 4:
ctx.save(); ctx.strokeRect(xx-w/2,yy-w/2,w,w); ctx.restore();
ctx.fillRect(xx-w/2,yy-w/2,w,w);
break;
case 5:
ctx.beginPath(); ctx.arc(xx,yy,w/2,0,Math.PI*2,true); ctx.stroke();
break;
default:
case 6:
ctx.beginPath(); ctx.arc(xx,yy,w/2,0,Math.PI*2,true); ctx.fill();
break;
case 7:
ctx.beginPath();
ctx.moveTo(xx,yy-w); ctx.lineTo(xx-w,yy+w/2); ctx.lineTo(xx+w,yy+w/2);
ctx.closePath();
ctx.stroke();
break;
case 8:
ctx.beginPath();
ctx.moveTo(xx,yy-w); ctx.lineTo(xx-w,yy+w/2); ctx.lineTo(xx+w,yy+w/2);
ctx.closePath();
ctx.fill();
break;
}
}
// Zoomable image
gnuplot.ZI = function (image, m, n, x1, y1, x2, y2) {
if (gnuplot.zoomed) {
var sx, sy, sw, sh, dx, dy, dw, dh;
var zoom = gnuplot.zoomXY(x1/10.0,y1/10.0);
if (zoom.x >= gnuplot.plot_xmax) return;
if (zoom.y >= gnuplot.plot_ybot) return;
x1raw = zoom.xraw; y1raw = zoom.yraw;
dx = zoom.x; dy = zoom.y;
zoom = gnuplot.zoomXY((x2)/10.,(y2)/10.);
if (zoom.xraw <= gnuplot.plot_xmin) return;
if (zoom.yraw <= gnuplot.plot_ytop) return;
var x2raw = zoom.xraw; var y2raw = zoom.yraw;
dw = zoom.x - dx; dh = zoom.y - dy;
// FIXME: This is sometimes flaky. Needs integer truncation?
sx = 0; sy = 0; sw = m; sh = n;
if (x1raw < dx) sx = m * (dx - x1raw) / (x2raw - x1raw);
if (y1raw < dy) sy = n * (dy - y1raw) / (y2raw - y1raw);
if (x2raw > zoom.x)
sw = m * (1. - ((x2raw - zoom.x) / (x2raw - x1raw)));
if (y2raw > zoom.y)
sh = n * (1. - ((y2raw - zoom.y) / (y2raw - y1raw)));
sw = sw - sx; sh = sh - sy;
ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
} else
ctx.drawImage(image, x1/10.0, y1/10.0, (x2-x1)/10.0, (y2-y1)/10.0);
}
// These methods are place holders that are loaded by gnuplot_dashedlines.js
gnuplot.dashtype = function (dt) {} ;
gnuplot.dashstart = function (x,y) {gnuplot.M(x,y);} ;
gnuplot.dashstep = function (x,y) {gnuplot.L(x,y);} ;
gnuplot.pattern = [];

View File

@ -0,0 +1,68 @@
/*
* $Id: gnuplot_dashedlines.js,v 1.3 2012/05/03 20:35:22 sfeam Exp $
*/
/*
* Ethan Merritt November 2010
* Add a dashed line method to gnuplot's HTML5 canvas toolset.
* To start a path use DS(x0,y0) instead of M(x0,y0)
* To draw line segments use DL(x,y) instead of L(x,y)
* Finish as usual with ctx.stroke(); ctx.closePath();
*/
gnuplot.solid = [];
gnuplot.dashpattern1 = gnuplot.solid;
gnuplot.dashpattern2 = [ 0.5, 1.0 ];
gnuplot.dashpattern3 = [ .12, .25, .37, .50, .62, .75, .87, 1.0 ];
gnuplot.dashpattern4 = [ 0.50, 0.70, 0.80, 1.0 ];
gnuplot.dashpattern5 = [ 0.30, 0.50, 0.60, 0.75, 0.85, 1.0 ];
gnuplot.dashlength = 200.; // length of one complete pattern
gnuplot.dashfraction = 0; // how far in the pattern we got last time
gnuplot.dash_x = 0;
gnuplot.dash_y = 0;
gnuplot.dashtype = function(dt) {
gnuplot.pattern = dt;
}
gnuplot.dashstart = function(x,y) {
gnuplot.dash_x = x;
gnuplot.dash_y = y;
gnuplot.dashfraction = 0;
gnuplot.M(x,y);
}
gnuplot.dashstep = function(x,y) {
var delx = x - gnuplot.dash_x;
var dely = y - gnuplot.dash_y;
var stride = Math.sqrt(delx*delx + dely*dely) / gnuplot.dashlength;
var this_step; var new_x, new_y;
if (gnuplot.pattern.length == 0) {gnuplot.L(x,y); return;}
while (stride > 0) {
// Find which piece of the pattern we are in
for (i=0; gnuplot.pattern[i] <= gnuplot.dashfraction; i++);
this_step = gnuplot.pattern[i] - gnuplot.dashfraction;
if (stride > this_step) {
new_x = gnuplot.dash_x + delx*this_step/stride;
new_y = gnuplot.dash_y + dely*this_step/stride;
stride = stride - this_step;
gnuplot.dashfraction = gnuplot.pattern[i];
delx = x - new_x;
dely = y - new_y;
} else {
new_x = x;
new_y = y;
gnuplot.dashfraction = gnuplot.dashfraction + stride;
stride = 0;
}
if (i%2==0) gnuplot.L(new_x,new_y);
else gnuplot.M(new_x,new_y);
gnuplot.dash_x = new_x;
gnuplot.dash_y = new_y;
if (gnuplot.dashfraction >= 1.0) gnuplot.dashfraction = 0;
}
}

View File

@ -0,0 +1,19 @@
canvas { border:0; background-color: white; }
td.mousebox { vertical-align: top; }
td.mbh { height: 16pt; }
td.mb0 { text-align: left; font-size: smaller; width: 20px; padding-left: 15px;
color:#000; background-color: #EEE}
td.mb1 { text-align: left; font-size: smaller; width: 80px; padding-left: 15px;
color:#070; background-color: #EEE}
td.icon { width: 16px; height: 16px; font-size: 14px; text-align: right;
background-color: #EEE}
td.blank { height: 16px; font-size: 12pt; text-align: right;
background-color: #EEE}
.icon-image { float:right; margin: 0 0 0 0; }
.noborder { border: 0; vertical-align: text-top; }
table.mbleft { border-collapse: collapse; border: 0; margin-left: auto; margin-right: auto; }
table.mbright { border-collapse: collapse; border: 0; margin-left: 0; margin-right: auto; }
table.mbunder { border-collapse: collapse; border: 0; margin-left: auto; margin-right: auto; }
table.plot { border-collapse: collapse; border: 0; margin-left: auto; margin-right: auto; }
table.mousebox { border-collapse: collapse; margin-left: 0; margin-right: 10px; border: 0; }
div.gnuplot { background-color: white; }

View File

@ -0,0 +1,619 @@
/*
* $Id: gnuplot_mouse.js,v 1.28 2017/02/17 21:27:53 sfeam Exp $
*/
gnuplot.mouse_version = " 17 February 2017";
// Mousing code for use with gnuplot's 'canvas' terminal driver.
// The functions defined here assume that the javascript plot produced by
// gnuplot initializes the plot boundary and scaling parameters.
gnuplot.mousex = 0;
gnuplot.mousey = 0;
gnuplot.plotx = 0;
gnuplot.ploty = 0;
gnuplot.scaled_x = 0;
gnuplot.scaled_y = 0;
// These will be initialized by the gnuplot canvas-drawing function
gnuplot.plot_xmin = 0;
gnuplot.plot_xmax = 0;
gnuplot.plot_ybot = 0;
gnuplot.plot_ytop = 0;
gnuplot.plot_width = 0
gnuplot.plot_height = 0
gnuplot.plot_term_ymax = 0;
gnuplot.plot_axis_xmin = 0;
gnuplot.plot_axis_xmax = 0;
gnuplot.plot_axis_width = 0;
gnuplot.plot_axis_height = 0;
gnuplot.plot_axis_ymin = 0;
gnuplot.plot_axis_ymax = 0;
gnuplot.plot_axis_x2min = "none";
gnuplot.plot_axis_y2min = "none";
gnuplot.plot_logaxis_x = 0;
gnuplot.plot_logaxis_y = 0;
gnuplot.grid_lines = true;
gnuplot.zoom_text = false;
gnuplot.display_is_uptodate = false;
// These are the equivalent parameters while zooming
gnuplot.zoom_axis_xmin = 0;
gnuplot.zoom_axis_xmax = 0;
gnuplot.zoom_axis_ymin = 0;
gnuplot.zoom_axis_ymax = 0;
gnuplot.zoom_axis_x2min = 0;
gnuplot.zoom_axis_x2max = 0;
gnuplot.zoom_axis_y2min = 0;
gnuplot.zoom_axis_y2max = 0;
gnuplot.zoom_axis_width = 0;
gnuplot.zoom_axis_height = 0;
gnuplot.zoom_temp_xmin = 0;
gnuplot.zoom_temp_ymin = 0;
gnuplot.zoom_temp_x2min = 0;
gnuplot.zoom_temp_y2min = 0;
gnuplot.zoom_in_progress = false;
gnuplot.full_canvas_image = null;
gnuplot.axisdate = new Date();
gnuplot.init = function ()
{
if (document.getElementById("gnuplot_canvas"))
document.getElementById("gnuplot_canvas").onmousemove = gnuplot.mouse_update;
if (document.getElementById("gnuplot_canvas"))
document.getElementById("gnuplot_canvas").onmouseup = gnuplot.zoom_in;
if (document.getElementById("gnuplot_canvas"))
document.getElementById("gnuplot_canvas").onmousedown = gnuplot.saveclick;
if (document.getElementById("gnuplot_canvas"))
document.getElementById("gnuplot_canvas").onkeydown = gnuplot.do_hotkey;
if (document.getElementById("gnuplot_grid_icon"))
document.getElementById("gnuplot_grid_icon").onmouseup = gnuplot.toggle_grid;
if (document.getElementById("gnuplot_textzoom_icon"))
document.getElementById("gnuplot_textzoom_icon").onmouseup = gnuplot.toggle_zoom_text;
if (document.getElementById("gnuplot_rezoom_icon"))
document.getElementById("gnuplot_rezoom_icon").onmouseup = gnuplot.rezoom;
if (document.getElementById("gnuplot_unzoom_icon"))
document.getElementById("gnuplot_unzoom_icon").onmouseup = gnuplot.unzoom;
gnuplot.mouse_update();
}
gnuplot.getMouseCoordsWithinTarget = function(event)
{
var coords = { x: 0, y: 0};
if(!event) // then we're in a non-DOM (probably IE) browser
{
event = window.event;
if (event) {
coords.x = event.offsetX;
coords.y = event.offsetY;
}
}
else // we assume DOM modeled javascript
{
var Element = event.target ;
var CalculatedTotalOffsetLeft = 0;
var CalculatedTotalOffsetTop = 0 ;
while (Element.offsetParent)
{
CalculatedTotalOffsetLeft += Element.offsetLeft ;
CalculatedTotalOffsetTop += Element.offsetTop ;
Element = Element.offsetParent ;
}
coords.x = event.pageX - CalculatedTotalOffsetLeft ;
coords.y = event.pageY - CalculatedTotalOffsetTop ;
}
gnuplot.mousex = coords.x;
gnuplot.mousey = coords.y;
}
gnuplot.mouse_update = function(e)
{
gnuplot.getMouseCoordsWithinTarget(e);
gnuplot.plotx = gnuplot.mousex - gnuplot.plot_xmin;
gnuplot.ploty = -(gnuplot.mousey - gnuplot.plot_ybot);
// Limit tracking to the interior of the plot
if (gnuplot.plotx < 0 || gnuplot.ploty < 0){
if (gnuplot.hypertext_list != "undefined" && gnuplot.hypertext_list.length > 0) {
gnuplot.check_hypertext();
}
return;
}
if (gnuplot.mousex > gnuplot.plot_xmax || gnuplot.mousey < gnuplot.plot_ytop){
if (gnuplot.hypertext_list != "undefined" && gnuplot.hypertext_list.length > 0) {
gnuplot.check_hypertext();
}
return;
}
var axis_xmin = (gnuplot.zoomed) ? gnuplot.zoom_axis_xmin : gnuplot.plot_axis_xmin;
var axis_xmax = (gnuplot.zoomed) ? gnuplot.zoom_axis_xmax : gnuplot.plot_axis_xmax;
var axis_ymin = (gnuplot.zoomed) ? gnuplot.zoom_axis_ymin : gnuplot.plot_axis_ymin;
var axis_ymax = (gnuplot.zoomed) ? gnuplot.zoom_axis_ymax : gnuplot.plot_axis_ymax;
if (gnuplot.plot_logaxis_x != 0) {
x = Math.log(axis_xmax) - Math.log(axis_xmin);
x = x * (gnuplot.plotx / (gnuplot.plot_xmax-gnuplot.plot_xmin)) + Math.log(axis_xmin);
x = Math.exp(x);
} else {
x = axis_xmin + (gnuplot.plotx / (gnuplot.plot_xmax-gnuplot.plot_xmin)) * (axis_xmax - axis_xmin);
}
if (gnuplot.plot_logaxis_y != 0) {
y = Math.log(axis_ymax) - Math.log(axis_ymin);
y = y * (-gnuplot.ploty / (gnuplot.plot_ytop-gnuplot.plot_ybot)) + Math.log(axis_ymin);
y = Math.exp(y);
} else {
y = axis_ymin - (gnuplot.ploty / (gnuplot.plot_ytop-gnuplot.plot_ybot)) * (axis_ymax - axis_ymin);
}
if (gnuplot.plot_axis_x2min != "none") {
gnuplot.axis_x2min = (gnuplot.zoomed) ? gnuplot.zoom_axis_x2min : gnuplot.plot_axis_x2min;
gnuplot.axis_x2max = (gnuplot.zoomed) ? gnuplot.zoom_axis_x2max : gnuplot.plot_axis_x2max;
if (gnuplot.x2_mapping != undefined)
x2 = gnuplot.x2_mapping(x);
else
x2 = gnuplot.axis_x2min + (gnuplot.plotx / (gnuplot.plot_xmax-gnuplot.plot_xmin)) * (gnuplot.axis_x2max - gnuplot.axis_x2min);
if (document.getElementById(gnuplot.active_plot_name + "_x2"))
document.getElementById(gnuplot.active_plot_name + "_x2").innerHTML = x2.toPrecision(4);
}
if (gnuplot.plot_axis_y2min != "none") {
gnuplot.axis_y2min = (gnuplot.zoomed) ? gnuplot.zoom_axis_y2min : gnuplot.plot_axis_y2min;
gnuplot.axis_y2max = (gnuplot.zoomed) ? gnuplot.zoom_axis_y2max : gnuplot.plot_axis_y2max;
if (gnuplot.y2_mapping != undefined)
y2 = gnuplot.y2_mapping(y);
else
y2 = gnuplot.axis_y2min - (gnuplot.ploty / (gnuplot.plot_ytop-gnuplot.plot_ybot)) * (gnuplot.axis_y2max - gnuplot.axis_y2min);
if (document.getElementById(gnuplot.active_plot_name + "_y2"))
document.getElementById(gnuplot.active_plot_name + "_y2").innerHTML = y2.toPrecision(4);
}
var label_x, label_y;
if (gnuplot.polar_mode) {
var polar = gnuplot.convert_to_polar(x,y);
label_x = "ang= " + polar.ang.toPrecision(4);
label_y = "R= " + polar.r.toPrecision(4);
} else {
if (typeof(gnuplot.plot_timeaxis_x) == "string" && gnuplot.plot_timeaxis_x != "")
label_x = gnuplot.datafmt(x);
else
label_x = x.toPrecision(4);
if (typeof(gnuplot.plot_timeaxis_y) == "string" && gnuplot.plot_timeaxis_y != "")
label_y = gnuplot.datafmt(y);
else
label_y = y.toPrecision(4);
}
if (document.getElementById(gnuplot.active_plot_name + "_x"))
document.getElementById(gnuplot.active_plot_name + "_x").innerHTML = label_x;
if (document.getElementById(gnuplot.active_plot_name + "_y"))
document.getElementById(gnuplot.active_plot_name + "_y").innerHTML = label_y;
// Echo the zoom box interactively
if (gnuplot.zoom_in_progress) {
// Clear previous box before drawing a new one
if (gnuplot.full_canvas_image == null) {
gnuplot.full_canvas_image = ctx.getImageData(0,0,canvas.width,canvas.height);
} else {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.putImageData(gnuplot.full_canvas_image,0,0);
}
ctx.strokeStyle="rgba(128,128,128,0.60)";
var x0 = gnuplot.plot_xmin + gnuplot.zoom_temp_plotx;
var y0 = gnuplot.plot_ybot - gnuplot.zoom_temp_ploty;
var w = gnuplot.plotx - gnuplot.zoom_temp_plotx;
var h = -(gnuplot.ploty - gnuplot.zoom_temp_ploty);
if (w<0) {x0 = x0 + w; w = -w;}
if (h<0) {y0 = y0 + h; h = -h;}
ctx.strokeRect(x0,y0,w,h);
}
// See if we are over a hypertext anchor point
if (gnuplot.hypertext_list != "undefined" && gnuplot.hypertext_list.length > 0) {
gnuplot.check_hypertext();
}
}
gnuplot.check_hypertext = function()
{
var nitems = gnuplot.hypertext_list.length;
for (var i=0; i<nitems; i++) {
var linkx = gnuplot.hypertext_list[i].x / 10.;
var linky = gnuplot.hypertext_list[i].y / 10.;
if (gnuplot.zoomed) {
var zoom = gnuplot.zoomXY(linkx,linky);
linkx = zoom.x; linky = zoom.y;
}
var delx = Math.abs(gnuplot.mousex - linkx);
var dely = Math.abs(gnuplot.mousey - linky);
var w = gnuplot.hypertext_list[i].w / 20.;
if (delx < w && dely < w) {
if (i == gnuplot.on_hypertext)
break;
gnuplot.on_hypertext = i;
var text = gnuplot.hypertext_list[i].text;
var lines = text.split('\v');
var len = 0;
if (lines.length <= 1) {
len = ctx.measureText("sans", 10, text);
} else {
for (var l=0; l<lines.length; l++) {
var ll = ctx.measureText("sans", 10, lines[l]);
if (len < ll) len = ll;
}
}
// text shift (so it is not covered by cursor)
var shiftx = 14;
if (linkx > gnuplot.plot_term_xmax-(len+8) ){
// position text left of cursor if we are to far in the right hemisphere
// else we might print over the border
linkx = linkx - len;
shiftx = -shiftx;
}
if (linky > 14*lines.length ){
// position text top of cursor if we are low enough in the plot
// else we might print over the border
linky = linky - 14*lines.length;
}
ctx.fillStyle = "rgba(238,238,238,0.8)"
ctx.fillRect(linkx+shiftx, linky+4, len+8, 14*lines.length);
for (var l=0; l<lines.length; l++) {
ctx.drawText("sans", 10, linkx+shiftx+4, linky+14+l*14, lines[l]);
}
break;
}
}
if (i == nitems && gnuplot.on_hypertext >= 0) {
gnuplot.on_hypertext = -1;
ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax);
gnuplot.display_is_uptodate = false;
gnuplot_canvas();
}
}
gnuplot.datafmt = function (x)
{
if (gnuplot.plot_timeaxis_x == "DMS") {
return gnuplot.convert_to_DMS(x);
}
// gnuplot 4.6 used (x + 946684800)
gnuplot.axisdate.setTime(1000. * x);
if (gnuplot.plot_timeaxis_x == "DateTime") {
return gnuplot.axisdate.toUTCString();
}
if (gnuplot.plot_timeaxis_x == "Date") {
var year = gnuplot.axisdate.getUTCFullYear();
var month = gnuplot.axisdate.getUTCMonth();
var date = gnuplot.axisdate.getUTCDate();
return (" " + date).slice (-2) + "/"
+ ("0" + (month+1)).slice (-2) + "/"
+ year;
}
if (gnuplot.plot_timeaxis_x == "Time") {
var hour = gnuplot.axisdate.getUTCHours();
var minute = gnuplot.axisdate.getUTCMinutes();
var second = gnuplot.axisdate.getUTCSeconds();
return ("0" + hour).slice (-2) + ":"
+ ("0" + minute).slice (-2) + ":"
+ ("0" + second).slice (-2);
}
}
gnuplot.convert_to_DMS = function (x)
{
var dms = {d:0, m:0, s:0};
var deg = Math.abs(x);
dms.d = Math.floor(deg);
dms.m = Math.floor((deg - dms.d) * 60.);
dms.s = Math.floor((deg - dms.d) * 3600. - dms.m * 60.);
fmt = ((x<0)?"-":" ")
+ dms.d.toFixed(0) + "°"
+ dms.m.toFixed(0) + "\""
+ dms.s.toFixed(0) + "'";
return fmt;
}
gnuplot.convert_to_polar = function (x,y)
{
var polar = new Object;
var phi, r;
phi = Math.atan2(y,x);
if (gnuplot.plot_logaxis_r)
r = Math.exp( (x/Math.cos(phi) + Math.log(gnuplot.plot_axis_rmin)/Math.LN10) * Math.LN10);
else if (gnuplot.plot_axis_rmin > gnuplot.plot_axis_rmax)
r = gnuplot.plot_axis_rmin - x/Math.cos(phi);
else
r = gnuplot.plot_axis_rmin + x/Math.cos(phi);
phi = phi * (180./Math.PI);
if (gnuplot.polar_sense < 0)
phi = -phi;
if (gnuplot.polar_theta0 != undefined)
phi = phi + gnuplot.polar_theta0;
if (phi > 180.)
phi = phi - 360.;
polar.r = r;
polar.ang = phi;
return polar;
}
gnuplot.saveclick = function (event)
{
gnuplot.mouse_update(event);
var button, label_x, label_y;
// Limit tracking to the interior of the plot
if (gnuplot.plotx < 0 || gnuplot.ploty < 0) return;
if (gnuplot.mousex > gnuplot.plot_xmax || gnuplot.mousey < gnuplot.plot_ytop) return;
if (event.which == null) /* IE case */
button= (event.button < 2) ? "LEFT" : ((event.button == 4) ? "MIDDLE" : "RIGHT");
else /* All others */
button= (event.which < 2) ? "LEFT" : ((event.which == 2) ? "MIDDLE" : "RIGHT");
if (button == "LEFT") {
ctx.strokeStyle="black";
ctx.strokeRect(gnuplot.mousex, gnuplot.mousey, 1, 1);
if (typeof(gnuplot.plot_timeaxis_x) == "string" && gnuplot.plot_timeaxis_x != "")
label_x = gnuplot.datafmt(x);
else
label_x = x.toPrecision(4);
if (typeof(gnuplot.plot_timeaxis_y) == "string" && gnuplot.plot_timeaxis_y != "")
label_y = gnuplot.datafmt(y);
else
label_y = y.toPrecision(4);
click = " " + label_x + ", " + label_y;
var len = ctx.measureText("sans", 9, click);
if (gnuplot.mousex > gnuplot.plot_term_xmax-(len) ){
// draw left of cursor when we are near the right side of the plot
len += ctx.measureText("sans", 9, " ");
ctx.drawText("sans", 9, gnuplot.mousex-len, gnuplot.mousey, click);
} else {
ctx.drawText("sans", 9, gnuplot.mousex, gnuplot.mousey, click);
}
}
// Save starting corner of zoom box
else {
gnuplot.zoom_temp_xmin = x;
gnuplot.zoom_temp_ymin = y;
if (gnuplot.plot_axis_x2min != "none") gnuplot.zoom_temp_x2min = x2;
if (gnuplot.plot_axis_y2min != "none") gnuplot.zoom_temp_y2min = y2;
// Only used to echo the zoom box interactively
gnuplot.zoom_temp_plotx = gnuplot.plotx;
gnuplot.zoom_temp_ploty = gnuplot.ploty;
gnuplot.zoom_in_progress = true;
gnuplot.full_canvas_image = null;
}
return false; // Nobody else should respond to this event
}
gnuplot.zoom_in = function (event)
{
if (!gnuplot.zoom_in_progress)
return false;
gnuplot.mouse_update(event);
var button;
if (event.which == null) /* IE case */
button= (event.button < 2) ? "LEFT" : ((event.button == 4) ? "MIDDLE" : "RIGHT");
else /* All others */
button= (event.which < 2) ? "LEFT" : ((event.which == 2) ? "MIDDLE" : "RIGHT");
// Save ending corner of zoom box
if (button != "LEFT") {
if (x > gnuplot.zoom_temp_xmin) {
gnuplot.zoom_axis_xmin = gnuplot.zoom_temp_xmin;
gnuplot.zoom_axis_xmax = x;
if (gnuplot.plot_axis_x2min != "none") {
gnuplot.zoom_axis_x2min = gnuplot.zoom_temp_x2min;
gnuplot.zoom_axis_x2max = x2;
}
} else {
gnuplot.zoom_axis_xmin = x;
gnuplot.zoom_axis_xmax = gnuplot.zoom_temp_xmin;
if (gnuplot.plot_axis_x2min != "none") {
gnuplot.zoom_axis_x2min = x2;
gnuplot.zoom_axis_x2max = gnuplot.zoom_temp_x2min;
}
}
if (y > gnuplot.zoom_temp_ymin) {
gnuplot.zoom_axis_ymin = gnuplot.zoom_temp_ymin;
gnuplot.zoom_axis_ymax = y;
if (gnuplot.plot_axis_y2min != "none") {
gnuplot.zoom_axis_y2min = gnuplot.zoom_temp_y2min;
gnuplot.zoom_axis_y2max = y2;
}
} else {
gnuplot.zoom_axis_ymin = y;
gnuplot.zoom_axis_ymax = gnuplot.zoom_temp_ymin;
if (gnuplot.plot_axis_y2min != "none") {
gnuplot.zoom_axis_y2min = y2;
gnuplot.zoom_axis_y2max = gnuplot.zoom_temp_y2min;
}
}
gnuplot.zoom_axis_width = gnuplot.zoom_axis_xmax - gnuplot.zoom_axis_xmin;
gnuplot.zoom_axis_height = gnuplot.zoom_axis_ymax - gnuplot.zoom_axis_ymin;
gnuplot.zoom_in_progress = false;
gnuplot.rezoom(event);
}
return false; // Nobody else should respond to this event
}
gnuplot.toggle_grid = function (e)
{
if (!gnuplot.grid_lines) gnuplot.grid_lines = true;
else gnuplot.grid_lines = false;
ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax);
gnuplot.display_is_uptodate = false;
gnuplot_canvas();
}
gnuplot.toggle_zoom_text = function (e)
{
if (!gnuplot.zoom_text) gnuplot.zoom_text = true;
else gnuplot.zoom_text = false;
ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax);
gnuplot.display_is_uptodate = false;
gnuplot_canvas();
}
gnuplot.rezoom = function (e)
{
if (gnuplot.zoom_axis_width > 0)
gnuplot.zoomed = true;
ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax);
gnuplot.display_is_uptodate = false;
gnuplot_canvas();
}
gnuplot.unzoom = function (e)
{
gnuplot.zoomed = false;
ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax);
gnuplot.display_is_uptodate = false;
gnuplot_canvas();
}
gnuplot.zoomXY = function(x,y)
{
var zoom = new Object;
var xreal, yreal;
zoom.x = x; zoom.y = y; zoom.clip = false;
if (gnuplot.plot_logaxis_x != 0) {
xreal = Math.log(gnuplot.plot_axis_xmax) - Math.log(gnuplot.plot_axis_xmin);
xreal = Math.log(gnuplot.plot_axis_xmin) + (x - gnuplot.plot_xmin) * xreal/gnuplot.plot_width;
zoom.x = Math.log(gnuplot.zoom_axis_xmax) - Math.log(gnuplot.zoom_axis_xmin);
zoom.x = gnuplot.plot_xmin + (xreal - Math.log(gnuplot.zoom_axis_xmin)) * gnuplot.plot_width/zoom.x;
} else {
xreal = gnuplot.plot_axis_xmin + (x - gnuplot.plot_xmin) * (gnuplot.plot_axis_width/gnuplot.plot_width);
zoom.x = gnuplot.plot_xmin + (xreal - gnuplot.zoom_axis_xmin) * (gnuplot.plot_width/gnuplot.zoom_axis_width);
}
if (gnuplot.plot_logaxis_y != 0) {
yreal = Math.log(gnuplot.plot_axis_ymax) - Math.log(gnuplot.plot_axis_ymin);
yreal = Math.log(gnuplot.plot_axis_ymin) + (gnuplot.plot_ybot - y) * yreal/gnuplot.plot_height;
zoom.y = Math.log(gnuplot.zoom_axis_ymax) - Math.log(gnuplot.zoom_axis_ymin);
zoom.y = gnuplot.plot_ybot - (yreal - Math.log(gnuplot.zoom_axis_ymin)) * gnuplot.plot_height/zoom.y;
} else {
yreal = gnuplot.plot_axis_ymin + (gnuplot.plot_ybot - y) * (gnuplot.plot_axis_height/gnuplot.plot_height);
zoom.y = gnuplot.plot_ybot - (yreal - gnuplot.zoom_axis_ymin) * (gnuplot.plot_height/gnuplot.zoom_axis_height);
}
// Report unclipped coords also
zoom.xraw = zoom.x; zoom.yraw = zoom.y;
// Limit the zoomed plot to the original plot area
if (x > gnuplot.plot_xmax) {
zoom.x = x;
if (gnuplot.plot_axis_y2min == "none") {
zoom.y = y;
return zoom;
}
if (gnuplot.plot_ybot <= y && y <= gnuplot.plot_ybot + 15)
zoom.clip = true;
}
else if (x < gnuplot.plot_xmin)
zoom.x = x;
else if (zoom.x < gnuplot.plot_xmin)
{ zoom.x = gnuplot.plot_xmin; zoom.clip = true; }
else if (zoom.x > gnuplot.plot_xmax)
{ zoom.x = gnuplot.plot_xmax; zoom.clip = true; }
if (y < gnuplot.plot_ytop) {
zoom.y = y;
if (gnuplot.plot_axis_x2min == "none") {
zoom.x = x; zoom.clip = false;
return zoom;
}
}
else if (y > gnuplot.plot_ybot)
zoom.y = y;
else if (zoom.y > gnuplot.plot_ybot)
{ zoom.y = gnuplot.plot_ybot; zoom.clip = true; }
else if (zoom.y < gnuplot.plot_ytop)
{ zoom.y = gnuplot.plot_ytop; zoom.clip = true; }
return zoom;
}
gnuplot.zoomW = function (w) { return (w*gnuplot.plot_axis_width/gnuplot.zoom_axis_width); }
gnuplot.zoomH = function (h) { return (h*gnuplot.plot_axis_height/gnuplot.zoom_axis_height); }
gnuplot.popup_help = function(URL) {
if (typeof(URL) != "string") {
if (typeof(gnuplot.help_URL) == "string")
URL = gnuplot.help_URL;
else
return;
}
// FIXME: Placeholder for useful action
if (URL != "")
window.open (URL,"gnuplot help");
}
gnuplot.toggle_plot = function(plotid) {
if (typeof(gnuplot["hide_"+plotid]) == "unknown")
gnuplot["hide_"+plotid] = false;
gnuplot["hide_"+plotid] = !gnuplot["hide_"+plotid];
ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax);
gnuplot.display_is_uptodate = false;
gnuplot_canvas();
}
gnuplot.do_hotkey = function(event) {
keychar = String.fromCharCode(event.charCode ? event.charCode : event.keyCode);
switch (keychar) {
case 'e': ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax);
gnuplot.display_is_uptodate = false;
gnuplot_canvas();
break;
case 'g': gnuplot.toggle_grid();
break;
case 'n': gnuplot.rezoom();
break;
case 'r':
ctx.lineWidth = 0.5;
ctx.strokeStyle="rgba(128,128,128,0.50)";
ctx.moveTo(gnuplot.plot_xmin, gnuplot.mousey); ctx.lineTo(gnuplot.plot_xmax, gnuplot.mousey);
ctx.moveTo(gnuplot.mousex, gnuplot.plot_ybot); ctx.lineTo(gnuplot.mousex, gnuplot.plot_ytop);
ctx.stroke();
break;
case 'p':
case 'u': gnuplot.unzoom();
break;
case '': zoom_in_progress = false;
break;
// Arrow keys
case '%': // ctx.drawText("sans", 10, gnuplot.mousex, gnuplot.mousey, "<");
break;
case '\'': // ctx.drawText("sans", 10, gnuplot.mousex, gnuplot.mousey, ">");
break;
case '&': // ctx.drawText("sans", 10, gnuplot.mousex, gnuplot.mousey, "^");
break;
case '(': // ctx.drawText("sans", 10, gnuplot.mousex, gnuplot.mousey, "v");
break;
default: ctx.drawText("sans", 10, gnuplot.mousex, gnuplot.mousey, keychar);
return true; // Let someone else handle it
break;
}
return false; // Nobody else should respond to this keypress
}

View File

@ -0,0 +1,376 @@
// Javascript routines for interaction with SVG documents produced by
// gnuplot's SVG terminal driver.
// Find your root SVG element
var svg = document.querySelector('svg');
// Create an SVGPoint for future math
var pt = svg.createSVGPoint();
// Get point in global SVG space
function cursorPoint(evt){
pt.x = evt.clientX; pt.y = evt.clientY;
return pt.matrixTransform(svg.getScreenCTM().inverse());
}
var gnuplot_svg = { };
gnuplot_svg.version = "17 February 2017";
gnuplot_svg.SVGDoc = null;
gnuplot_svg.SVGRoot = null;
gnuplot_svg.Init = function(e)
{
gnuplot_svg.SVGDoc = e.target.ownerDocument;
gnuplot_svg.SVGRoot = gnuplot_svg.SVGDoc.documentElement;
gnuplot_svg.axisdate = new Date();
}
gnuplot_svg.toggleVisibility = function(evt, targetId)
{
var newTarget = evt.target;
if (targetId)
newTarget = gnuplot_svg.SVGDoc.getElementById(targetId);
var newValue = newTarget.getAttributeNS(null, 'visibility')
if ('hidden' != newValue)
newValue = 'hidden';
else
newValue = 'visible';
newTarget.setAttributeNS(null, 'visibility', newValue);
if (targetId) {
newTarget = gnuplot_svg.SVGDoc.getElementById(targetId.concat("_keyentry"));
if (newTarget)
newTarget.setAttributeNS(null, 'style',
newValue == 'hidden' ? 'filter:url(#greybox)' : 'none');
}
evt.preventDefault();
evt.stopPropagation();
}
// Mouse tracking echos coordinates to a floating text box
gnuplot_svg.getText = function() {
return(document.getElementById("coord_text"));
}
gnuplot_svg.updateCoordBox = function(t, evt) {
/*
* Apply screen CTM transformation to the evt screenX and screenY to get
* coordinates in SVG coordinate space. Use scaling parameters stored in
* the plot document by gnuplot to convert further into plot coordinates.
* Then position the floating text box using the SVG coordinates.
*/
var m = document.documentElement.getScreenCTM();
var p = document.documentElement.createSVGPoint();
var loc = cursorPoint(evt);
p.x = loc.x;
p.y = loc.y;
var label_x, label_y;
// Allow for scrollbar position (Firefox, others?)
if (typeof evt.pageX != 'undefined') {
p.x = evt.pageX; p.y = evt.pageY;
}
t.setAttribute("x", p.x);
t.setAttribute("y", p.y);
var plotcoord = gnuplot_svg.mouse2plot(p.x,p.y);
if (gnuplot_svg.plot_timeaxis_x == "DMS" || gnuplot_svg.plot_timeaxis_y == "DMS") {
if (gnuplot_svg.plot_timeaxis_x == "DMS")
label_x = gnuplot_svg.convert_to_DMS(x);
else
label_x = plotcoord.x.toFixed(2);
if (gnuplot_svg.plot_timeaxis_y == "DMS")
label_y = gnuplot_svg.convert_to_DMS(y);
else
label_y = plotcoord.y.toFixed(2);
} else if (gnuplot_svg.polar_mode) {
polar = gnuplot_svg.convert_to_polar(plotcoord.x,plotcoord.y);
label_x = "ang= " + polar.ang.toPrecision(4);
label_y = "R= " + polar.r.toPrecision(4);
} else if (gnuplot_svg.plot_timeaxis_x == "Date") {
gnuplot_svg.axisdate.setTime(1000. * plotcoord.x);
var year = gnuplot_svg.axisdate.getUTCFullYear();
var month = gnuplot_svg.axisdate.getUTCMonth();
var date = gnuplot_svg.axisdate.getUTCDate();
label_x = (" " + date).slice (-2) + "/"
+ ("0" + (month+1)).slice (-2) + "/"
+ year;
label_y = plotcoord.y.toFixed(2);
} else if (gnuplot_svg.plot_timeaxis_x == "Time") {
gnuplot_svg.axisdate.setTime(1000. * plotcoord.x);
var hour = gnuplot_svg.axisdate.getUTCHours();
var minute = gnuplot_svg.axisdate.getUTCMinutes();
var second = gnuplot_svg.axisdate.getUTCSeconds();
label_x = ("0" + hour).slice (-2) + ":"
+ ("0" + minute).slice (-2) + ":"
+ ("0" + second).slice (-2);
label_y = plotcoord.y.toFixed(2);
} else if (gnuplot_svg.plot_timeaxis_x == "DateTime") {
gnuplot_svg.axisdate.setTime(1000. * plotcoord.x);
label_x = gnuplot_svg.axisdate.toUTCString();
label_y = plotcoord.y.toFixed(2);
} else {
label_x = plotcoord.x.toFixed(2);
label_y = plotcoord.y.toFixed(2);
}
while (null != t.firstChild) {
t.removeChild(t.firstChild);
}
var textNode = document.createTextNode(". "+label_x+" "+label_y);
t.appendChild(textNode);
}
gnuplot_svg.showCoordBox = function(evt) {
var t = gnuplot_svg.getText();
if (null != t) {
t.setAttribute("visibility", "visible");
gnuplot_svg.updateCoordBox(t, evt);
}
}
gnuplot_svg.moveCoordBox = function(evt) {
var t = gnuplot_svg.getText();
if (null != t)
gnuplot_svg.updateCoordBox(t, evt);
}
gnuplot_svg.hideCoordBox = function(evt) {
var t = gnuplot_svg.getText();
if (null != t)
t.setAttribute("visibility", "hidden");
}
gnuplot_svg.toggleCoordBox = function(evt) {
var t = gnuplot_svg.getText();
if (null != t) {
var state = t.getAttribute('visibility');
if ('hidden' != state)
state = 'hidden';
else
state = 'visible';
t.setAttribute('visibility', state);
}
}
gnuplot_svg.toggleGrid = function() {
if (!gnuplot_svg.SVGDoc.getElementsByClassName) // Old browsers
return;
var grid = gnuplot_svg.SVGDoc.getElementsByClassName('gridline');
for (var i=0; i<grid.length; i++) {
var state = grid[i].getAttribute('visibility');
grid[i].setAttribute('visibility', (state == 'hidden') ? 'visible' : 'hidden');
}
}
gnuplot_svg.showHypertext = function(evt, mouseovertext)
{
var lines = mouseovertext.split('\n');
// If text starts with "image:" process it as an xlinked bitmap
if (lines[0].substring(0,5) == "image") {
var nameindex = lines[0].indexOf(":");
if (nameindex > 0) {
gnuplot_svg.showHyperimage(evt, lines[0]);
lines[0] = lines[0].slice(nameindex+1);
}
}
var loc = cursorPoint(evt);
var anchor_x = loc.x;
var anchor_y = loc.y;
var hypertextbox = document.getElementById("hypertextbox")
hypertextbox.setAttributeNS(null,"x",anchor_x+10);
hypertextbox.setAttributeNS(null,"y",anchor_y+4);
hypertextbox.setAttributeNS(null,"visibility","visible");
var hypertext = document.getElementById("hypertext")
hypertext.setAttributeNS(null,"x",anchor_x+14);
hypertext.setAttributeNS(null,"y",anchor_y+18);
hypertext.setAttributeNS(null,"visibility","visible");
var height = 2+16*lines.length;
hypertextbox.setAttributeNS(null,"height",height);
var length = hypertext.getComputedTextLength();
hypertextbox.setAttributeNS(null,"width",length+8);
// bounce off frame bottom
if (anchor_y > gnuplot_svg.plot_ybot + 16 - height) {
anchor_y -= height;
hypertextbox.setAttributeNS(null,"y",anchor_y+4);
hypertext.setAttributeNS(null,"y",anchor_y+18);
}
while (null != hypertext.firstChild) {
hypertext.removeChild(hypertext.firstChild);
}
var textNode = document.createTextNode(lines[0]);
if (lines.length <= 1) {
hypertext.appendChild(textNode);
} else {
xmlns="http://www.w3.org/2000/svg";
var tspan_element = document.createElementNS(xmlns, "tspan");
tspan_element.appendChild(textNode);
hypertext.appendChild(tspan_element);
length = tspan_element.getComputedTextLength();
var ll = length;
for (var l=1; l<lines.length; l++) {
var tspan_element = document.createElementNS(xmlns, "tspan");
tspan_element.setAttributeNS(null,"dy", 16);
textNode = document.createTextNode(lines[l]);
tspan_element.appendChild(textNode);
hypertext.appendChild(tspan_element);
ll = tspan_element.getComputedTextLength();
if (length < ll) length = ll;
}
hypertextbox.setAttributeNS(null,"width",length+8);
}
// bounce off right edge
if (anchor_x > gnuplot_svg.plot_xmax + 14 - length) {
anchor_x -= length;
hypertextbox.setAttributeNS(null,"x",anchor_x+10);
hypertext.setAttributeNS(null,"x",anchor_x+14);
}
// left-justify multiline text
var tspan_element = hypertext.firstChild;
while (tspan_element) {
tspan_element.setAttributeNS(null,"x",anchor_x+14);
tspan_element = tspan_element.nextElementSibling;
}
}
gnuplot_svg.hideHypertext = function ()
{
var hypertextbox = document.getElementById("hypertextbox")
var hypertext = document.getElementById("hypertext")
var hyperimage = document.getElementById("hyperimage")
hypertextbox.setAttributeNS(null,"visibility","hidden");
hypertext.setAttributeNS(null,"visibility","hidden");
hyperimage.setAttributeNS(null,"visibility","hidden");
}
gnuplot_svg.showHyperimage = function(evt, linktext)
{
var loc = cursorPoint(evt);
var anchor_x = loc.x;
var anchor_y = loc.y;
// Allow for scrollbar position (Firefox, others?)
if (typeof evt.pageX != 'undefined') {
anchor_x = evt.pageX; anchor_y = evt.pageY;
}
var hyperimage = document.getElementById("hyperimage")
hyperimage.setAttributeNS(null,"x",anchor_x);
hyperimage.setAttributeNS(null,"y",anchor_y);
hyperimage.setAttributeNS(null,"visibility","visible");
// Pick up height and width from "image(width,height):name"
var width = hyperimage.getAttributeNS(null,"width");
var height = hyperimage.getAttributeNS(null,"height");
if (linktext.charAt(5) == "(") {
width = parseInt(linktext.slice(6));
height = parseInt(linktext.slice(linktext.indexOf(",") + 1));
hyperimage.setAttributeNS(null,"width",width);
hyperimage.setAttributeNS(null,"height",height);
hyperimage.setAttributeNS(null,"preserveAspectRatio","none");
}
// bounce off frame bottom and right
if (anchor_y > gnuplot_svg.plot_ybot + 50 - height)
hyperimage.setAttributeNS(null,"y",20 + anchor_y-height);
if (anchor_x > gnuplot_svg.plot_xmax + 150 - width)
hyperimage.setAttributeNS(null,"x",10 + anchor_x-width);
// attach image URL as a link
linktext = linktext.slice(linktext.indexOf(":") + 1);
var xlinkns = "http://www.w3.org/1999/xlink";
hyperimage.setAttributeNS(xlinkns,"xlink:href",linktext);
}
// Convert from svg panel mouse coordinates to the coordinate
// system of the gnuplot figure
gnuplot_svg.mouse2plot = function(mousex,mousey) {
var plotcoord = new Object;
var plotx = mousex - gnuplot_svg.plot_xmin;
var ploty = mousey - gnuplot_svg.plot_ybot;
var x,y;
if (gnuplot_svg.plot_logaxis_x != 0) {
x = Math.log(gnuplot_svg.plot_axis_xmax)
- Math.log(gnuplot_svg.plot_axis_xmin);
x = x * (plotx / (gnuplot_svg.plot_xmax - gnuplot_svg.plot_xmin))
+ Math.log(gnuplot_svg.plot_axis_xmin);
x = Math.exp(x);
} else {
x = gnuplot_svg.plot_axis_xmin + (plotx / (gnuplot_svg.plot_xmax-gnuplot_svg.plot_xmin)) * (gnuplot_svg.plot_axis_xmax - gnuplot_svg.plot_axis_xmin);
}
if (gnuplot_svg.plot_logaxis_y != 0) {
y = Math.log(gnuplot_svg.plot_axis_ymax)
- Math.log(gnuplot_svg.plot_axis_ymin);
y = y * (ploty / (gnuplot_svg.plot_ytop - gnuplot_svg.plot_ybot))
+ Math.log(gnuplot_svg.plot_axis_ymin);
y = Math.exp(y);
} else {
y = gnuplot_svg.plot_axis_ymin + (ploty / (gnuplot_svg.plot_ytop-gnuplot_svg.plot_ybot)) * (gnuplot_svg.plot_axis_ymax - gnuplot_svg.plot_axis_ymin);
}
plotcoord.x = x;
plotcoord.y = y;
return plotcoord;
}
gnuplot_svg.convert_to_polar = function (x,y)
{
polar = new Object;
var phi, r;
phi = Math.atan2(y,x);
if (gnuplot_svg.plot_logaxis_r)
r = Math.exp( (x/Math.cos(phi) + Math.log(gnuplot_svg.plot_axis_rmin)/Math.LN10) * Math.LN10);
else if (gnuplot_svg.plot_axis_rmin > gnuplot_svg.plot_axis_rmax)
r = gnuplot_svg.plot_axis_rmin - x/Math.cos(phi);
else
r = gnuplot_svg.plot_axis_rmin + x/Math.cos(phi);
phi = phi * (180./Math.PI);
if (gnuplot_svg.polar_sense < 0)
phi = -phi;
if (gnuplot_svg.polar_theta0 != undefined)
phi = phi + gnuplot_svg.polar_theta0;
if (phi > 180.)
phi = phi - 360.;
polar.r = r;
polar.ang = phi;
return polar;
}
gnuplot_svg.convert_to_DMS = function (x)
{
var dms = {d:0, m:0, s:0};
var deg = Math.abs(x);
dms.d = Math.floor(deg);
dms.m = Math.floor((deg - dms.d) * 60.);
dms.s = Math.floor((deg - dms.d) * 3600. - dms.m * 60.);
fmt = ((x<0)?"-":" ")
+ dms.d.toFixed(0) + "°"
+ dms.m.toFixed(0) + "\""
+ dms.s.toFixed(0) + "'";
return fmt;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

48
src/netdaemon/www/plot Executable file
View File

@ -0,0 +1,48 @@
#!/bin/bash
OUT=tmpfile.txt
cat $1 > $OUT
VAL=$(head -n1 $1 | awk '{print $3}')
NM=$(basename $1)
if [ $NM = "T0" ]; then Tname="TOP side"
else Tname="BOTTOM side"
fi
echo -e "30\t30\t$VAL\n-30\t-30\t$VAL" >> $OUT
cat << EOF > gnutplt
#!/usr/bin/gnuplot
set contour
unset surface
set cntrparam order 4
set cntrparam bspline
#set cntrparam levels auto 6
#set cntrparam levels incremental -30,0.1,30
set view map
set size square
set xrange [-40:40]
set yrange [-40:40]
set dgrid3d 100,100,4
set table "contour.txt"
splot '$OUT' u 1:2:3
unset table
unset contour
set surface
set table "dgrid.txt"
splot '$OUT' u 1:2:3
unset table
reset
set terminal canvas enhanced mousing size 1024,768 jsdir 'js'
set output "$1.html"
set size square
set xrange [-30:30]
set yrange [-30:30]
set xlabel "X, dm"
set ylabel "Y, dm"
set title "Mirror temperature $NM for $2 ($Tname)"
set pm3d map
unset key
circle(x,y,z) = x**2+y**2 > 900 ? NaN : z
splot 'dgrid.txt' u 1:2:(circle(\$1,\$2,\$3)) w pm3d, 'contour.txt' u 1:2:(circle(\$1,\$2,\$3)) w l lc rgb "black", '$OUT' u 1:2:(circle(\$1,\$2,\$3)):3 with labels font ",8"
EOF
chmod 755 gnutplt
./gnutplt
rm gnutplt dgrid.txt contour.txt tmpfile.txt 2>&1 > /dev/null