Copyright 1990 Regents of the University of California. All rights reserved.
**********/
Manage graph data structure.
*/
#include "ngspice/ngspice.h"
#include "ngspice/graph.h"
#include "ngspice/ftedebug.h"
#include "ngspice/fteext.h"
#include "ngspice/ftedbgra.h"
#include "graphdb.h"
#include "../breakp2.h"
#include "../display.h"
GRAPH *currentgraph;
* We use a linked list rather than a circular one because we
* expect few links per list and we don't need to add at the
* end of a list (we can add at the beginning).
*/
typedef struct listgraph {
GRAPH graph;
struct listgraph *next;
} LISTGRAPH;
#define NUMGBUCKETS 16
typedef struct gbucket {
LISTGRAPH *list;
} GBUCKET;
static GBUCKET GBucket[NUMGBUCKETS];
static int RunningId = 1;
static inline void setgraph(GRAPH *pgraph, int id)
{
pgraph->graphid = id;
pgraph->degree = 1;
pgraph->linestyle = -1;
}
GRAPH *NewGraph(void)
{
LISTGRAPH *list;
const int BucketId = RunningId % NUMGBUCKETS;
if ((list = TMALLOC(LISTGRAPH, 1)) == NULL) {
internalerror("can't allocate a listgraph");
return (GRAPH *) NULL;
}
GRAPH * const pgraph = &list->graph;
setgraph(pgraph, RunningId);
GBUCKET *p_bucket = GBucket + BucketId;
if (!p_bucket->list) {
p_bucket->list = list;
}
else {
list->next = p_bucket->list;
p_bucket->list = list;
}
RunningId++;
return pgraph;
}
GRAPH *FindGraph(int id)
{
LISTGRAPH *list;
for (list = GBucket[id % NUMGBUCKETS].list;
list && list->graph.graphid != id;
list = list->next) {
;
}
if (list) {
return &list->graph;
}
else {
return (GRAPH *) NULL;
}
}
GRAPH *CopyGraph(GRAPH *graph)
{
GRAPH *ret;
struct dveclist *link = NULL, *newlink = NULL;
if (!graph) {
return NULL;
}
ret = NewGraph();
{
const int id = ret->graphid;
memcpy(ret, graph, sizeof(GRAPH));
ret->graphid = id;
}
{
struct _keyed *k;
for (ret->keyed = NULL, k = graph->keyed; k; k = k->next) {
SaveText(ret, k->text, k->x, k->y);
}
}
{
struct dveclist *new_plotdata = (struct dveclist *) NULL;
for (link = graph->plotdata; link; link = link->next) {
if (link->f_own_vector) {
struct dvec * const old_vector = link->vector;
struct dvec * const new_vector = vec_copy(old_vector);
new_vector->v_color = old_vector->v_color;
new_vector->v_linestyle = old_vector->v_linestyle;
new_vector->v_flags |= VF_PERMANENT;
newlink = TMALLOC(struct dveclist, 1);
newlink->next = new_plotdata;
newlink->f_own_vector = TRUE;
newlink->vector = new_vector;
* vector, if present */
struct dvec *old_scale = old_vector->v_scale;
if (old_scale != (struct dvec *) NULL) {
new_plotdata = newlink;
struct dvec * const new_scale = vec_copy(old_scale);
new_scale->v_flags |= VF_PERMANENT;
newlink->vector->v_scale = new_scale;
}
}
else {
newlink->vector = link->vector;
newlink->f_own_vector = FALSE;
}
new_plotdata = newlink;
}
ret->plotdata = new_plotdata;
}
ret->commandline = copy(graph->commandline);
ret->plotname = copy(graph->plotname);
{
const char * const lbl = graph->grid.xlabel;
if (lbl) {
ret->grid.xlabel = copy(lbl);
}
}
{
const char * const lbl = graph->grid.ylabel;
if (lbl) {
ret->grid.ylabel = copy(lbl);
}
}
{
const void * const p = graph->devdep;
if (p != NULL) {
const size_t n = ret->n_byte_devdep = graph->n_byte_devdep;
void * const dst = ret->devdep = tmalloc(n);
(void) memcpy(dst, graph->devdep, n);
}
}
return ret;
}
int DestroyGraph(int id)
{
const int index = id % NUMGBUCKETS;
LISTGRAPH *list = GBucket[index].list;
* node is deleted. Init to NULL to indicate that at head of list */
LISTGRAPH *lastlist = (LISTGRAPH *) NULL;
while (list) {
if (list->graph.graphid == id) {
struct _keyed *k, *nextk;
struct dbcomm *db;
for (db = dbs; db && db->db_graphid != id; db = db->db_next) {
;
}
if (db && (db->db_type == DB_IPLOT ||
db->db_type == DB_IPLOTALL)) {
db->db_type = DB_DEADIPLOT;
return 0;
}
if (lastlist) {
lastlist->next = list->next;
}
else {
GBucket[index].list = list->next;
}
k = list->graph.keyed;
while (k) {
nextk = k->next;
txfree(k->text);
txfree(k);
k = nextk;
}
{
struct dveclist *d = list->graph.plotdata;
struct dveclist *nextd;
while (d != (struct dveclist *) NULL) {
nextd = d->next;
if (d->f_own_vector) {
if (d->vector->v_scale) {
dvec_free(d->vector->v_scale);
}
dvec_free(d->vector);
}
txfree(d);
d = nextd;
}
}
txfree(list->graph.commandline);
txfree(list->graph.plotname);
txfree(list->graph.grid.xlabel);
txfree(list->graph.grid.ylabel);
{
void * const p = list->graph.devdep;
if (p) {
txfree(p);
}
}
txfree(list);
return 1;
}
lastlist = list;
list = list->next;
}
internalerror("tried to destroy non-existent graph");
return 0;
}
void FreeGraphs(void)
{
GBUCKET *gbucket;
for (gbucket = GBucket; gbucket < &GBucket[NUMGBUCKETS]; gbucket++) {
LISTGRAPH * list = gbucket->list;
while (list) {
LISTGRAPH *deadl = list;
list = list->next;
txfree(deadl);
}
}
}
void SetGraphContext(int graphid)
{
currentgraph = FindGraph(graphid);
}
typedef struct gcstack {
GRAPH *pgraph;
struct gcstack *next;
} GCSTACK;
static GCSTACK *gcstacktop;
Push(graph) will push the currentgraph onto the stack
and set currentgraph to graph.
Pop() simply sets currentgraph to previous value at the top of the stack
and pops stack.
*/
void PushGraphContext(GRAPH *graph)
{
GCSTACK *gcstack = TMALLOC(GCSTACK, 1);
if (!gcstacktop) {
gcstacktop = gcstack;
}
else {
gcstack->next = gcstacktop;
gcstacktop = gcstack;
}
gcstacktop->pgraph = currentgraph;
currentgraph = graph;
}
void PopGraphContext(void)
{
currentgraph = gcstacktop->pgraph;
GCSTACK *dead = gcstacktop;
gcstacktop = gcstacktop->next;
txfree(dead);
}