1 /*
2 * Copyright (c) 2004-2008 Hypertriton, Inc. <http://hypertriton.com/>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23 * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 /*
27 * Base vector graphics object.
28 */
29
30 #include <agar/core/core.h>
31 #include <agar/gui/gui.h>
32 #include <agar/gui/primitive.h>
33 #include <agar/gui/iconmgr.h>
34 #include <agar/vg/vg.h>
35 #include <agar/vg/vg_view.h>
36 #include <agar/vg/icons.h>
37 #include <agar/vg/icons_data.h>
38
39 #include <string.h>
40
41 const AG_Version vgVer = { 6, 1 };
42
43 int vgInitedSubsystem = 0;
44 VG_NodeOps **vgNodeClasses;
45 Uint vgNodeClassCount;
46 int vgGUI = 1;
47
48 extern VG_NodeOps vgPointOps;
49 extern VG_NodeOps vgLineOps;
50 extern VG_NodeOps vgPolygonOps;
51 extern VG_NodeOps vgCircleOps;
52 extern VG_NodeOps vgArcOps;
53 extern VG_NodeOps vgTextOps;
54
55 VG_NodeOps *vgBuiltinClasses[] = {
56 &vgPointOps,
57 &vgLineOps,
58 &vgPolygonOps,
59 &vgCircleOps,
60 &vgArcOps,
61 &vgTextOps,
62 NULL
63 };
64
65 void
VG_InitSubsystem(void)66 VG_InitSubsystem(void)
67 {
68 VG_NodeOps **vnOps;
69
70 if (vgInitedSubsystem)
71 return;
72
73 vgNodeClasses = NULL;
74 vgNodeClassCount = 0;
75
76 AG_RegisterNamespace("VG", "VG_", "http://libagar.org/");
77 if (vgGUI) {
78 AG_RegisterClass(&vgViewClass);
79 }
80 for (vnOps = &vgBuiltinClasses[0]; *vnOps != NULL; vnOps++)
81 VG_RegisterClass(*vnOps);
82
83 vgIcon_Init();
84 vgInitedSubsystem = 1;
85 }
86
87 void
VG_DestroySubsystem(void)88 VG_DestroySubsystem(void)
89 {
90 if (!vgInitedSubsystem)
91 return;
92
93 if (vgGUI) {
94 AG_UnregisterClass(&vgViewClass);
95 }
96 Free(vgNodeClasses);
97 vgNodeClasses = NULL;
98 vgNodeClassCount = 0;
99
100 AG_UnregisterNamespace("VG");
101
102 vgInitedSubsystem = 0;
103 }
104
105 VG *
VG_New(Uint flags)106 VG_New(Uint flags)
107 {
108 VG *vg;
109
110 vg = Malloc(sizeof(VG));
111 VG_Init(vg, flags);
112 return (vg);
113 }
114
115 void
VG_Init(VG * vg,Uint flags)116 VG_Init(VG *vg, Uint flags)
117 {
118 VG_Point *ptRoot;
119
120 vg->flags = flags;
121 vg->colors = NULL;
122 vg->nColors = 0;
123 vg->fillColor = VG_GetColorRGB(0,0,0);
124 vg->selectionColor = VG_GetColorRGBA(0,200,0,150);
125 vg->mouseoverColor = VG_GetColorRGBA(250,250,0,100);
126 vg->layers = NULL;
127 vg->nLayers = 0;
128 TAILQ_INIT(&vg->nodes);
129 AG_MutexInitRecursive(&vg->lock);
130
131 vg->T = Malloc(sizeof(VG_Matrix));
132 vg->nT = 1;
133 vg->T[0] = VG_MatrixIdentity();
134
135 VG_PushLayer(vg, _("Layer 0"));
136
137 ptRoot = VG_PointNew(NULL, VGVECTOR(0.0f,0.0f));
138 vg->root = VGNODE(ptRoot);
139 vg->root->vg = vg;
140 vg->root->handle = 1;
141 VG_SetColorRGB(vg->root, 0, 150, 0);
142 }
143
144 /* Delete and free a node (including its children). */
145 void
VG_NodeDestroy(void * p)146 VG_NodeDestroy(void *p)
147 {
148 VG_Node *vn = p;
149 VG_Node *vnChld, *vnNext;
150
151 #ifdef AG_DEBUG
152 if (vn->vg != NULL || vn->parent != NULL)
153 AG_FatalError("VG_NodeDetach() must precede VG_NodeDestroy()");
154 #endif
155
156 for (vnChld = TAILQ_FIRST(&vn->cNodes);
157 vnChld != TAILQ_END(&vn->cNodes);
158 vnChld = vnNext) {
159 vnNext = TAILQ_NEXT(vnChld, tree);
160 TAILQ_REMOVE(&vnChld->vg->nodes, vnChld, list);
161 VG_NodeDestroy(vnChld);
162 }
163 TAILQ_INIT(&vn->cNodes);
164
165 if (vn->ops->destroy != NULL) {
166 vn->ops->destroy(vn);
167 }
168 Free(vn);
169 }
170
171 /* Reinitialize the drawing. */
172 void
VG_Clear(VG * vg)173 VG_Clear(VG *vg)
174 {
175 VG_ClearNodes(vg);
176 VG_ClearColors(vg);
177 }
178
179 /* Reinitialize the tree of entities. */
180 void
VG_ClearNodes(VG * vg)181 VG_ClearNodes(VG *vg)
182 {
183 VG_Node *vnChld, *vnNext;
184
185 if (vg->root == NULL) {
186 return;
187 }
188 for (vnChld = TAILQ_FIRST(&vg->root->cNodes);
189 vnChld != TAILQ_END(&vg->root->cNodes);
190 vnChld = vnNext) {
191 vnNext = TAILQ_NEXT(vnChld, tree);
192 VG_NodeDetach(vnChld);
193 VG_NodeDestroy(vnChld);
194 }
195 TAILQ_INIT(&vg->root->cNodes);
196 TAILQ_INIT(&vg->nodes);
197 }
198
199 /* Reinitialize the color array. */
200 void
VG_ClearColors(VG * vg)201 VG_ClearColors(VG *vg)
202 {
203 if (vg->colors != NULL) {
204 Free(vg->colors);
205 vg->colors = NULL;
206 }
207 vg->nColors = 0;
208 }
209
210 void
VG_Destroy(VG * vg)211 VG_Destroy(VG *vg)
212 {
213 VG_Clear(vg);
214 Free(vg->layers);
215 AG_MutexDestroy(&vg->lock);
216 }
217
218 void
VG_RegisterClass(VG_NodeOps * vnOps)219 VG_RegisterClass(VG_NodeOps *vnOps)
220 {
221 vgNodeClasses = Realloc(vgNodeClasses,
222 (vgNodeClassCount+1)*sizeof(VG_NodeOps *));
223 vgNodeClasses[vgNodeClassCount++] = vnOps;
224 }
225
226 void
VG_UnregisterClass(VG_NodeOps * vnOps)227 VG_UnregisterClass(VG_NodeOps *vnOps)
228 {
229 int i;
230
231 for (i = 0; i < vgNodeClassCount; i++) {
232 if (vgNodeClasses[i] == vnOps)
233 break;
234 }
235 if (i == vgNodeClassCount) {
236 return;
237 }
238 if (i < vgNodeClassCount-1) {
239 memmove(&vgNodeClasses[i], &vgNodeClasses[i+1],
240 (vgNodeClassCount-i-1)*sizeof(VG_NodeOps *));
241 }
242 vgNodeClassCount--;
243 }
244
245
246 /* Lookup a node class by name. */
247 VG_NodeOps *
VG_LookupClass(const char * name)248 VG_LookupClass(const char *name)
249 {
250 Uint i;
251
252 for (i = 0; i < vgNodeClassCount; i++) {
253 VG_NodeOps *vnOps = vgNodeClasses[i];
254 if (strcmp(vnOps->name, name) == 0)
255 return (vnOps);
256 }
257 AG_SetError("Invalid node type: %s", name);
258 return (NULL);
259 }
260
261 /* Detach and free the specified node and its children. */
262 int
VG_Delete(void * pVn)263 VG_Delete(void *pVn)
264 {
265 VG_Node *vn = pVn;
266 VG *vg = vn->vg;
267
268 #ifdef AG_DEBUG
269 if (vg == NULL)
270 AG_FatalError("VG_Delete() on unattached node %s%d",
271 vn->ops->name, vn->handle);
272 #endif
273 VG_Lock(vg);
274 if (vn->nDeps > 0) {
275 AG_SetError("%s%u is in use", vn->ops->name, (Uint)vn->handle);
276 goto fail;
277 }
278 if (vn->ops->deleteNode != NULL) {
279 vn->ops->deleteNode(vn);
280 }
281 VG_NodeDetach(vn);
282 VG_NodeDestroy(vn);
283 VG_Unlock(vg);
284 return (0);
285 fail:
286 VG_Unlock(vg);
287 return (-1);
288 }
289
290 static void
MoveNodesRecursively(VG * vgDst,VG_Node * vn)291 MoveNodesRecursively(VG *vgDst, VG_Node *vn)
292 {
293 VG_Node *vnChld;
294
295 VG_FOREACH_CHLD(vnChld, vn, vg_node) {
296 MoveNodesRecursively(vgDst, vnChld);
297 }
298 vn->handle = VG_GenNodeName(vgDst, vn->ops->name);
299 TAILQ_REMOVE(&vn->vg->nodes, vn, list);
300 vn->vg = vgDst;
301 TAILQ_INSERT_TAIL(&vgDst->nodes, vn, list);
302 }
303
304 /*
305 * Move the contents of a source VG (to be discarded) to the specified
306 * destination VG, under a given node.
307 */
308 void
VG_Merge(void * pVnDst,VG * vgSrc)309 VG_Merge(void *pVnDst, VG *vgSrc)
310 {
311 VG_Node *vnDst = pVnDst;
312 VG_Node *vn = vgSrc->root;
313
314 vn->vg = vnDst->vg;
315 vn->parent = vnDst;
316 TAILQ_INSERT_TAIL(&vnDst->vg->nodes, vn, list);
317 TAILQ_INSERT_TAIL(&vnDst->cNodes, vn, tree);
318 MoveNodesRecursively(vnDst->vg, vn);
319 vgSrc->root = NULL;
320 }
321
322 /* Create a node reference to another node. */
323 void
VG_AddRef(void * p,void * pRef)324 VG_AddRef(void *p, void *pRef)
325 {
326 VG_Node *vn = p;
327
328 if (vn->vg != NULL) { VG_Lock(vn->vg); }
329 vn->refs = Realloc(vn->refs, (vn->nRefs+1)*sizeof(VG_Node *));
330 vn->refs[vn->nRefs++] = VGNODE(pRef);
331 VGNODE(pRef)->nDeps++;
332 if (vn->vg != NULL) { VG_Unlock(vn->vg); }
333 }
334
335 /* Remove a node reference to another node. */
336 Uint
VG_DelRef(void * pVn,void * pRef)337 VG_DelRef(void *pVn, void *pRef)
338 {
339 VG_Node *vn = pVn;
340 Uint newDeps;
341 int i;
342
343 if (vn->vg != NULL) { VG_Lock(vn->vg); }
344 for (i = 0; i < vn->nRefs; i++) {
345 if (vn->refs[i] == VGNODE(pRef))
346 break;
347 }
348 if (i == vn->nRefs) {
349 AG_FatalError("No such reference");
350 }
351 if (i < vn->nRefs-1) {
352 memmove(&vn->refs[i], &vn->refs[i+1],
353 (vn->nRefs-i-1)*sizeof(VG_Node *));
354 }
355 vn->nRefs--;
356 newDeps = (--VGNODE(pRef)->nDeps);
357 if (vn->vg != NULL) { VG_Unlock(vn->vg); }
358 return (newDeps);
359 }
360
VG_SetBackgroundColor(VG * vg,VG_Color c)361 void VG_SetBackgroundColor(VG *vg, VG_Color c) { vg->fillColor = c; }
VG_SetSelectionColor(VG * vg,VG_Color c)362 void VG_SetSelectionColor(VG *vg, VG_Color c) { vg->selectionColor = c; }
VG_SetMouseOverColor(VG * vg,VG_Color c)363 void VG_SetMouseOverColor(VG *vg, VG_Color c) { vg->mouseoverColor = c; }
364
365 void
VG_NodeInit(void * p,VG_NodeOps * vnOps)366 VG_NodeInit(void *p, VG_NodeOps *vnOps)
367 {
368 VG_Node *vn = p;
369
370 vn->ops = vnOps;
371 vn->handle = 0;
372 vn->sym[0] = '\0';
373 vn->flags = 0;
374 vn->layer = 0;
375 vn->color = VG_GetColorRGB(250,250,250);
376 vn->parent = NULL;
377 vn->vg = NULL;
378 vn->refs = NULL;
379 vn->nRefs = 0;
380 vn->nDeps = 0;
381 vn->T = VG_MatrixIdentity();
382 vn->p = NULL;
383 TAILQ_INIT(&vn->cNodes);
384
385 if (vn->ops->init != NULL)
386 vn->ops->init(vn);
387 }
388
389 /* Generate a unique name for a node of the specified type. */
390 Uint32
VG_GenNodeName(VG * vg,const char * type)391 VG_GenNodeName(VG *vg, const char *type)
392 {
393 Uint32 name = 1;
394
395 while (VG_FindNode(vg, name, type) != NULL) {
396 if (++name >= VG_HANDLE_MAX)
397 AG_FatalError("Out of node names");
398 }
399 return (name);
400 }
401
402 /*
403 * Attach the specified node to a new parent. If the node has no
404 * name assigned (handle=0), one is generated.
405 */
406 void
VG_NodeAttach(void * pParent,void * pNode)407 VG_NodeAttach(void *pParent, void *pNode)
408 {
409 VG_Node *vnParent = pParent;
410 VG_Node *vn = pNode;
411 VG *vg;
412
413 if (vnParent == NULL) {
414 vn->parent = NULL;
415 vn->vg = NULL;
416 return;
417 }
418 vg = vnParent->vg;
419
420 VG_Lock(vg);
421
422 if (vn->handle == 0) {
423 vn->handle = VG_GenNodeName(vg, vn->ops->name);
424 }
425 vn->parent = vnParent;
426 TAILQ_INSERT_TAIL(&vnParent->cNodes, vn, tree);
427 TAILQ_INSERT_TAIL(&vg->nodes, vn, list);
428 vn->vg = vg;
429 VG_Unlock(vg);
430 }
431
432 /*
433 * Detach the specified node from its current parent. The node itself
434 * and all of its children are also detached from the VG. No reference
435 * checking is done.
436 */
437 void
VG_NodeDetach(void * p)438 VG_NodeDetach(void *p)
439 {
440 VG_Node *vn = p;
441 VG *vg = vn->vg;
442 VG_Node *vnChld, *vnNext;
443
444 #ifdef AG_DEBUG
445 if (vg == NULL)
446 AG_FatalError("VG_NodeDetach() on unattached node");
447 #endif
448 VG_Lock(vg);
449 for (vnChld = TAILQ_FIRST(&vn->cNodes);
450 vnChld != TAILQ_END(&vn->cNodes);
451 vnChld = vnNext) {
452 vnNext = TAILQ_NEXT(vnChld, tree);
453 VG_NodeDetach(vnChld);
454 }
455 TAILQ_INIT(&vn->cNodes);
456
457 if (vn->parent != NULL) {
458 TAILQ_REMOVE(&vn->parent->cNodes, vn, tree);
459 vn->parent = NULL;
460 }
461 TAILQ_REMOVE(&vg->nodes, vn, list);
462 vn->vg = NULL;
463 VG_Unlock(vg);
464 }
465
466 /* Set the symbolic name of a node. */
467 void
VG_SetSym(void * pNode,const char * fmt,...)468 VG_SetSym(void *pNode, const char *fmt, ...)
469 {
470 VG_Node *vn = pNode;
471 va_list args;
472
473 if (vn->vg != NULL) { VG_Lock(vn->vg); }
474 va_start(args, fmt);
475 Vsnprintf(vn->sym, sizeof(vn->sym), fmt, args);
476 va_end(args);
477 if (vn->vg != NULL) { VG_Unlock(vn->vg); }
478 }
479
480 void
VG_SetLayer(void * pNode,int layer)481 VG_SetLayer(void *pNode, int layer)
482 {
483 VGNODE(pNode)->layer = layer;
484 }
485
486 void
VG_SetColorv(void * pNode,const VG_Color * c)487 VG_SetColorv(void *pNode, const VG_Color *c)
488 {
489 VG_Node *vn = pNode;
490 vn->color.r = c->r;
491 vn->color.g = c->g;
492 vn->color.b = c->b;
493 vn->color.a = c->a;
494 }
495
496 void
VG_SetColorRGB(void * pNode,Uint8 r,Uint8 g,Uint8 b)497 VG_SetColorRGB(void *pNode, Uint8 r, Uint8 g, Uint8 b)
498 {
499 VG_Node *vn = pNode;
500 vn->color.r = r;
501 vn->color.g = g;
502 vn->color.b = b;
503 }
504
505 void
VG_SetColorRGBA(void * pNode,Uint8 r,Uint8 g,Uint8 b,Uint8 a)506 VG_SetColorRGBA(void *pNode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
507 {
508 VG_Node *vn = pNode;
509 vn->color.r = r;
510 vn->color.g = g;
511 vn->color.b = b;
512 vn->color.a = a;
513 }
514
515 /* Push a new layer onto the layer stack. */
516 VG_Layer *
VG_PushLayer(VG * vg,const char * name)517 VG_PushLayer(VG *vg, const char *name)
518 {
519 VG_Layer *vgl;
520
521 VG_Lock(vg);
522 vg->layers = Realloc(vg->layers, (vg->nLayers+1) *
523 sizeof(VG_Layer));
524 vgl = &vg->layers[vg->nLayers];
525 vg->nLayers++;
526 Strlcpy(vgl->name, name, sizeof(vgl->name));
527 vgl->visible = 1;
528 vgl->alpha = 255;
529 vgl->color = VG_GetColorRGB(255,255,255);
530 VG_Unlock(vg);
531 return (vgl);
532 }
533
534 /* Pop the highest layer off the layer stack. */
535 void
VG_PopLayer(VG * vg)536 VG_PopLayer(VG *vg)
537 {
538 VG_Lock(vg);
539 if (--vg->nLayers < 1) {
540 vg->nLayers = 1;
541 }
542 VG_Unlock(vg);
543 }
544
545 static void
SaveMatrix(VG_Matrix * A,AG_DataSource * ds)546 SaveMatrix(VG_Matrix *A, AG_DataSource *ds)
547 {
548 int m, n;
549
550 for (m = 0; m < 3; m++)
551 for (n = 0; n < 3; n++)
552 AG_WriteFloat(ds, A->m[m][n]);
553 }
554
555 static void
LoadMatrix(VG_Matrix * A,AG_DataSource * ds)556 LoadMatrix(VG_Matrix *A, AG_DataSource *ds)
557 {
558 int m, n;
559
560 for (m = 0; m < 3; m++)
561 for (n = 0; n < 3; n++)
562 A->m[m][n] = AG_ReadFloat(ds);
563 }
564
565 static void
SaveNodeGeneric(VG * vg,VG_Node * vn,AG_DataSource * ds)566 SaveNodeGeneric(VG *vg, VG_Node *vn, AG_DataSource *ds)
567 {
568 off_t nNodesOffs;
569 Uint32 nNodes = 0;
570 VG_Node *vnChld;
571
572 if (vn->flags & VG_NODE_NOSAVE)
573 return;
574
575 AG_WriteString(ds, vn->ops->name);
576 AG_WriteString(ds, vn->sym);
577 AG_WriteUint32(ds, (Uint32)vn->handle);
578 AG_WriteUint32(ds, (Uint32)(vn->flags & VG_NODE_SAVED_FLAGS));
579 AG_WriteUint32(ds, (Uint32)vn->layer);
580 VG_WriteColor(ds, &vn->color);
581 SaveMatrix(&vn->T, ds);
582
583 /* Save the entities. */
584 nNodesOffs = AG_Tell(ds);
585 AG_WriteUint32(ds, 0);
586 VG_FOREACH_CHLD(vnChld, vn, vg_node) {
587 SaveNodeGeneric(vg, vnChld, ds);
588 nNodes++;
589 }
590 AG_WriteUint32At(ds, nNodes, nNodesOffs);
591 }
592
593 static int
SaveNodeData(VG * vg,VG_Node * vn,AG_DataSource * ds)594 SaveNodeData(VG *vg, VG_Node *vn, AG_DataSource *ds)
595 {
596 VG_Node *vnChld;
597
598 VG_FOREACH_CHLD(vnChld, vn, vg_node) {
599 if (SaveNodeData(vg, vnChld, ds) == -1)
600 return (-1);
601 }
602 if (vn->ops->save != NULL) {
603 vn->ops->save(vn, ds);
604 }
605 return (0);
606 }
607
608 void
VG_Save(VG * vg,AG_DataSource * ds)609 VG_Save(VG *vg, AG_DataSource *ds)
610 {
611 off_t nNodesOffs;
612 Uint32 nNodes = 0;
613 VG_Node *vn;
614 Uint i;
615
616 AG_WriteVersion(ds, "Agar-VG", &vgVer);
617 AG_WriteString(ds, "VG"); /* name */
618
619 VG_Lock(vg);
620
621 AG_WriteUint32(ds, (Uint32)vg->flags);
622 VG_WriteColor(ds, &vg->fillColor);
623 VG_WriteColor(ds, &vg->fillColor); /* gridColor */
624 VG_WriteColor(ds, &vg->selectionColor);
625 VG_WriteColor(ds, &vg->mouseoverColor);
626 AG_WriteFloat(ds, 0.0f); /* gridIval */
627
628 /* Save the layer information. */
629 AG_WriteUint32(ds, (Uint32)vg->nLayers);
630 for (i = 0; i < vg->nLayers; i++) {
631 VG_Layer *layer = &vg->layers[i];
632
633 AG_WriteString(ds, layer->name);
634 AG_WriteUint8(ds, (Uint8)layer->visible);
635 VG_WriteColor(ds, &layer->color);
636 AG_WriteUint8(ds, layer->alpha);
637 }
638
639 /* Save the color table. */
640 AG_WriteUint32(ds, (Uint32)vg->nColors);
641 for (i = 0; i < vg->nColors; i++) {
642 VG_IndexedColor *vic = &vg->colors[i];
643 AG_WriteString(ds, vic->name);
644 VG_WriteColor(ds, &vic->color);
645 }
646
647 /* Save the entities. */
648 nNodesOffs = AG_Tell(ds);
649 AG_WriteUint32(ds, 0);
650 VG_FOREACH_CHLD(vn, vg->root, vg_node) {
651 SaveNodeGeneric(vg, vn, ds);
652 nNodes++;
653 }
654 AG_WriteUint32At(ds, nNodes, nNodesOffs);
655 SaveNodeData(vg, vg->root, ds);
656 VG_Unlock(vg);
657 }
658
659 static int
LoadNodeGeneric(VG * vg,VG_Node * vnParent,AG_DataSource * ds)660 LoadNodeGeneric(VG *vg, VG_Node *vnParent, AG_DataSource *ds)
661 {
662 char type[VG_TYPE_NAME_MAX];
663 VG_Node *vn;
664 VG_NodeOps *vnOps;
665 Uint32 i, nNodes;
666
667 AG_CopyString(type, ds, sizeof(type));
668 if ((vnOps = VG_LookupClass(type)) == NULL) {
669 return (-1);
670 }
671 vn = Malloc(vnOps->size);
672 VG_NodeInit(vn, vnOps);
673 AG_CopyString(vn->sym, ds, sizeof(vn->sym));
674 vn->handle = AG_ReadUint32(ds);
675 vn->flags = AG_ReadUint32(ds);
676 vn->layer = (int)AG_ReadUint32(ds);
677 vn->color = VG_ReadColor(ds);
678 LoadMatrix(&vn->T, ds);
679 VG_NodeAttach(vnParent, vn);
680
681 nNodes = AG_ReadUint32(ds);
682 for (i = 0; i < nNodes; i++) {
683 if (LoadNodeGeneric(vg, vn, ds) == -1)
684 return (-1);
685 }
686 return (0);
687 }
688
689 static int
LoadNodeData(VG * vg,VG_Node * vn,AG_DataSource * ds,const AG_Version * dsVer)690 LoadNodeData(VG *vg, VG_Node *vn, AG_DataSource *ds, const AG_Version *dsVer)
691 {
692 VG_Node *vnChld;
693
694 VG_FOREACH_CHLD(vnChld, vn, vg_node) {
695 if (LoadNodeData(vg, vnChld, ds, dsVer) == -1)
696 return (-1);
697 }
698 if (vn->ops->load != NULL &&
699 vn->ops->load(vn, ds, dsVer) == -1) {
700 AG_SetError("%s%u: %s", vn->ops->name, (Uint)vn->handle,
701 AG_GetError());
702 return (-1);
703 }
704 return (0);
705 }
706
707 int
VG_Load(VG * vg,AG_DataSource * ds)708 VG_Load(VG *vg, AG_DataSource *ds)
709 {
710 char name[VG_NAME_MAX];
711 AG_Version dsVer;
712 Uint32 i, nColors, nNodes;
713
714 if (AG_ReadVersion(ds, "Agar-VG", &vgVer, &dsVer) != 0) {
715 return (-1);
716 }
717 AG_CopyString(name, ds, sizeof(name)); /* Ignore */
718
719 VG_Lock(vg);
720 vg->flags = AG_ReadUint32(ds);
721 vg->fillColor = VG_ReadColor(ds);
722 (void)VG_ReadColor(ds); /* gridColor */
723 vg->selectionColor = VG_ReadColor(ds);
724 vg->mouseoverColor = VG_ReadColor(ds);
725 (void)AG_ReadFloat(ds); /* gridIval */
726
727 /* Read the layer information. */
728 vg->nLayers = (Uint)AG_ReadUint32(ds);
729 vg->layers = Realloc(vg->layers, vg->nLayers*sizeof(VG_Layer));
730 for (i = 0; i < vg->nLayers; i++) {
731 VG_Layer *layer = &vg->layers[i];
732
733 AG_CopyString(layer->name, ds, sizeof(layer->name));
734 layer->visible = (int)AG_ReadUint8(ds);
735 layer->color = VG_ReadColor(ds);
736 layer->alpha = AG_ReadUint8(ds);
737 }
738
739 /* Read the color table. */
740 nColors = AG_ReadUint32(ds);
741 vg->colors = Malloc(nColors*sizeof(VG_IndexedColor *));
742 vg->nColors = nColors;
743 for (i = 0; i < nColors; i++) {
744 VG_IndexedColor *vic = &vg->colors[i];
745 AG_CopyString(vic->name, ds, sizeof(vic->name));
746 vic->color = VG_ReadColor(ds);
747 }
748
749 /* Read the entities. */
750 VG_ClearNodes(vg);
751 nNodes = AG_ReadUint32(ds);
752 for (i = 0; i < nNodes; i++) {
753 if (LoadNodeGeneric(vg, vg->root, ds) == -1)
754 goto fail;
755 }
756 if (LoadNodeData(vg, vg->root, ds, &dsVer) == -1) {
757 goto fail;
758 }
759 VG_Unlock(vg);
760 return (0);
761 fail:
762 VG_Unlock(vg);
763 return (-1);
764 }
765
766 void
VG_WriteVector(AG_DataSource * ds,const VG_Vector * vtx)767 VG_WriteVector(AG_DataSource *ds, const VG_Vector *vtx)
768 {
769 AG_WriteFloat(ds, vtx->x);
770 AG_WriteFloat(ds, vtx->y);
771 }
772
773 void
VG_WriteColor(AG_DataSource * ds,const VG_Color * c)774 VG_WriteColor(AG_DataSource *ds, const VG_Color *c)
775 {
776 AG_WriteUint8(ds, c->r);
777 AG_WriteUint8(ds, c->g);
778 AG_WriteUint8(ds, c->b);
779 AG_WriteUint8(ds, c->a);
780 }
781
782 VG_Vector
VG_ReadVector(AG_DataSource * ds)783 VG_ReadVector(AG_DataSource *ds)
784 {
785 VG_Vector v;
786
787 v.x = AG_ReadFloat(ds);
788 v.y = AG_ReadFloat(ds);
789 return (v);
790 }
791
792 VG_Color
VG_ReadColor(AG_DataSource * ds)793 VG_ReadColor(AG_DataSource *ds)
794 {
795 VG_Color c;
796
797 c.r = AG_ReadUint8(ds);
798 c.g = AG_ReadUint8(ds);
799 c.b = AG_ReadUint8(ds);
800 c.a = AG_ReadUint8(ds);
801 c.idx = -1;
802 return (c);
803 }
804
805 /* Serialize a node->node reference. */
806 void
VG_WriteRef(AG_DataSource * ds,void * p)807 VG_WriteRef(AG_DataSource *ds, void *p)
808 {
809 VG_Node *vn = p;
810
811 AG_WriteString(ds, vn->ops->name);
812 AG_WriteUint32(ds, vn->handle);
813 }
814
815 /* Deserialize a node->node reference. */
816 void *
VG_ReadRef(AG_DataSource * ds,void * pNode,const char * expType)817 VG_ReadRef(AG_DataSource *ds, void *pNode, const char *expType)
818 {
819 VG_Node *vn = pNode;
820 char rType[VG_TYPE_NAME_MAX];
821 Uint32 handle;
822 void *vnFound;
823
824 AG_CopyString(rType, ds, sizeof(rType));
825 handle = AG_ReadUint32(ds);
826
827 if (expType != NULL) {
828 if (strcmp(rType, expType) != 0) {
829 AG_SetError("Unexpected reference type: %s "
830 "(expecting %s)", rType, expType);
831 return (NULL);
832 }
833 }
834 if ((vnFound = VG_FindNode(vn->vg, handle, rType)) == NULL) {
835 AG_SetError("Reference to unexisting item: %s%u", rType,
836 (Uint)handle);
837 return (NULL);
838 }
839 VG_AddRef(vn, vnFound);
840 return (vnFound);
841 }
842
843 /* Return the element closest to the given point. */
844 void *
VG_PointProximity(VG_View * vv,const char * type,const VG_Vector * vPt,VG_Vector * vC,void * ignoreNode)845 VG_PointProximity(VG_View *vv, const char *type, const VG_Vector *vPt,
846 VG_Vector *vC, void *ignoreNode)
847 {
848 VG *vg = vv->vg;
849 VG_Node *vn, *vnClosest = NULL;
850 float distClosest = AG_FLT_MAX, p;
851 VG_Vector v, vClosest = VGVECTOR(AG_FLT_MAX,AG_FLT_MAX);
852
853 VG_FOREACH_NODE(vn, vg, vg_node) {
854 if (vn == ignoreNode ||
855 vn->ops->pointProximity == NULL) {
856 continue;
857 }
858 if (type != NULL &&
859 strcmp(vn->ops->name, type) != 0) {
860 continue;
861 }
862 v = *vPt;
863 p = vn->ops->pointProximity(vn, vv, &v);
864 if (p < distClosest) {
865 distClosest = p;
866 vnClosest = vn;
867 vClosest = v;
868 }
869 }
870 if (vC != NULL) {
871 *vC = vClosest;
872 }
873 return (vnClosest);
874 }
875
876 /*
877 * Return the element closest to the given point, ignoring all elements
878 * beyond a specified distance.
879 */
880 void *
VG_PointProximityMax(VG_View * vv,const char * type,const VG_Vector * vPt,VG_Vector * vC,void * ignoreNode,float distMax)881 VG_PointProximityMax(VG_View *vv, const char *type, const VG_Vector *vPt,
882 VG_Vector *vC, void *ignoreNode, float distMax)
883 {
884 VG *vg = vv->vg;
885 VG_Node *vn, *vnClosest = NULL;
886 float distClosest = AG_FLT_MAX, p;
887 VG_Vector v, vClosest = VGVECTOR(AG_FLT_MAX,AG_FLT_MAX);
888
889 VG_FOREACH_NODE(vn, vg, vg_node) {
890 if (vn == ignoreNode ||
891 vn->ops->pointProximity == NULL) {
892 continue;
893 }
894 if (type != NULL &&
895 strcmp(vn->ops->name, type) != 0) {
896 continue;
897 }
898 v = *vPt;
899 p = vn->ops->pointProximity(vn, vv, &v);
900 if (p < distMax && p < distClosest) {
901 distClosest = p;
902 vnClosest = vn;
903 vClosest = v;
904 }
905 }
906 if (vC != NULL) {
907 *vC = vClosest;
908 }
909 return (vnClosest);
910 }
911
912 /*
913 * Compute the product of the transform matrices of the given node and its
914 * parents in order. T is initialized to identity.
915 */
916 void
VG_NodeTransform(void * p,VG_Matrix * T)917 VG_NodeTransform(void *p, VG_Matrix *T)
918 {
919 VG_Node *node = p;
920 VG_Node *cNode = node;
921 TAILQ_HEAD_(vg_node) rNodes = TAILQ_HEAD_INITIALIZER(rNodes);
922
923 while (cNode != NULL) {
924 TAILQ_INSERT_HEAD(&rNodes, cNode, reverse);
925 if (cNode->parent == NULL) {
926 break;
927 }
928 cNode = cNode->parent;
929 }
930 *T = VG_MatrixIdentity();
931 TAILQ_FOREACH(cNode, &rNodes, reverse)
932 VG_MultMatrix(T, &cNode->T);
933 }
934
935 /* Compute the inverse of a VG transformation matrix. */
936 VG_Matrix
VG_MatrixInvert(VG_Matrix A)937 VG_MatrixInvert(VG_Matrix A)
938 {
939 VG_Matrix B;
940 float det, detInv;
941 int i, j;
942
943 B.m[0][0] = A.m[1][1]*A.m[2][2] - A.m[1][2]*A.m[2][1];
944 B.m[0][1] = A.m[0][2]*A.m[2][1] - A.m[0][1]*A.m[2][2];
945 B.m[0][2] = A.m[0][1]*A.m[1][2] - A.m[0][2]*A.m[1][1];
946 B.m[1][0] = A.m[1][2]*A.m[2][0] - A.m[1][0]*A.m[2][2];
947 B.m[1][1] = A.m[0][0]*A.m[2][2] - A.m[0][2]*A.m[2][0];
948 B.m[1][2] = A.m[0][2]*A.m[1][0] - A.m[0][0]*A.m[1][2];
949 B.m[2][0] = A.m[1][0]*A.m[2][1] - A.m[1][1]*A.m[2][0];
950 B.m[2][1] = A.m[0][1]*A.m[2][0] - A.m[0][0]*A.m[2][1];
951 B.m[2][2] = A.m[0][0]*A.m[1][1] - A.m[0][1]*A.m[1][0];
952
953 det = A.m[0][0]*B.m[0][0] +
954 A.m[0][1]*B.m[1][0] +
955 A.m[0][2]*B.m[2][0];
956 if (Fabs(det) <= 1e-6f)
957 AG_FatalError("Singular matrix");
958
959 detInv = 1.0/det;
960 for (i = 0; i < 3; i++) {
961 for (j = 0; j < 3; j++)
962 B.m[i][j] *= detInv;
963 }
964 return (B);
965 }
966
967