/* * * COPYRIGHT * * pcb-rnd, interactive printed circuit board design * Copyright (C) 2018 Tibor 'Igor2' Palinkas * * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * * Contact: * Project page: http://repo.hu/projects/pcb-rnd * lead developer: http://repo.hu/projects/pcb-rnd/contact.html * mailing list: pcb-rnd (at) list.repo.hu (send "subscribe") * */ #include "config.h" #include "idpath.h" #include #define TDL_DONT_UNDEF #include "view.h" #include #undef TDL_DONT_UNDEF #include #include #include #include #include #include #include #include "conf_core.h" #include "netlist.h" static unsigned long int pcb_view_next_uid = 0; void pcb_view_free(pcb_view_t *item) { pcb_view_list_remove(item); pcb_idpath_list_clear(&item->objs[0]); pcb_idpath_list_clear(&item->objs[1]); free(item->title); free(item->netname[0]); free(item->netname[1]); if (item->long_desc.used == 0) free(item->description); else gds_uninit(&item->long_desc); free(item); } void pcb_view_list_free_fields(pcb_view_list_t *lst) { for(;;) { pcb_view_t *item = pcb_view_list_first(lst); if (item == NULL) break; pcb_view_free(item); } } void pcb_view_list_free(pcb_view_list_t *lst) { pcb_view_list_free_fields(lst); free(lst); } pcb_view_t *pcb_view_by_uid(const pcb_view_list_t *lst, unsigned long int uid) { pcb_view_t *v; for(v = pcb_view_list_first((pcb_view_list_t *)lst); v != NULL; v = pcb_view_list_next(v)) if (v->uid == uid) return v; return NULL; } pcb_view_t *pcb_view_by_uid_cnt(const pcb_view_list_t *lst, unsigned long int uid, long *cnt) { pcb_view_t *v; long c = 0; for(v = pcb_view_list_first((pcb_view_list_t *)lst); v != NULL; v = pcb_view_list_next(v), c++) { if (v->uid == uid) { *cnt = c; return v; } } *cnt = -1; return NULL; } void pcb_view_goto(pcb_view_t *item) { if (item->have_bbox) { fgw_arg_t res, argv[5]; argv[1].type = FGW_COORD; fgw_coord(&argv[1]) = item->bbox.X1; argv[2].type = FGW_COORD; fgw_coord(&argv[2]) = item->bbox.Y1; argv[3].type = FGW_COORD; fgw_coord(&argv[3]) = item->bbox.X2; argv[4].type = FGW_COORD; fgw_coord(&argv[4]) = item->bbox.Y2; rnd_actionv_bin(item->hidlib, "zoom", &res, 5, argv); } } pcb_view_t *pcb_view_new(rnd_design_t *hl, const char *type, const char *title, const char *description) { pcb_view_t *v = calloc(sizeof(pcb_view_t), 1); pcb_view_next_uid++; v->uid = pcb_view_next_uid; if (type == NULL) type = ""; if (title == NULL) title = ""; if (description == NULL) description = ""; v->type = rnd_strdup(type); v->title = rnd_strdup(title); v->description = rnd_strdup(description); v->hidlib = hl; return v; } void pcb_view_append_obj(pcb_view_t *view, int grp, pcb_any_obj_t *obj) { pcb_idpath_t *idp; assert((grp == 0) || (grp == 1)); switch(obj->type) { case PCB_OBJ_TEXT: case PCB_OBJ_SUBC: case PCB_OBJ_LINE: case PCB_OBJ_ARC: case PCB_OBJ_POLY: case PCB_OBJ_PSTK: case PCB_OBJ_RAT: case PCB_OBJ_GFX: idp = pcb_obj2idpath(obj); if (idp == NULL) rnd_message(RND_MSG_ERROR, "Internal error in pcb_drc_append_obj: can not resolve object id path\n"); else pcb_idpath_list_append(&view->objs[grp], idp); break; case PCB_OBJ_NET: view->netname[grp] = rnd_strdup(((pcb_net_t *)obj)->name); break; default: rnd_message(RND_MSG_ERROR, "Internal error in pcb_drc_append_obj: unknown object type %i\n", obj->type); } } void pcb_view_set_bbox_by_objs(pcb_data_t *data, pcb_view_t *v) { int g; rnd_box_t b; pcb_any_obj_t *obj; pcb_idpath_t *idp; /* special case: no object - leave coords unloaded/invalid */ if ((pcb_idpath_list_length(&v->objs[0]) < 1) && (pcb_idpath_list_length(&v->objs[1]) < 1)) return; /* special case: single objet in group A, use the center */ if (!conf_core.editor.drc_inclusive_bbox && (pcb_idpath_list_length(&v->objs[0]) == 1)) { idp = pcb_idpath_list_first(&v->objs[0]); obj = pcb_idpath2obj_in(data, idp); if (obj != NULL) { v->have_bbox = 1; pcb_obj_center(obj, &v->x, &v->y); memcpy(&v->bbox, &obj->BoundingBox, sizeof(obj->BoundingBox)); rnd_box_enlarge(&v->bbox, 0.25, 0.25); return; } } b.X1 = b.Y1 = RND_MAX_COORD; b.X2 = b.Y2 = -RND_MAX_COORD; for(g = 0; g < 2; g++) { for(idp = pcb_idpath_list_first(&v->objs[g]); idp != NULL; idp = pcb_idpath_list_next(idp)) { obj = pcb_idpath2obj_in(data, idp); if (obj != NULL) { v->have_bbox = 1; rnd_box_bump_box(&b, &obj->BoundingBox); } } } if (v->have_bbox) { v->x = (b.X1 + b.X2)/2; v->y = (b.Y1 + b.Y2)/2; memcpy(&v->bbox, &b, sizeof(b)); rnd_box_enlarge(&v->bbox, 0.25, 0.25); } } static void view_append_str(gds_t *dst, const char *prefix, const char *key, const char *value) { const char *s; rnd_append_printf(dst, "%s %s = {", prefix, key); for(s = value; *s != '\0'; s++) { switch(*s) { case '\\': case '}': gds_append(dst, '\\'); break; } gds_append(dst, *s); } gds_append_str(dst, "}\n"); } void pcb_view_save_list_begin(gds_t *dst, const char *prefix) { if (prefix != NULL) gds_append_str(dst, prefix); gds_append_str(dst, "li:view-list-v1 {\n"); } void pcb_view_save_list_end(gds_t *dst, const char *prefix) { if (prefix != NULL) gds_append_str(dst, prefix); gds_append_str(dst, "}\n"); } void pcb_view_save(pcb_view_t *v, gds_t *dst, const char *prefix) { int g, n; if (prefix == NULL) prefix = ""; rnd_append_printf(dst, "%sha:view.%lu {\n", prefix, v->uid); view_append_str(dst, prefix, "type", v->type); view_append_str(dst, prefix, "title", v->title); view_append_str(dst, prefix, "description", v->description); if (v->have_bbox) rnd_append_printf(dst, "%s li:bbox = {%.08$$mm; %.08$$mm; %.08$$mm; %.08$$mm;}\n", prefix, v->bbox.X1, v->bbox.Y1, v->bbox.X2, v->bbox.Y2); if (v->have_xy) rnd_append_printf(dst, "%s li:xy = {%.08$$mm; %.08$$mm;}\n", prefix, v->x, v->y); for(g = 0; g < 2; g++) { if (pcb_idpath_list_length(&v->objs[g]) > 0) { pcb_idpath_t *i; rnd_append_printf(dst, "%s li:objs.%d {\n", prefix, g); for(i = pcb_idpath_list_first(&v->objs[g]); i != NULL; i = pcb_idpath_list_next(i)) { rnd_append_printf(dst, "%s li:id {", prefix); for(n = 0; n < i->len; n++) rnd_append_printf(dst, "%ld;", i->id[n]); rnd_append_printf(dst, "}\n", prefix); } rnd_append_printf(dst, "%s }\n", prefix); } } if (v->netname[0] != NULL) rnd_append_printf(dst, "%s netname.0 = {%s}\n", prefix, v->netname[0]); if (v->netname[1] != NULL) rnd_append_printf(dst, "%s netname.0 = {%s}\n", prefix, v->netname[1]); switch(v->data_type) { case PCB_VIEW_PLAIN: rnd_append_printf(dst, "%s data_type = plain\n", prefix); break; case PCB_VIEW_DRC: rnd_append_printf(dst, "%s data_type = drc\n", prefix); rnd_append_printf(dst, "%s ha:data {\n", prefix); rnd_append_printf(dst, "%s required_value = %mw\n", prefix, v->data.drc.required_value); if (v->data.drc.have_measured) rnd_append_printf(dst, "%s measured_value = %mw\n", prefix, v->data.drc.measured_value); rnd_append_printf(dst, "%s }\n", prefix); break; } rnd_append_printf(dst, "%s}\n", prefix); } typedef struct { lht_doc_t *doc; lht_node_t *next; } load_ctx_t; void pcb_view_load_end(void *load_ctx) { load_ctx_t *ctx = load_ctx; lht_dom_uninit(ctx->doc); free(ctx); } static void *view_load_post(load_ctx_t *ctx) { if (ctx->doc->root == NULL) goto error; if (ctx->doc->root->type == LHT_LIST) { if (strcmp(ctx->doc->root->name, "view-list-v1") != 0) goto error; ctx->next = ctx->doc->root->data.list.first; return ctx; } if (ctx->doc->root->type == LHT_HASH) { if (strncmp(ctx->doc->root->name, "view.", 5) != 0) goto error; ctx->next = ctx->doc->root; return ctx; } error:; pcb_view_load_end(ctx); return NULL; } void *pcb_view_load_start_str(const char *src) { load_ctx_t *ctx = malloc(sizeof(load_ctx_t)); ctx->doc = lht_dom_init(); for(; *src != '\0'; src++) { lht_err_t err = lht_dom_parser_char(ctx->doc, *src); if ((err != LHTE_SUCCESS) && (err != LHTE_STOP)) { pcb_view_load_end(ctx); return NULL; } } return view_load_post(ctx); } void *pcb_view_load_start_file(FILE *f) { int c; load_ctx_t *ctx = malloc(sizeof(load_ctx_t)); ctx->doc = lht_dom_init(); while((c = fgetc(f)) != EOF) { lht_err_t err = lht_dom_parser_char(ctx->doc, c); if ((err != LHTE_SUCCESS) && (err != LHTE_STOP)) { pcb_view_load_end(ctx); return NULL; } } return view_load_post(ctx); } #define LOADERR "Error loading view: " static void pcb_view_load_objs(pcb_view_t *dst, int grp, lht_node_t *olist) { lht_node_t *n, *m; for(n = olist->data.list.first; n != NULL; n = n->next) { if ((n->type == LHT_LIST) && (strcmp(n->name, "id") == 0)) { pcb_idpath_t *i; int len, cnt; for(m = n->data.list.first, len = 0; m != NULL; m = m->next) { if (m->type != LHT_TEXT) { rnd_message(RND_MSG_ERROR, LOADERR "Invalid object id (non-text)\n"); goto nope; } len++; } i = pcb_idpath_alloc(len); for(m = n->data.list.first, cnt = 0; m != NULL; m = m->next, cnt++) i->id[cnt] = strtol(m->data.text.value, NULL, 10); pcb_idpath_list_append(&dst->objs[grp], i); } else rnd_message(RND_MSG_ERROR, LOADERR "Invalid object id-path\n"); nope:; } } static fgw_arg_t load_drc_val(const char *val) { const rnd_unit_t *u; double d; fgw_arg_t a; char *end; if (rnd_get_value_unit(val, NULL, 1, &d, &u)) { a.type = FGW_COORD; fgw_coord(&a) = d; return a; } if (strchr(val, '.')) { d = strtod(val, &end); if (*end == '\0') { a.type = FGW_DOUBLE; a.val.nat_double = d; return a; } } else { long l = strtol(val, &end, 10); if (*end == '\0') { a.type = FGW_LONG; a.val.nat_long = l; return a; } } a.type = FGW_VOID; return a; } pcb_view_t *pcb_view_load_next(void *load_ctx, pcb_view_t *dst) { load_ctx_t *ctx = load_ctx; lht_node_t *n, *c; unsigned long int uid; char *end; pcb_view_type_t data_type; rnd_bool succ; if (ctx->next == NULL) return NULL; if ((ctx->next->type != LHT_HASH) || (strncmp(ctx->next->name, "view.", 5) != 0)) return NULL; uid = strtoul(ctx->next->name + 5, &end, 10); if (*end != '\0') return NULL; data_type = PCB_VIEW_PLAIN; n = lht_dom_hash_get(ctx->next, "data_type"); if ((n != NULL) && (n->type == LHT_TEXT)) { if (strcmp(n->data.text.value, "drc") == 0) data_type = PCB_VIEW_DRC; else if (strcmp(n->data.text.value, "plain") == 0) data_type = PCB_VIEW_PLAIN; else { rnd_message(RND_MSG_ERROR, LOADERR "Invalid data type: '%s'\n", n->data.text.value); return NULL; } } if (dst == NULL) dst = calloc(sizeof(pcb_view_t), 1); dst->uid = uid; dst->data_type= data_type; n = lht_dom_hash_get(ctx->next, "type"); if ((n != NULL) && (n->type == LHT_TEXT)) { dst->type = n->data.text.value; n->data.text.value = NULL; } n = lht_dom_hash_get(ctx->next, "title"); if ((n != NULL) && (n->type == LHT_TEXT)) { dst->title = n->data.text.value; n->data.text.value = NULL; } n = lht_dom_hash_get(ctx->next, "description"); if ((n != NULL) && (n->type == LHT_TEXT)) { dst->description = n->data.text.value; n->data.text.value = NULL; } n = lht_dom_hash_get(ctx->next, "bbox"); if ((n != NULL) && (n->type == LHT_LIST)) { int ok = 0; c = n->data.list.first; if ((c != NULL) && (c->type == LHT_TEXT)) { dst->bbox.X1 = rnd_get_value(c->data.text.value, NULL, NULL, &succ); if (succ) ok++; c = c->next; } if ((c != NULL) && (c->type == LHT_TEXT)) { dst->bbox.Y1 = rnd_get_value(c->data.text.value, NULL, NULL, &succ); if (succ) ok++; c = c->next; } if ((c != NULL) && (c->type == LHT_TEXT)) { dst->bbox.X2 = rnd_get_value(c->data.text.value, NULL, NULL, &succ); if (succ) ok++; c = c->next; } if ((c != NULL) && (c->type == LHT_TEXT)) { dst->bbox.Y2 = rnd_get_value(c->data.text.value, NULL, NULL, &succ); if (succ) ok++; c = c->next; } if ((c == NULL) && (ok == 4)) dst->have_bbox = 1; else rnd_message(RND_MSG_ERROR, LOADERR "Invalid bbox values\n"); } n = lht_dom_hash_get(ctx->next, "xy"); if ((n != NULL) && (n->type == LHT_LIST)) { int ok = 0; c = n->data.list.first; if ((c != NULL) && (c->type == LHT_TEXT)) { dst->x = rnd_get_value(c->data.text.value, NULL, NULL, &succ); if (succ) ok++; c = c->next; } if ((c != NULL) && (c->type == LHT_TEXT)) { dst->y = rnd_get_value(c->data.text.value, NULL, NULL, &succ); if (succ) ok++; c = c->next; } if ((c == NULL) && (ok == 2)) dst->have_bbox = 1; else rnd_message(RND_MSG_ERROR, LOADERR "Invalid xy values\n"); } n = lht_dom_hash_get(ctx->next, "objs.0"); if ((n != NULL) && (n->type == LHT_LIST)) pcb_view_load_objs(dst, 0, n); n = lht_dom_hash_get(ctx->next, "objs.1"); if ((n != NULL) && (n->type == LHT_LIST)) pcb_view_load_objs(dst, 1, n); n = lht_dom_hash_get(ctx->next, "netname.0"); if ((n != NULL) && (n->type == LHT_TEXT)) { dst->netname[0] = n->data.text.value; n->data.text.value = NULL; } n = lht_dom_hash_get(ctx->next, "netname.1"); if ((n != NULL) && (n->type == LHT_TEXT)) { dst->netname[1] = n->data.text.value; n->data.text.value = NULL; } switch(dst->data_type) { case PCB_VIEW_PLAIN: break; case PCB_VIEW_DRC: n = lht_dom_hash_get(ctx->next, "data"); if ((n != NULL) && (n->type == LHT_HASH)) { c = lht_dom_hash_get(n, "required_value"); if ((c != NULL) && (c->type == LHT_TEXT)) { dst->data.drc.required_value = load_drc_val(c->data.text.value); if (!succ) rnd_message(RND_MSG_ERROR, LOADERR "invalid drc required value: '%s'\n", c->data.text.value); } c = lht_dom_hash_get(n, "measured_value"); if ((c != NULL) && (c->type == LHT_TEXT)) { dst->data.drc.measured_value = load_drc_val(c->data.text.value); if (succ) dst->data.drc.have_measured = 1; else rnd_message(RND_MSG_ERROR, LOADERR "invalid drc measured value: '%s'\n", c->data.text.value); } } break; } ctx->next = ctx->next->next; return dst; } void pcb_view_append_text(pcb_view_t *view, const char *txt) { if (view->long_desc.used == 0) { gds_append_str(&view->long_desc, view->description); gds_append_str(&view->long_desc, " Details: "); free(view->description); } gds_append_str(&view->long_desc, txt); view->description = view->long_desc.array; }