/* * COPYRIGHT * * cschem - modular/flexible schematics editor - libcschem (core library) * Copyright (C) 2018 Tibor 'Igor2' Palinkas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version.* * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA * * Contact: * Project page: http://repo.hu/projects/sch-rnd * contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html * mailing list: http://www.repo.hu/projects/sch-rnd/contact.html */ /* Generic object helpers */ #ifndef CSCH_CONCRETE_OBJ_H #define CSCH_CONCRETE_OBJ_H #include #include #include #include "event.h" #include "attrib.h" #include "cnc_pen.h" #include "cnc_text.h" #define csch_obj_bbox_reset(obj) csch_bbox_reset(&((obj)->hdr.bbox)) #define csch_cobj_redraw(obj) \ do { \ csch_chdr_t *__obj__ = (csch_chdr_t *)obj; \ if (__obj__->bbox.x1 < __obj__->bbox.x2) { \ if (__obj__->sheet->redraw_inhibit == 0) \ rnd_event(&__obj__->sheet->hidlib, CSCH_EVENT_BOX_NEEDS_REDRAW, "p", &__obj__->bbox); \ else \ csch_sheet_inval_lr(__obj__->sheet, &__obj__->bbox); \ } \ } while(0) /* Stop generating redraw events on object changes */ RND_INLINE void csch_cobj_redraw_freeze(csch_sheet_t *sheet); RND_INLINE void csch_cobj_redraw_unfreeze(csch_sheet_t *sheet); /* implement heuristics to figure on which display layer an object is supposed to go */ csch_displayer_t csch_cobj_dsply(csch_chdr_t *obj); /* Recalculates the display layer of obj and do all the rtree administration if it has changed. Returns whether it has changed. */ int csch_cobj_update_dsply(csch_sheet_t *sheet, csch_chdr_t *obj); /* Object rtree management; del returns 0 on success (object got removed from the tree) and non-zero when the object was not in the tree. */ RND_INLINE int csch_cobj_rtree_del(csch_sheet_t *sheet, csch_chdr_t *obj); RND_INLINE void csch_cobj_rtree_add(csch_sheet_t *sheet, csch_chdr_t *obj); /* Insert an object in a group by setting the common header fields; do not use directly, use csch_cobj_insert() */ RND_INLINE void csch_cobj_insert_(csch_chdr_t *dst, csch_sheet_t *sheet, csch_cgrp_t *parent, csch_oid_t oid); /* Initialize an object by setting the common header fields; the only purpose of this function is to get compiler warnings at every caller when the header is extended (because the number of args would be extended here) */ RND_INLINE void csch_cobj_init(csch_chdr_t *dst, csch_sheet_t *sheet, csch_cgrp_t *parent, csch_oid_t oid, csch_ctype_t type); /* free common fields */ RND_INLINE void csch_cobj_uninit(csch_chdr_t *dst); /* Recalculate stroke/fill cache in dst */ RND_INLINE void csch_cobj_update_pen(csch_chdr_t *dst); /* Insert an object in parent group, using new oid, if the object is not already in a group (so obj->parent must be NULL). Sheet is (the new) parent's sheet. Returns 0 on success. */ int csch_cobj_insert(csch_sheet_t *sheet, csch_cgrp_t *parent, csch_chdr_t *obj, csch_oid_t oid); /* Returns whether the group is atomic (group parts are not directly editable) */ RND_INLINE int csch_grp_is_atomic(csch_sheet_t *sheet, csch_cgrp_t *grp); /* Returns 1 if obj is locked for any reason (lock property, group-lock); this means obj should not be edited directly. */ RND_INLINE int csch_cobj_is_locked(csch_chdr_t *obj); /* Returns 1 if obj is locked with the lock property directly or through parent, but ignores group locking */ RND_INLINE int csch_cobj_is_explicit_locked(csch_chdr_t *obj); /* Similar to csch_cobj_is_locked() but returns the first parent that is not locked; doesn't return sheet->direct; returns NULL if no such parent found */ RND_INLINE csch_chdr_t *csch_cobj_first_unlocked(csch_chdr_t *obj); /* Similar to csch_cobj_is_locked() but returns the first parent that is explicitly locked using the lock attr; doesn't return sheet->direct; returns NULL if no such parent found */ RND_INLINE csch_chdr_t *csch_cobj_first_explicit_locked(csch_chdr_t *obj); /* Modify common properties (doc: {des3:81}) of any object */ void csch_commprp_modify(csch_sheet_t *sheet, csch_chdr_t *obj, int *lock, int *floater, int undoable); /* Call this after obj's bbox changed; updates parent group's bbox recursively up to the root group, as needed */ void csch_cobj_bbox_changed(csch_sheet_t *sheet, csch_chdr_t *obj); /*** Hash ***/ unsigned csch_chdr_hash(const csch_chdr_t *hdr); RND_INLINE unsigned csch_coord_hash(csch_coord_t crd); RND_INLINE unsigned csch_angle_hash(double angle); unsigned csch_chdr_eq(const csch_chdr_t *hdr1, const csch_chdr_t *hdr2); RND_INLINE unsigned csch_angle_eq(double angle1, double angle2); /*** Implementation ***/ /* Internal call for csch_cobj_redraw(): add box to the redraw bbox (sheet->inbbox) without any immediate redraw */ RND_INLINE void csch_sheet_inval_lr(csch_sheet_t *sheet, csch_rtree_box_t *box) { if (box->x1 < sheet->invbox.x1) sheet->invbox.x1 = box->x1; if (box->x2 > sheet->invbox.x2) sheet->invbox.x2 = box->x2; if (box->y1 < sheet->invbox.y1) sheet->invbox.y1 = box->y1; if (box->y2 > sheet->invbox.y2) sheet->invbox.y2 = box->y2; sheet->invbox_valid = 1; } RND_INLINE void csch_cobj_redraw_freeze(csch_sheet_t *sheet) { sheet->redraw_inhibit++; if (sheet->redraw_inhibit == 1) { sheet->invbox.x1 = sheet->invbox.y1 = CSCH_COORD_MAX; sheet->invbox.x2 = sheet->invbox.y2 = -CSCH_COORD_MAX; sheet->invbox_valid = 0; } } RND_INLINE void csch_cobj_redraw_unfreeze(csch_sheet_t *sheet) { assert(sheet->redraw_inhibit > 0); if (sheet->redraw_inhibit > 0) { sheet->redraw_inhibit--; if ((sheet->redraw_inhibit == 0) && sheet->invbox_valid) { rnd_event(&sheet->hidlib, CSCH_EVENT_BOX_NEEDS_REDRAW, "p", &sheet->invbox); sheet->invbox_valid = 0; } } } int csch_cobj_has_fill_(csch_chdr_t *obj); RND_INLINE int csch_cobj_has_fill(csch_chdr_t *obj) { if (obj->type != CSCH_CTYPE_POLY) return 0; return csch_cobj_has_fill_(obj); } RND_INLINE int csch_cobj_rtree_del(csch_sheet_t *sheet, csch_chdr_t *obj) { if ((obj->dsply < 0) || (obj->dsply >= CSCH_DSPLY_max) || (csch_bbox_is_invalid(&obj->bbox))) return -1; if (csch_cobj_has_fill(obj)) csch_rtree_delete(&sheet->dsply_fill[obj->dsply], obj, &obj->bbox); return csch_rtree_delete(&sheet->dsply[obj->dsply], obj, &obj->bbox); } RND_INLINE void csch_cobj_rtree_add(csch_sheet_t *sheet, csch_chdr_t *obj) { if ((obj->dsply < 0) || (obj->dsply >= CSCH_DSPLY_max) || (csch_bbox_is_invalid(&obj->bbox)) || obj->indirect) return; if (csch_cobj_has_fill(obj)) csch_rtree_insert(&sheet->dsply_fill[obj->dsply], obj, &obj->bbox); csch_rtree_insert(&sheet->dsply[obj->dsply], obj, &obj->bbox); } RND_INLINE void csch_cobj_insert_(csch_chdr_t *dst, csch_sheet_t *sheet, csch_cgrp_t *parent, csch_oid_t oid) { csch_cgrp_t *p; dst->oid = oid; dst->parent = parent; dst->sheet = sheet; dst->dsply = csch_cobj_dsply(dst); gdl_append(&sheet->active, dst, link); if (dst->parent != NULL) htip_set(&parent->id2obj, oid, dst); dst->indirect = 0; for(p = parent; p != NULL; p = p->hdr.parent) { if (p == &sheet->indirect) { dst->indirect = 1; break; } } } RND_INLINE void csch_cobj_init(csch_chdr_t *dst, csch_sheet_t *sheet, csch_cgrp_t *parent, csch_oid_t oid, csch_ctype_t type) { dst->type = type; csch_cobj_insert_(dst, sheet, parent, oid); csch_bbox_reset(&dst->bbox); } RND_INLINE void csch_cobj_uninit(csch_chdr_t *dst) { csch_chdr_t *o; if (dst->link.parent != NULL) gdl_remove(dst->link.parent, dst, link); csch_cobj_rtree_del(dst->sheet, dst); if ((dst->sheet != NULL) && (dst->parent != NULL)) { o = htip_pop(&dst->parent->id2obj, dst->oid); if (o != NULL) assert(o == dst); } vtp0_uninit(&dst->conn); } RND_INLINE void csch_cobj_update_pen(csch_chdr_t *dst) { csch_cobj_redraw(dst); csch_stroke_(dst); csch_fill_(dst); if (dst->type == CSCH_CTYPE_TEXT) csch_text_invalidate_font((csch_text_t *)dst); csch_cobj_redraw(dst); } RND_INLINE int csch_grp_is_atomic(csch_sheet_t *sheet, csch_cgrp_t *grp) { if ((grp == NULL) || (grp == &sheet->direct) || (grp == &sheet->indirect)) return 0; switch(grp->role) { case CSCH_ROLE_invalid: case CSCH_ROLE_WIRE_NET: return 0; case CSCH_ROLE_empty: /* the user groupped it for this specific reason */ case CSCH_ROLE_JUNCTION: case CSCH_ROLE_BUS_NET: case CSCH_ROLE_BUS_TERMINAL: case CSCH_ROLE_HUB_POINT: case CSCH_ROLE_TERMINAL: case CSCH_ROLE_EXTOBJ_GFX: return 1; case CSCH_ROLE_SYMBOL: return !sheet->loose_sym; } return 0; } RND_INLINE int csch_cobj_is_explicit_locked(csch_chdr_t *obj) { csch_chdr_t *o; /* if the object or any parent is locked, object is locked */ for(o = obj; o != NULL; o = &o->parent->hdr) if (o->lock) return 1; return 0; } RND_INLINE int csch_cobj_is_locked(csch_chdr_t *obj) { /* group lock */ if (csch_grp_is_atomic(obj->sheet, obj->parent) && !obj->floater) return 1; return csch_cobj_is_explicit_locked(obj); } RND_INLINE csch_chdr_t *csch_cobj_first_unlocked(csch_chdr_t *obj) { csch_chdr_t *o; /* obj not group locked */ if (!obj->lock && (!csch_grp_is_atomic(obj->sheet, obj->parent) || obj->floater)) return obj; /* find first unlocked parent */ for(o = &obj->parent->hdr; ((o != NULL) && (o != &obj->sheet->direct.hdr)); o = &o->parent->hdr) if (!o->lock && (!csch_grp_is_atomic(o->sheet, obj->parent) || o->floater)) return o; return NULL; } RND_INLINE csch_chdr_t *csch_cobj_first_explicit_locked(csch_chdr_t *obj) { csch_chdr_t *o; if (obj->lock) return obj; /* find first unlocked parent */ for(o = &obj->parent->hdr; ((o != NULL) && (o != &obj->sheet->direct.hdr)); o = &o->parent->hdr) if (o->lock) return o; return NULL; } RND_INLINE unsigned csch_coord_hash(csch_coord_t crd) { return crd; } RND_INLINE unsigned csch_angle_hash(double angle) { return floor(angle * 100.0); } RND_INLINE unsigned csch_angle_eq(double angle1, double angle2) { double diff = angle1 - angle2; return ((diff >= -0.01) && (diff <= +0.01)); } #endif