1 /**********
2 Copyright 1990 Regents of the University of California. All rights reserved.
3 **********/
4
5 /*
6 Manage graph data structure.
7 */
8
9 #include "ngspice/ngspice.h"
10 #include "ngspice/graph.h"
11 #include "ngspice/ftedebug.h"
12 #include "ngspice/fteext.h"
13
14 #include "ngspice/ftedbgra.h"
15 #include "graphdb.h"
16 #include "../breakp2.h"
17 #include "../display.h"
18
19
20 /* invariant: currentgraph contains the current graph */
21 GRAPH *currentgraph;
22
23 /*
24 * We use a linked list rather than a circular one because we
25 * expect few links per list and we don't need to add at the
26 * end of a list (we can add at the beginning).
27 */
28
29 /* linked list of graphs */
30 typedef struct listgraph {
31 /* we use GRAPH here instead of a pointer to save a tmalloc */
32 GRAPH graph;
33 struct listgraph *next;
34 } LISTGRAPH;
35
36 #define NUMGBUCKETS 16
37
38 typedef struct gbucket {
39 LISTGRAPH *list;
40 } GBUCKET;
41
42 static GBUCKET GBucket[NUMGBUCKETS];
43
44 /* note: Zero is not a valid id. This is used in plot() in graf.c. */
45 static int RunningId = 1;
46
47 /* Initialize graph structure */
setgraph(GRAPH * pgraph,int id)48 static inline void setgraph(GRAPH *pgraph, int id)
49 {
50 pgraph->graphid = id;
51 pgraph->degree = 1;
52 pgraph->linestyle = -1;
53 } /* end of function setgraph */
54
55
56
57 /* Creates a new graph. Returns NULL on error */
NewGraph(void)58 GRAPH *NewGraph(void)
59 {
60 LISTGRAPH *list;
61 const int BucketId = RunningId % NUMGBUCKETS;
62
63 /* allocate memory for graph via LISTGRAPH */
64 if ((list = TMALLOC(LISTGRAPH, 1)) == NULL) {
65 internalerror("can't allocate a listgraph");
66 return (GRAPH *) NULL;
67 }
68
69 GRAPH * const pgraph = &list->graph;
70 setgraph(pgraph, RunningId);
71 GBUCKET *p_bucket = GBucket + BucketId;
72
73 /* Add to the appropriate bucket at the front of the linked list */
74 if (!p_bucket->list) { /* no list yet */
75 p_bucket->list = list;
76 }
77 else {
78 /* insert at front of current list */
79 list->next = p_bucket->list;
80 p_bucket->list = list;
81 }
82
83 RunningId++;
84
85 return pgraph;
86 } /* end of function NewGraph */
87
88
89
90 /* Given graph id, return graph */
FindGraph(int id)91 GRAPH *FindGraph(int id)
92 {
93 LISTGRAPH *list;
94
95 /* Step through list of graphs until found or list ends */
96 for (list = GBucket[id % NUMGBUCKETS].list;
97 list && list->graph.graphid != id;
98 list = list->next) {
99 ;
100 }
101
102 if (list) { /* found */
103 return &list->graph;
104 }
105 else {
106 return (GRAPH *) NULL;
107 }
108 } /* end of function FindGraph */
109
110
111
CopyGraph(GRAPH * graph)112 GRAPH *CopyGraph(GRAPH *graph)
113 {
114 GRAPH *ret;
115 struct dveclist *link = NULL, *newlink = NULL;
116
117 if (!graph) {
118 return NULL;
119 }
120
121 ret = NewGraph();
122
123 {
124 const int id = ret->graphid; /* save ID of the new graph */
125 memcpy(ret, graph, sizeof(GRAPH)); /* copy graph info (inc. ID) */
126 ret->graphid = id; /* restore ID */
127 }
128
129 /* copy keyed */
130 {
131 struct _keyed *k;
132 for (ret->keyed = NULL, k = graph->keyed; k; k = k->next) {
133 SaveText(ret, k->text, k->x, k->y);
134 }
135 }
136
137 /* copy dvecs or reuse if "borrowed" already */
138 {
139 struct dveclist *new_plotdata = (struct dveclist *) NULL;
140 for (link = graph->plotdata; link; link = link->next) {
141 if (link->f_own_vector) {
142 struct dvec * const old_vector = link->vector;
143 struct dvec * const new_vector = vec_copy(old_vector);
144 /* vec_copy doesn't set v_color or v_linestyle */
145 new_vector->v_color = old_vector->v_color;
146 new_vector->v_linestyle = old_vector->v_linestyle;
147 new_vector->v_flags |= VF_PERMANENT;
148 newlink = TMALLOC(struct dveclist, 1);
149 newlink->next = new_plotdata;
150 newlink->f_own_vector = TRUE;
151 newlink->vector = new_vector;
152
153 /* If the link owns the vector, it also owns its scale
154 * vector, if present */
155 struct dvec *old_scale = old_vector->v_scale;
156 if (old_scale != (struct dvec *) NULL) {
157 new_plotdata = newlink; /* put in front */
158 struct dvec * const new_scale = vec_copy(old_scale);
159 new_scale->v_flags |= VF_PERMANENT;
160 newlink->vector->v_scale = new_scale;
161 }
162 }
163 else {
164 newlink->vector = link->vector;
165 newlink->f_own_vector = FALSE;
166 }
167 new_plotdata = newlink; /* put in front */
168 }
169
170 ret->plotdata = new_plotdata; /* give vector list to plot */
171 } /* end of block copying or reusing dvecs */
172
173 ret->commandline = copy(graph->commandline);
174 ret->plotname = copy(graph->plotname);
175
176 {
177 const char * const lbl = graph->grid.xlabel;
178 if (lbl) {
179 ret->grid.xlabel = copy(lbl);
180 }
181 }
182
183 {
184 const char * const lbl = graph->grid.ylabel;
185 if (lbl) {
186 ret->grid.ylabel = copy(lbl);
187 }
188 }
189
190 /* Copy devdep information and size if present */
191 {
192 const void * const p = graph->devdep;
193 if (p != NULL) {
194 const size_t n = ret->n_byte_devdep = graph->n_byte_devdep;
195 void * const dst = ret->devdep = tmalloc(n);
196 (void) memcpy(dst, graph->devdep, n);
197 }
198 }
199
200 return ret;
201 } /* end of function CopyGraph */
202
203
204
DestroyGraph(int id)205 int DestroyGraph(int id)
206 {
207 /* Locate hash bucket for this graph */
208 const int index = id % NUMGBUCKETS;
209 LISTGRAPH *list = GBucket[index].list;
210
211 /* Pointer before current one. Allows fixing list when the current
212 * node is deleted. Init to NULL to indicate that at head of list */
213 LISTGRAPH *lastlist = (LISTGRAPH *) NULL;
214
215 /* Step through graphs in the bucket until the one with id is found */
216 while (list) {
217 if (list->graph.graphid == id) { /* found it */
218 struct _keyed *k, *nextk;
219 struct dbcomm *db;
220
221 /* Fix the iplot/trace dbs list */
222 for (db = dbs; db && db->db_graphid != id; db = db->db_next) {
223 ;
224 }
225
226 if (db && (db->db_type == DB_IPLOT ||
227 db->db_type == DB_IPLOTALL)) {
228 db->db_type = DB_DEADIPLOT;
229 /* Delete this later */
230 return 0;
231 }
232
233 /* Adjust bucket pointers to remove the current node */
234 if (lastlist) { /* not at front */
235 lastlist->next = list->next;
236 }
237 else { /* at front */
238 GBucket[index].list = list->next;
239 }
240
241 /* Run through and de-allocate dynamically allocated keyed list */
242 k = list->graph.keyed;
243 while (k) {
244 nextk = k->next;
245 txfree(k->text);
246 txfree(k);
247 k = nextk;
248 }
249
250 /* Free vectors owned by this graph and free the list */
251 {
252 struct dveclist *d = list->graph.plotdata;
253 struct dveclist *nextd;
254 while (d != (struct dveclist *) NULL) {
255 nextd = d->next;
256 if (d->f_own_vector) {
257 /* list responsible for freeing this vector */
258 if (d->vector->v_scale) {
259 dvec_free(d->vector->v_scale);
260 }
261 dvec_free(d->vector);
262 }
263 txfree(d);
264 d = nextd;
265 }
266 }
267
268 txfree(list->graph.commandline);
269 txfree(list->graph.plotname);
270 txfree(list->graph.grid.xlabel);
271 txfree(list->graph.grid.ylabel);
272
273 /* If device-dependent space was allocated, free it. */
274 {
275 void * const p = list->graph.devdep;
276 if (p) {
277 txfree(p);
278 }
279 }
280
281 txfree(list);
282
283 return 1;
284 } /* end of case that graph ID was found */
285
286 lastlist = list; /* update previous node */
287 list = list->next; /* step to next node */
288 } /* end of loop over graphs in the current bucket */
289
290 /* The graph with ID id was not found */
291 internalerror("tried to destroy non-existent graph");
292 return 0;
293 } /* end of function DestroyGraph */
294
295
296
297 /* Free up all dynamically allocated data structures */
FreeGraphs(void)298 void FreeGraphs(void)
299 {
300 /* Iterate over all hash buckets */
301 GBUCKET *gbucket;
302 for (gbucket = GBucket; gbucket < &GBucket[NUMGBUCKETS]; gbucket++) {
303 LISTGRAPH * list = gbucket->list; /* linked list of graphs here */
304 while (list) { /* Free each until end of list */
305 LISTGRAPH *deadl = list;
306 list = list->next;
307 txfree(deadl);
308 }
309 }
310 } /* end of functdion FreeGraphs */
311
312
313
314 /* This function sets global varial currentgraph based on graphid */
SetGraphContext(int graphid)315 void SetGraphContext(int graphid)
316 {
317 currentgraph = FindGraph(graphid);
318 } /* end of function SetGraphContext */
319
320
321
322 /* Stack of graph objects implemented as a linked list */
323 typedef struct gcstack {
324 GRAPH *pgraph;
325 struct gcstack *next;
326 } GCSTACK;
327
328 static GCSTACK *gcstacktop; /* top of the stack of graphs */
329
330
331 /* note: This Push and Pop has tricky semantics.
332 Push(graph) will push the currentgraph onto the stack
333 and set currentgraph to graph.
334 Pop() simply sets currentgraph to previous value at the top of the stack
335 and pops stack.
336 */
PushGraphContext(GRAPH * graph)337 void PushGraphContext(GRAPH *graph)
338 {
339 GCSTACK *gcstack = TMALLOC(GCSTACK, 1);
340
341 if (!gcstacktop) {
342 gcstacktop = gcstack;
343 }
344 else {
345 gcstack->next = gcstacktop;
346 gcstacktop = gcstack;
347 }
348 gcstacktop->pgraph = currentgraph;
349 currentgraph = graph;
350 } /* end of function PushGraphContext */
351
352
353
PopGraphContext(void)354 void PopGraphContext(void)
355 {
356 currentgraph = gcstacktop->pgraph; /* pop from stack, making current */
357 GCSTACK *dead = gcstacktop; /* remove from stack */
358 gcstacktop = gcstacktop->next;
359 txfree(dead); /* free allocation */
360 } /* end of function PopGraphContext */
361
362
363
364