/* * COPYRIGHT * * pcb-rnd, interactive printed circuit board design * (this file is based on PCB, interactive printed circuit board design) * Copyright (C) 1994,1995,1996 Thomas Nau * Copyright (C) 2020,2023 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 Street, 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") * */ /* Drawing primitive: polygons */ #include "config.h" #include #include "board.h" #include "data.h" #include "undo.h" #include "polygon.h" #include #include #include "rotate.h" #include "search.h" #include "thermal.h" #include #include "conf_core.h" #include "obj_poly.h" #include "obj_poly_op.h" #include "obj_poly_list.h" #include "obj_poly_draw.h" #include "obj_term.h" #include "obj_subc_parent.h" #include "obj_hash.h" #define STEP_POLYGONPOINT 10 #define STEP_POLYGONHOLEINDEX 10 #include "obj_poly_draw_helper.c" TODO("ui_layer parent fix: remove this") #include "layer_ui.h" /*** allocation ***/ void pcb_poly_reg(pcb_layer_t *layer, pcb_poly_t *poly) { polylist_append(&layer->Polygon, poly); PCB_SET_PARENT(poly, layer, layer); if (pcb_is_uilayer(layer)) pcb_obj_id_reg(layer->parent.data, poly); else if (layer->parent_type == PCB_PARENT_DATA) pcb_obj_id_reg(layer->parent.data, poly); } void pcb_poly_unreg(pcb_poly_t *poly) { pcb_layer_t *layer = poly->parent.layer; pcb_term_del_auto((pcb_any_obj_t *)poly); assert(poly->parent_type == PCB_PARENT_LAYER); polylist_remove(poly); if (pcb_is_uilayer(layer)) { pcb_obj_id_del(layer->parent.data, poly); } else { assert(layer->parent_type == PCB_PARENT_DATA); pcb_obj_id_del(layer->parent.data, poly); } PCB_SET_PARENT(poly, layer, NULL); } pcb_poly_t *pcb_poly_alloc_id(pcb_layer_t *layer, long int id) { pcb_poly_t *new_obj; new_obj = calloc(sizeof(pcb_poly_t), 1); new_obj->ID = id; new_obj->type = PCB_OBJ_POLY; new_obj->Attributes.post_change = pcb_obj_attrib_post_change; pcb_poly_reg(layer, new_obj); return new_obj; } pcb_poly_t *pcb_poly_alloc(pcb_layer_t *layer) { return pcb_poly_alloc_id(layer, pcb_create_ID_get()); } void pcb_poly_free(pcb_poly_t *poly) { if ((poly->parent.layer != NULL) && (poly->parent.layer->polygon_tree != NULL)) rnd_rtree_delete(poly->parent.layer->polygon_tree, poly, (rnd_rtree_box_t *)poly); pcb_attribute_free(&poly->Attributes); pcb_poly_unreg(poly); pcb_obj_common_free((pcb_any_obj_t *)poly); free(poly); } /* realloc new memory if necessary and clear it */ RND_INLINE void pcb_poly_point_prealloc_(pcb_poly_t *Polygon, long num_pt, long step) { if (num_pt >= Polygon->PointMax) { long diff, old = Polygon->PointMax; Polygon->PointMax = num_pt + step; diff = Polygon->PointMax - old; Polygon->Points = realloc(Polygon->Points, Polygon->PointMax * sizeof(rnd_point_t)); memset(Polygon->Points + old, 0, diff * sizeof(rnd_point_t)); } } void pcb_poly_point_prealloc(pcb_poly_t *Polygon, long num_pt) { pcb_poly_point_prealloc_(Polygon, num_pt, 0); } /* gets the next slot for a point in a polygon struct, allocates memory if necessary */ rnd_point_t *pcb_poly_point_alloc(pcb_poly_t *Polygon) { pcb_poly_point_prealloc_(Polygon, Polygon->PointN, STEP_POLYGONPOINT); return (Polygon->Points + Polygon->PointN++); } /* gets the next slot for a point in a polygon struct, allocates memory if necessary */ rnd_cardinal_t *pcb_poly_holeidx_new(pcb_poly_t *Polygon) { rnd_cardinal_t *holeindex = Polygon->HoleIndex; /* realloc new memory if necessary and clear it */ if (Polygon->HoleIndexN >= Polygon->HoleIndexMax) { Polygon->HoleIndexMax += STEP_POLYGONHOLEINDEX; holeindex = (rnd_cardinal_t *) realloc(holeindex, Polygon->HoleIndexMax * sizeof(int)); Polygon->HoleIndex = holeindex; memset(holeindex + Polygon->HoleIndexN, 0, STEP_POLYGONHOLEINDEX * sizeof(int)); } return (holeindex + Polygon->HoleIndexN++); } /* frees memory used by a polygon */ void pcb_poly_free_fields(pcb_poly_t * polygon) { pcb_parent_t parent; pcb_parenttype_t parent_type; if (polygon == NULL) return; free(polygon->Points); free(polygon->HoleIndex); if (polygon->Clipped) rnd_polyarea_free(&polygon->Clipped); rnd_poly_contours_free(&polygon->NoHoles); /* have to preserve parent info for unreg */ parent = polygon->parent; parent_type = polygon->parent_type; reset_obj_mem(pcb_poly_t, polygon); polygon->parent = parent; polygon->parent_type = parent_type; } /*** utility ***/ /* rotates a polygon in 90 degree steps */ void pcb_poly_rotate90(pcb_poly_t *Polygon, rnd_coord_t X, rnd_coord_t Y, unsigned Number) { pcb_layer_t *layer = Polygon->parent.layer; assert(Polygon->parent_type = PCB_PARENT_LAYER); PCB_POLY_POINT_LOOP(Polygon); { RND_COORD_ROTATE90(point->X, point->Y, X, Y, Number); } PCB_END_LOOP; if (layer->parent_type == PCB_PARENT_DATA) pcb_poly_init_clip(layer->parent.data, layer, Polygon); /* simply rotating the bounding box here won't work, because the box is 'closed' (increased by 1) */ pcb_poly_bbox(Polygon); } void pcb_poly_rotate(pcb_layer_t *layer, pcb_poly_t *polygon, rnd_coord_t X, rnd_coord_t Y, double cosa, double sina) { if (layer->polygon_tree != NULL) rnd_rtree_delete(layer->polygon_tree, polygon, (rnd_rtree_box_t *)polygon); PCB_POLY_POINT_LOOP(polygon); { rnd_rotate(&point->X, &point->Y, X, Y, cosa, sina); } PCB_END_LOOP; if (layer->parent_type == PCB_PARENT_DATA) pcb_poly_init_clip(layer->parent.data, layer, polygon); pcb_poly_bbox(polygon); if (layer->polygon_tree != NULL) rnd_rtree_insert(layer->polygon_tree, polygon, (rnd_rtree_box_t *)polygon); } static rnd_polyarea_t *pcb_poly_is_valid_build(pcb_poly_t *p, int start, int end) { rnd_pline_t *contour = NULL; rnd_polyarea_t *np1 = NULL; rnd_cardinal_t n; rnd_vector_t v; np1 = rnd_polyarea_create(); if (np1 == NULL) return NULL; for (n = start; n < end; n++) { v[0] = p->Points[n].X; v[1] = p->Points[n].Y; if (contour == NULL) { if ((contour = rnd_poly_contour_new(v)) == NULL) goto err; } else { rnd_poly_vertex_include(contour->head->prev, rnd_poly_node_create(v)); } /* Is current point last in contour? If so process it. */ if (n == end - 1) { rnd_poly_contour_pre(contour, rnd_true); if (contour->Count == 0) { rnd_poly_contours_free(&contour); goto err; } { /* count number of not-on-the-same-line vertices to make sure there's more than 2*/ rnd_vnode_t *cur; int r = 0; cur = contour->head; do { r++; } while ((cur = cur->next) != contour->head); if (r < 3) { rnd_poly_contours_free(&contour); goto err; } } /* make sure it is a positive contour (outer) or negative (hole) */ if (contour->Flags.orient != RND_PLF_DIR) { rnd_poly_contour_inv(contour); } rnd_polyarea_contour_include(np1, contour); if (contour->Count == 0) goto err; contour = NULL; if (!rnd_poly_valid(np1)) goto err; } } return np1; err:; rnd_polyarea_free(&np1); return NULL; } int pcb_poly_is_valid(pcb_poly_t *p) { rnd_polyarea_t *np, *hp, *hp2; int valid = 1; /* first make initial polygon contour */ np = pcb_poly_is_valid_build(p, 0, (p->HoleIndexN > 0) ? p->HoleIndex[0] : p->PointN); if (np == NULL) { rnd_polyarea_free(&np); return 0; } if (p->HoleIndexN > 0) { vtp0_t holes = {0}; long h, n; /* build each hole and check if it is a valid contour alone */ vtp0_enlarge(&holes, p->HoleIndexN); for(h = 0; h < p->HoleIndexN; h++) { hp = pcb_poly_is_valid_build(p, p->HoleIndex[h], (h == p->HoleIndexN-1) ? p->PointN : p->HoleIndex[h+1]); vtp0_append(&holes, hp); if (hp == NULL) valid = 0; } /* check if holes do not cross anything */ for(h = 1; h < p->HoleIndexN; h++) { hp = holes.array[h]; if (rnd_polyarea_touching(hp, np)) { valid = 0; goto bad; } for(n = 1; n < h; n++) { hp2 = holes.array[n]; if (rnd_polyarea_touching(hp, hp2)) { valid = 0; goto bad; } } } bad:; /* free all holes */ for(h = 0; h < p->HoleIndexN; h++) { hp = holes.array[h]; if (hp != NULL) rnd_polyarea_free(&hp); } vtp0_uninit(&holes); } rnd_polyarea_free(&np); return valid; } /*** undoable mirror ***/ static const char core_poly_cookie[] = "core-poly"; typedef struct { pcb_poly_t *poly; /* it is safe to save the object pointer because it is persistent (through the removed object list) */ rnd_coord_t y_offs; } undo_poly_mirror_t; static int undo_poly_mirror_swap(void *udata) { undo_poly_mirror_t *g = udata; pcb_layer_t *layer = g->poly->parent.layer; if (layer->polygon_tree != NULL) rnd_rtree_delete(layer->polygon_tree, g->poly, (rnd_rtree_box_t *)g->poly); PCB_POLY_POINT_LOOP(g->poly); { point->X = PCB_SWAP_X(point->X); point->Y = PCB_SWAP_Y(point->Y) + g->y_offs; } PCB_END_LOOP; if (layer->parent_type == PCB_PARENT_DATA) pcb_poly_init_clip(layer->parent.data, layer, g->poly); pcb_poly_bbox(g->poly); if (layer->polygon_tree != NULL) rnd_rtree_insert(layer->polygon_tree, g->poly, (rnd_rtree_box_t *)g->poly); return 0; } static void undo_poly_mirror_print(void *udata, char *dst, size_t dst_len) { undo_poly_mirror_t *g = udata; rnd_snprintf(dst, dst_len, "poly mirror y=%$mm", g->y_offs); } static const uundo_oper_t undo_poly_mirror = { core_poly_cookie, NULL, undo_poly_mirror_swap, undo_poly_mirror_swap, undo_poly_mirror_print }; void pcb_poly_mirror(pcb_poly_t *poly, rnd_coord_t y_offs, rnd_bool undoable) { undo_poly_mirror_t gtmp, *g = >mp; if (undoable) g = pcb_undo_alloc(pcb_data_get_top(poly->parent.layer->parent.data), &undo_poly_mirror, sizeof(undo_poly_mirror_t)); g->poly = poly; g->y_offs = y_offs; undo_poly_mirror_swap(g); if (undoable) pcb_undo_inc_serial(); } void pcb_poly_flip_side(pcb_layer_t *layer, pcb_poly_t *polygon) { rnd_rtree_delete(layer->polygon_tree, polygon, (rnd_rtree_box_t *)polygon); PCB_POLY_POINT_LOOP(polygon); { point->X = PCB_SWAP_X(point->X); point->Y = PCB_SWAP_Y(point->Y); } PCB_END_LOOP; if (layer->parent_type == PCB_PARENT_DATA) pcb_poly_init_clip(layer->parent.data, layer, polygon); pcb_poly_bbox(polygon); rnd_rtree_insert(layer->polygon_tree, polygon, (rnd_rtree_box_t *)polygon); /* hmmm, how to handle clip */ } void pcb_poly_scale(pcb_poly_t *poly, double sx, double sy) { int onbrd = (poly->parent.layer != NULL) && (!poly->parent.layer->is_bound); if (onbrd) pcb_poly_pre(poly); PCB_POLY_POINT_LOOP(poly); { point->X = rnd_round((double)point->X * sx); point->Y = rnd_round((double)point->Y * sy); } PCB_END_LOOP; pcb_poly_bbox(poly); if (onbrd) pcb_poly_post(poly); } /* sets the bounding box of a polygons */ void pcb_poly_bbox(pcb_poly_t *Polygon) { Polygon->bbox_naked.X1 = Polygon->bbox_naked.Y1 = RND_MAX_COORD; Polygon->bbox_naked.X2 = Polygon->bbox_naked.Y2 = -RND_MAX_COORD; PCB_POLY_POINT_LOOP(Polygon); { RND_MAKE_MIN(Polygon->bbox_naked.X1, point->X); RND_MAKE_MIN(Polygon->bbox_naked.Y1, point->Y); RND_MAKE_MAX(Polygon->bbox_naked.X2, point->X); RND_MAKE_MAX(Polygon->bbox_naked.Y2, point->Y); } PCB_END_LOOP; /* clearance is generally considered to be part of the bbox for all objects */ { rnd_coord_t clr = PCB_POLY_HAS_CLEARANCE(Polygon) ? Polygon->Clearance/2 : 0; Polygon->BoundingBox.X1 = Polygon->bbox_naked.X1 - clr; Polygon->BoundingBox.Y1 = Polygon->bbox_naked.Y1 - clr; Polygon->BoundingBox.X2 = Polygon->bbox_naked.X2 + clr; Polygon->BoundingBox.Y2 = Polygon->bbox_naked.Y2 + clr; } /* boxes don't include the lower right corner */ rnd_close_box(&Polygon->bbox_naked); rnd_close_box(&Polygon->BoundingBox); } int pcb_poly_eq(const pcb_host_trans_t *tr1, const pcb_poly_t *p1, const pcb_host_trans_t *tr2, const pcb_poly_t *p2) { if (p1->PointN != p2->PointN) return 0; if (pcb_field_neq(p1, p2, Clearance)) return 0; if (pcb_neqs(p1->term, p2->term)) return 0; if (!PCB_FLAG_TEST(PCB_FLAG_FLOATER, p1) && !PCB_FLAG_TEST(PCB_FLAG_FLOATER, p2)) { rnd_cardinal_t n; for(n = 0; n < p1->PointN; n++) { rnd_coord_t x1, y1, x2, y2; pcb_hash_tr_coords(tr1, &x1, &y1, p1->Points[n].X, p1->Points[n].Y); pcb_hash_tr_coords(tr2, &x2, &y2, p2->Points[n].X, p2->Points[n].Y); if ((x1 != x2) || (y1 != y2)) return 0; } } return 1; } unsigned int pcb_poly_hash(const pcb_host_trans_t *tr, const pcb_poly_t *p) { unsigned int crd = 0; rnd_cardinal_t n; if (!PCB_FLAG_TEST(PCB_FLAG_FLOATER, p)) for(n = 0; n < p->PointN; n++) { rnd_coord_t x, y; pcb_hash_tr_coords(tr, &x, &y, p->Points[n].X, p->Points[n].Y); crd ^= pcb_hash_coord(x) ^ pcb_hash_coord(y); } return pcb_hash_coord(p->Clearance) ^ pcb_hash_str(p->term) ^ crd; } /* creates a new polygon from the old formats rectangle data */ pcb_poly_t *pcb_poly_new_from_rectangle(pcb_layer_t *Layer, rnd_coord_t X1, rnd_coord_t Y1, rnd_coord_t X2, rnd_coord_t Y2, rnd_coord_t Clearance, pcb_flag_t Flags) { pcb_poly_t *polygon = pcb_poly_new(Layer, Clearance, Flags); if (!polygon) return polygon; pcb_poly_point_new(polygon, X1, Y1); pcb_poly_point_new(polygon, X2, Y1); pcb_poly_point_new(polygon, X2, Y2); pcb_poly_point_new(polygon, X1, Y2); pcb_add_poly_on_layer(Layer, polygon); return polygon; } pcb_poly_t *pcb_poly_new_from_poly(pcb_layer_t *Layer, pcb_poly_t *src, rnd_coord_t offs, rnd_coord_t Clearance, pcb_flag_t Flags) { rnd_coord_t x, y; pcb_poly_it_t it; rnd_pline_t *pl; int go; pcb_poly_t *polygon = pcb_poly_new(Layer, Clearance, Flags); if (!polygon) return polygon; it.pa = src->Clipped; pcb_poly_island_first(src, &it); pl = pcb_poly_contour(&it); it.cntr = rnd_pline_dup_offset(pl, offs); for(go = pcb_poly_vect_first(&it, &x, &y); go; go = pcb_poly_vect_next(&it, &x, &y)) pcb_poly_point_new(polygon, x, y); rnd_poly_contours_free(&it.cntr); pcb_add_poly_on_layer(Layer, polygon); return polygon; } void pcb_add_poly_on_layer(pcb_layer_t *Layer, pcb_poly_t *polygon) { pcb_poly_bbox(polygon); if (!Layer->polygon_tree) rnd_rtree_init(Layer->polygon_tree = malloc(sizeof(rnd_rtree_t))); rnd_rtree_insert(Layer->polygon_tree, polygon, (rnd_rtree_box_t *)polygon); PCB_SET_PARENT(polygon, layer, Layer); pcb_poly_clear_from_poly(Layer->parent.data, PCB_OBJ_POLY, Layer, polygon); } /* creates a new polygon on a layer */ pcb_poly_t *pcb_poly_new(pcb_layer_t *Layer, rnd_coord_t Clearance, pcb_flag_t Flags) { pcb_poly_t *polygon = pcb_poly_alloc(Layer); /* copy values */ polygon->Flags = Flags; polygon->Clearance = Clearance; polygon->Clipped = NULL; polygon->NoHoles = NULL; polygon->NoHolesValid = 0; return polygon; } static pcb_poly_t *pcb_poly_copy_meta(pcb_poly_t *dst, pcb_poly_t *src) { if (dst == NULL) return NULL; pcb_attribute_copy_all(&dst->Attributes, &src->Attributes); return dst; } pcb_poly_t *pcb_poly_dup(pcb_layer_t *dst, pcb_poly_t *src) { pcb_board_t *pcb; pcb_poly_t *p = pcb_poly_new(dst, src->Clearance, src->Flags); pcb_poly_copy(p, src, 0, 0); pcb_add_poly_on_layer(dst, p); pcb_poly_copy_meta(p, src); pcb = pcb_data_get_top(dst->parent.data); if (pcb != NULL) pcb_poly_init_clip(pcb->Data, dst, p); return p; } pcb_poly_t *pcb_poly_dup_at(pcb_layer_t *dst, pcb_poly_t *src, rnd_coord_t dx, rnd_coord_t dy) { pcb_board_t *pcb; pcb_poly_t *p = pcb_poly_new(dst, src->Clearance, src->Flags); pcb_poly_copy(p, src, dx, dy); pcb_add_poly_on_layer(dst, p); pcb_poly_copy_meta(p, src); pcb = pcb_data_get_top(dst->parent.data); if (pcb != NULL) pcb_poly_init_clip(pcb->Data, dst, p); return p; } /* creates a new point in a polygon */ rnd_point_t *pcb_poly_point_new(pcb_poly_t *Polygon, rnd_coord_t X, rnd_coord_t Y) { rnd_point_t *point = pcb_poly_point_alloc(Polygon); /* copy values */ point->X = X; point->Y = Y; TODO("ID: register points too") point->ID = pcb_create_ID_get(); return point; } /* creates a new hole in a polygon */ pcb_poly_t *pcb_poly_hole_new(pcb_poly_t * Polygon) { rnd_cardinal_t *holeindex = pcb_poly_holeidx_new(Polygon); *holeindex = Polygon->PointN; return Polygon; } /* copies data from one polygon to another; 'Dest' has to exist */ pcb_poly_t *pcb_poly_copy(pcb_poly_t *Dest, pcb_poly_t *Src, rnd_coord_t dx, rnd_coord_t dy) { rnd_cardinal_t hole = 0; rnd_cardinal_t n; for (n = 0; n < Src->PointN; n++) { if (hole < Src->HoleIndexN && n == Src->HoleIndex[hole]) { pcb_poly_hole_new(Dest); hole++; } pcb_poly_point_new(Dest, Src->Points[n].X+dx, Src->Points[n].Y+dy); } pcb_poly_bbox(Dest); Dest->Flags = Src->Flags; Dest->enforce_clearance = Src->enforce_clearance; PCB_FLAG_CLEAR(PCB_FLAG_FOUND, Dest); return Dest; } static double poly_area(rnd_point_t *points, rnd_cardinal_t n_points) { double area = 0; int n; for(n = 1; n < n_points; n++) area += (double)(points[n-1].X - points[n].X) * (double)(points[n-1].Y + points[n].Y); area +=(double)(points[n_points-1].X - points[0].X) * (double)(points[n_points-1].Y + points[0].Y); if (area > 0) area /= 2.0; else area /= -2.0; return area; } double pcb_poly_area(const pcb_poly_t *poly) { double area; if (poly->HoleIndexN > 0) { int h; area = poly_area(poly->Points, poly->HoleIndex[0]); for(h = 0; h < poly->HoleIndexN - 1; h++) area -= poly_area(poly->Points + poly->HoleIndex[h], poly->HoleIndex[h+1] - poly->HoleIndex[h]); area -= poly_area(poly->Points + poly->HoleIndex[h], poly->PointN - poly->HoleIndex[h]); } else area = poly_area(poly->Points, poly->PointN); return area; } /*** ops ***/ /* copies a polygon to buffer */ void *pcb_polyop_add_to_buffer(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { pcb_poly_t *polygon; rnd_layer_id_t lid = pcb_layer_id(ctx->buffer.src, Layer); pcb_layer_t *layer; /* the buffer may not have the specified layer, e.g. on loose subc subc-aux layer */ if (lid == -1) return NULL; layer = &ctx->buffer.dst->Layer[lid]; polygon = pcb_poly_new(layer, Polygon->Clearance, Polygon->Flags); pcb_poly_copy(polygon, Polygon, 0, 0); pcb_poly_copy_meta(polygon, Polygon); if (ctx->buffer.keep_id) pcb_obj_change_id((pcb_any_obj_t *)polygon, Polygon->ID); /* Update the polygon r-tree. Unlike similarly named functions for * other objects, CreateNewPolygon does not do this as it creates a * skeleton polygon object, which won't have correct bounds. */ if (!layer->polygon_tree) rnd_rtree_init(layer->polygon_tree = malloc(sizeof(rnd_rtree_t))); rnd_rtree_insert(layer->polygon_tree, polygon, (rnd_rtree_box_t *)polygon); PCB_FLAG_CLEAR(PCB_FLAG_FOUND | ctx->buffer.extraflg, polygon); return polygon; } /* moves a polygon between board and buffer. Doesn't allocate memory for the points */ void *pcb_polyop_move_buffer(pcb_opctx_t *ctx, pcb_layer_t *dstly, pcb_poly_t *polygon) { pcb_layer_t *srcly = polygon->parent.layer; assert(polygon->parent_type == PCB_PARENT_LAYER); if ((dstly == NULL) || (dstly == srcly)) { /* auto layer in dst */ rnd_layer_id_t lid = pcb_layer_id(ctx->buffer.src, srcly); if (lid < 0) { rnd_message(RND_MSG_ERROR, "Internal error: can't resolve source layer ID in pcb_polyop_move_buffer\n"); return NULL; } dstly = &ctx->buffer.dst->Layer[lid]; } pcb_poly_pprestore(polygon); rnd_rtree_delete(srcly->polygon_tree, polygon, (rnd_rtree_box_t *)polygon); pcb_poly_unreg(polygon); pcb_poly_reg(dstly, polygon); PCB_FLAG_CLEAR(PCB_FLAG_FOUND, polygon); if (!dstly->polygon_tree) rnd_rtree_init(dstly->polygon_tree = malloc(sizeof(rnd_rtree_t))); rnd_rtree_insert(dstly->polygon_tree, polygon, (rnd_rtree_box_t *)polygon); pcb_poly_ppclear(polygon); return polygon; } void *pcb_polyop_change_thermal(pcb_opctx_t *ctx, pcb_layer_t *ly, pcb_poly_t *poly) { return pcb_anyop_change_thermal(ctx, (pcb_any_obj_t *)poly); } /* Handle attempts to change the clearance of a polygon. */ void *pcb_polyop_change_clear_size(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *poly) { if (PCB_FLAG_TEST(PCB_FLAG_CLEARPOLYPOLY, poly)) { rnd_coord_t value = (ctx->chgsize.is_absolute) ? ctx->chgsize.value : poly->Clearance + ctx->chgsize.value; if (PCB_FLAG_TEST(PCB_FLAG_LOCK, poly)) return NULL; value = MIN(PCB_MAX_THICKNESS, value); if (!ctx->chgsize.is_absolute && ctx->chgsize.value < 0) value = 0; if (value != poly->Clearance) { pcb_undo_add_obj_to_clear_size(PCB_OBJ_POLY, Layer, poly, poly); pcb_poly_restore_to_poly(ctx->chgsize.pcb->Data, PCB_OBJ_POLY, Layer, poly); pcb_poly_invalidate_erase(poly); rnd_rtree_delete(Layer->polygon_tree, poly, (rnd_rtree_box_t *)poly); poly->Clearance = value; pcb_poly_bbox(poly); rnd_rtree_insert(Layer->polygon_tree, poly, (rnd_rtree_box_t *)poly); pcb_poly_clear_from_poly(ctx->chgsize.pcb->Data, PCB_OBJ_POLY, Layer, poly); pcb_poly_invalidate_draw(Layer, poly); return poly; } return poly; } /* poly does not clear other polys */ rnd_message(RND_MSG_WARNING, "This operation had no visible effect.\n" "If you meant this polygon should clear into other polygons, first set the clearpolypoly flag.\n" "To change the clearance of objects within a polygon, change \nthe objects, not the polygon.\n"); rnd_message(RND_MSG_WARNING, "Alternative: Consider using polygon-side clearance\n" " (host polygon's enforce_clearance property)\n" "Alternative: The user script minclrgap can change clearance value to \nmeet a minimum value on all selected objects\n" " see: http://repo.hu/cgi-bin/edakrill.cgi?cmd=show&krill=igor2/script/pcb-rnd/minclrgap.krill\n" "\n\n", NULL); return NULL; } /*** undoable enforce_clearance change ***/ typedef struct { pcb_data_t *data; pcb_poly_t *poly; /* it is safe to save the object pointer because it is persistent (through the removed object list) */ rnd_coord_t enfc; } undo_poly_enfc_t; static int undo_poly_enfc_swap(void *udata) { undo_poly_enfc_t *g = udata; pcb_layer_t *layer = g->poly->parent.layer; rnd_coord_t tmp; pcb_poly_restore_to_poly(g->data, PCB_OBJ_POLY, layer, g->poly); pcb_poly_invalidate_erase(g->poly); if (layer->polygon_tree != NULL) rnd_rtree_delete(layer->polygon_tree, g->poly, (rnd_rtree_box_t *)g->poly); tmp = g->poly->enforce_clearance; g->poly->enforce_clearance = g->enfc; g->enfc = tmp; pcb_poly_bbox(g->poly); if (layer->polygon_tree != NULL) rnd_rtree_insert(layer->polygon_tree, g->poly, (rnd_rtree_box_t *)g->poly); pcb_poly_clear_from_poly(g->data, PCB_OBJ_POLY, layer, g->poly); if (layer->polygon_tree != NULL) pcb_poly_invalidate_draw(layer, g->poly); return 0; } static void undo_poly_enfc_print(void *udata, char *dst, size_t dst_len) { undo_poly_enfc_t *g = udata; rnd_snprintf(dst, dst_len, "poly enforce_clearance=%$mm", g->enfc); } static const uundo_oper_t undo_poly_enfc = { core_poly_cookie, NULL, undo_poly_enfc_swap, undo_poly_enfc_swap, undo_poly_enfc_print }; /* Handle attempts to change the enforce_clearance of a polygon. */ void *pcb_polyop_change_enforce_clear_size(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *poly) { rnd_coord_t value = (ctx->chgsize.is_absolute) ? ctx->chgsize.value : poly->Clearance + ctx->chgsize.value; if (PCB_FLAG_TEST(PCB_FLAG_LOCK, poly)) return NULL; if (!ctx->chgsize.is_absolute && ctx->chgsize.value < 0) value = 0; if (value != poly->enforce_clearance) { undo_poly_enfc_t *g = pcb_undo_alloc(pcb_data_get_top(poly->parent.layer->parent.data), &undo_poly_enfc, sizeof(undo_poly_enfc_t)); g->data = ctx->chgsize.pcb->Data; g->poly = poly; g->enfc = value; undo_poly_enfc_swap(g); pcb_undo_inc_serial(); return poly; } return poly; } /* changes the CLEARPOLY flag of a polygon */ void *pcb_polyop_change_clear(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { if (PCB_FLAG_TEST(PCB_FLAG_LOCK, Polygon)) return NULL; pcb_undo_add_obj_to_clear_poly(PCB_OBJ_POLY, Layer, Polygon, Polygon, rnd_true); pcb_undo_add_obj_to_flag(Polygon); PCB_FLAG_TOGGLE(PCB_FLAG_CLEARPOLY, Polygon); pcb_poly_init_clip(PCB->Data, Layer, Polygon); pcb_poly_invalidate_draw(Layer, Polygon); return Polygon; } /* inserts a point into a polygon */ void *pcb_polyop_insert_point(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { rnd_point_t save; rnd_cardinal_t n; pcb_line_t line; pcb_poly_pprestore(Polygon); if (!ctx->insert.forcible) { /* * first make sure adding the point is sensible */ line.Thickness = 0; line.Point1 = Polygon->Points[pcb_poly_contour_prev_point(Polygon, ctx->insert.idx)]; line.Point2 = Polygon->Points[ctx->insert.idx]; if (pcb_is_point_on_line((float) ctx->insert.x, (float) ctx->insert.y, 0.0, &line)) return NULL; } /* * second, shift the points up to make room for the new point */ pcb_poly_invalidate_erase(Polygon); rnd_rtree_delete(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); save = *pcb_poly_point_new(Polygon, ctx->insert.x, ctx->insert.y); for (n = Polygon->PointN - 1; n > ctx->insert.idx; n--) Polygon->Points[n] = Polygon->Points[n - 1]; /* Shift up indices of any holes */ for (n = 0; n < Polygon->HoleIndexN; n++) if (Polygon->HoleIndex[n] > ctx->insert.idx || (ctx->insert.last && Polygon->HoleIndex[n] == ctx->insert.idx)) Polygon->HoleIndex[n]++; Polygon->Points[ctx->insert.idx] = save; pcb_board_set_changed_flag(PCB, rnd_true); pcb_undo_add_obj_to_insert_point(PCB_OBJ_POLY_POINT, Layer, Polygon, &Polygon->Points[ctx->insert.idx]); pcb_poly_bbox(Polygon); rnd_rtree_insert(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); pcb_poly_init_clip(PCB->Data, Layer, Polygon); if (ctx->insert.forcible || !pcb_poly_remove_excess_points(Layer, Polygon)) pcb_poly_invalidate_draw(Layer, Polygon); pcb_poly_ppclear(Polygon); return (&Polygon->Points[ctx->insert.idx]); } /* changes the clearance flag of a polygon */ void *pcb_polyop_change_join(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *poly) { if (PCB_FLAG_TEST(PCB_FLAG_LOCK, poly)) return NULL; pcb_poly_invalidate_erase(poly); if (PCB_FLAG_TEST(PCB_FLAG_CLEARPOLYPOLY, poly)) { pcb_undo_add_obj_to_clear_poly(PCB_OBJ_POLY, Layer, poly, poly, rnd_false); pcb_poly_restore_to_poly(PCB->Data, PCB_OBJ_POLY, Layer, poly); } pcb_undo_add_obj_to_flag(poly); PCB_FLAG_TOGGLE(PCB_FLAG_CLEARPOLYPOLY, poly); if (PCB_FLAG_TEST(PCB_FLAG_CLEARPOLYPOLY, poly)) { pcb_undo_add_obj_to_clear_poly(PCB_OBJ_POLY, Layer, poly, poly, rnd_true); pcb_poly_clear_from_poly(PCB->Data, PCB_OBJ_POLY, Layer, poly); } pcb_poly_invalidate_draw(Layer, poly); return poly; } /* low level routine to move a polygon */ void pcb_poly_move(pcb_poly_t *Polygon, rnd_coord_t DX, rnd_coord_t DY) { PCB_POLY_POINT_LOOP(Polygon); { RND_MOVE_POINT(point->X, point->Y, DX, DY); } PCB_END_LOOP; RND_BOX_MOVE_LOWLEVEL(&Polygon->BoundingBox, DX, DY); RND_BOX_MOVE_LOWLEVEL(&Polygon->bbox_naked, DX, DY); } /* moves a polygon */ void *pcb_polyop_move_noclip(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { if (Layer->meta.real.vis) { pcb_poly_invalidate_erase(Polygon); } pcb_poly_move(Polygon, ctx->move.dx, ctx->move.dy); pcb_poly_init_clip(PCB->Data, Layer, Polygon); if (Layer->meta.real.vis) pcb_poly_invalidate_draw(Layer, Polygon); return Polygon; } void *pcb_polyop_move(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { rnd_rtree_delete(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); pcb_poly_pprestore(Polygon); pcb_polyop_move_noclip(ctx, Layer, Polygon); rnd_rtree_insert(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); pcb_poly_ppclear(Polygon); return Polygon; } void *pcb_polyop_clip(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { if (ctx->clip.restore) { rnd_rtree_delete(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); pcb_poly_pprestore(Polygon); } if (ctx->clip.clear) { rnd_rtree_insert(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); pcb_poly_ppclear(Polygon); } return Polygon; } /* moves a polygon-point */ void *pcb_polyop_move_point(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon, rnd_point_t *Point) { pcb_poly_pprestore(Polygon); if (Layer->meta.real.vis) { pcb_poly_invalidate_erase(Polygon); } rnd_rtree_delete(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); RND_MOVE_POINT(Point->X, Point->Y, ctx->move.dx, ctx->move.dy); pcb_poly_bbox(Polygon); rnd_rtree_insert(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); pcb_poly_remove_excess_points(Layer, Polygon); pcb_poly_init_clip(PCB->Data, Layer, Polygon); if (Layer->meta.real.vis) pcb_poly_invalidate_draw(Layer, Polygon); pcb_poly_ppclear(Polygon); return Point; } /* moves a polygon between layers; lowlevel routines */ void *pcb_polyop_move_to_layer_low(pcb_opctx_t *ctx, pcb_layer_t * Source, pcb_poly_t * polygon, pcb_layer_t * Destination) { rnd_rtree_delete(Source->polygon_tree, polygon, (rnd_rtree_box_t *)polygon); pcb_poly_pprestore(polygon); pcb_poly_unreg(polygon); pcb_poly_reg(Destination, polygon); if (!Destination->polygon_tree) rnd_rtree_init(Destination->polygon_tree = malloc(sizeof(rnd_rtree_t))); rnd_rtree_insert(Destination->polygon_tree, polygon, (rnd_rtree_box_t *)polygon); pcb_poly_ppclear(polygon); return polygon; } /* moves a polygon between layers */ void *pcb_polyop_move_to_layer(pcb_opctx_t *ctx, pcb_layer_t * Layer, pcb_poly_t * Polygon) { pcb_poly_t *newone; if (PCB_FLAG_TEST(PCB_FLAG_LOCK, Polygon)) { rnd_message(RND_MSG_WARNING, "Sorry, polygon object is locked\n"); return NULL; } if (Layer == ctx->move.dst_layer) return Polygon; pcb_undo_add_obj_to_move_to_layer(PCB_OBJ_POLY, Layer, Polygon, Polygon); if (Layer->meta.real.vis) pcb_poly_invalidate_erase(Polygon); newone = (struct pcb_poly_s *) pcb_polyop_move_to_layer_low(ctx, Layer, Polygon, ctx->move.dst_layer); pcb_poly_init_clip(PCB->Data, ctx->move.dst_layer, newone); if (ctx->move.dst_layer->meta.real.vis) pcb_poly_invalidate_draw(ctx->move.dst_layer, newone); return newone; } /* destroys a polygon from a layer */ void *pcb_polyop_destroy(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { pcb_term_del_auto((pcb_any_obj_t *)Polygon); rnd_rtree_delete(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); pcb_poly_free_fields(Polygon); pcb_poly_free(Polygon); return NULL; } /* removes a polygon-point from a polygon and destroys the data */ void *pcb_polyop_destroy_point(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon, rnd_point_t *Point) { rnd_cardinal_t point_idx; rnd_cardinal_t i; rnd_cardinal_t contour; rnd_cardinal_t contour_start, contour_end, contour_points; pcb_poly_pprestore(Polygon); point_idx = pcb_poly_point_idx(Polygon, Point); contour = pcb_poly_contour_point(Polygon, point_idx); contour_start = (contour == 0) ? 0 : Polygon->HoleIndex[contour - 1]; contour_end = (contour == Polygon->HoleIndexN) ? Polygon->PointN : Polygon->HoleIndex[contour]; contour_points = contour_end - contour_start; if (contour_points <= 3) return pcb_polyop_remove_counter(ctx, Layer, Polygon, contour); rnd_rtree_delete(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); /* remove point from list, keep point order */ for (i = point_idx; i < Polygon->PointN - 1; i++) Polygon->Points[i] = Polygon->Points[i + 1]; Polygon->PointN--; /* Shift down indices of any holes */ for (i = 0; i < Polygon->HoleIndexN; i++) if (Polygon->HoleIndex[i] > point_idx) Polygon->HoleIndex[i]--; pcb_poly_bbox(Polygon); rnd_rtree_insert(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); pcb_poly_init_clip(PCB->Data, Layer, Polygon); pcb_poly_ppclear(Polygon); return Polygon; } /* removes a polygon from a layer */ void *pcb_polyop_remove(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { /* erase from screen */ pcb_poly_pprestore(Polygon); if (Layer->meta.real.vis) pcb_poly_invalidate_erase(Polygon); pcb_undo_move_obj_to_remove(PCB_OBJ_POLY, Layer, Polygon, Polygon); return NULL; } void *pcb_poly_remove(pcb_layer_t *Layer, pcb_poly_t *Polygon) { void *res; pcb_opctx_t ctx; ctx.remove.pcb = PCB; ctx.remove.destroy_target = NULL; res = pcb_polyop_remove(&ctx, Layer, Polygon); pcb_draw(); return res; } /* Removes a contour from a polygon. If removing the outer contour, it removes the whole polygon. */ void *pcb_polyop_remove_counter(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon, rnd_cardinal_t contour) { rnd_cardinal_t contour_start, contour_end, contour_points; rnd_cardinal_t i; if (contour == 0) return pcb_poly_remove(Layer, Polygon); pcb_poly_pprestore(Polygon); if (Layer->meta.real.vis) pcb_poly_invalidate_erase(Polygon); /* Copy the polygon to the undo list */ pcb_undo_add_obj_to_remove_contour(PCB_OBJ_POLY, Layer, Polygon); contour_start = (contour == 0) ? 0 : Polygon->HoleIndex[contour - 1]; contour_end = (contour == Polygon->HoleIndexN) ? Polygon->PointN : Polygon->HoleIndex[contour]; contour_points = contour_end - contour_start; /* remove points from list, keep point order */ for (i = contour_start; i < Polygon->PointN - contour_points; i++) Polygon->Points[i] = Polygon->Points[i + contour_points]; Polygon->PointN -= contour_points; /* remove hole from list and shift down remaining indices */ for (i = contour; i < Polygon->HoleIndexN; i++) Polygon->HoleIndex[i - 1] = Polygon->HoleIndex[i] - contour_points; Polygon->HoleIndexN--; pcb_poly_init_clip(PCB->Data, Layer, Polygon); pcb_poly_ppclear(Polygon); /* redraw polygon if necessary */ if (Layer->meta.real.vis) pcb_poly_invalidate_draw(Layer, Polygon); return NULL; } /* removes a polygon-point from a polygon */ void *pcb_polyop_remove_point(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon, rnd_point_t *Point) { rnd_cardinal_t point_idx; rnd_cardinal_t i; rnd_cardinal_t contour; rnd_cardinal_t contour_start, contour_end, contour_points; pcb_poly_pprestore(Polygon); point_idx = pcb_poly_point_idx(Polygon, Point); contour = pcb_poly_contour_point(Polygon, point_idx); contour_start = (contour == 0) ? 0 : Polygon->HoleIndex[contour - 1]; contour_end = (contour == Polygon->HoleIndexN) ? Polygon->PointN : Polygon->HoleIndex[contour]; contour_points = contour_end - contour_start; if (contour_points <= 3) return pcb_polyop_remove_counter(ctx, Layer, Polygon, contour); if (Layer->meta.real.vis) pcb_poly_invalidate_erase(Polygon); /* insert the polygon-point into the undo list */ pcb_undo_add_obj_to_remove_point(PCB_OBJ_POLY_POINT, Layer, Polygon, point_idx); rnd_rtree_delete(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); /* remove point from list, keep point order */ for (i = point_idx; i < Polygon->PointN - 1; i++) Polygon->Points[i] = Polygon->Points[i + 1]; Polygon->PointN--; /* Shift down indices of any holes */ for (i = 0; i < Polygon->HoleIndexN; i++) if (Polygon->HoleIndex[i] > point_idx) Polygon->HoleIndex[i]--; pcb_poly_bbox(Polygon); rnd_rtree_insert(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); pcb_poly_remove_excess_points(Layer, Polygon); pcb_poly_init_clip(PCB->Data, Layer, Polygon); /* redraw polygon if necessary */ if (Layer->meta.real.vis) pcb_poly_invalidate_draw(Layer, Polygon); pcb_poly_ppclear(Polygon); return NULL; } /* copies a polygon */ void *pcb_polyop_copy(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { pcb_poly_t *polygon; polygon = pcb_poly_new(Layer, Polygon->Clearance, pcb_no_flags()); if (ctx->copy.keep_id) polygon->ID = Polygon->ID; pcb_poly_copy(polygon, Polygon, ctx->copy.DeltaX, ctx->copy.DeltaY); pcb_poly_copy_meta(polygon, Polygon); if (!Layer->polygon_tree) rnd_rtree_init(Layer->polygon_tree = malloc(sizeof(rnd_rtree_t))); rnd_rtree_insert(Layer->polygon_tree, polygon, (rnd_rtree_box_t *)polygon); pcb_poly_init_clip(PCB->Data, Layer, polygon); pcb_poly_invalidate_draw(Layer, polygon); pcb_undo_add_obj_to_create(PCB_OBJ_POLY, Layer, polygon, polygon); pcb_poly_ppclear(polygon); return polygon; } void *pcb_polyop_rotate90(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { pcb_poly_pprestore(Polygon); if (Layer->meta.real.vis) pcb_poly_invalidate_erase(Polygon); if (Layer->polygon_tree != NULL) rnd_rtree_delete(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); pcb_poly_rotate90(Polygon, ctx->rotate.center_x, ctx->rotate.center_y, ctx->rotate.number); if (Layer->polygon_tree != NULL) rnd_rtree_insert(Layer->polygon_tree, Polygon, (rnd_rtree_box_t *)Polygon); pcb_poly_init_clip(PCB->Data, Layer, Polygon); if (Layer->meta.real.vis) pcb_poly_invalidate_draw(Layer, Polygon); pcb_poly_ppclear(Polygon); return Polygon; } void *pcb_polyop_rotate(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { pcb_poly_pprestore(Polygon); if (Layer->meta.real.vis) pcb_poly_invalidate_erase(Polygon); pcb_poly_rotate(Layer, Polygon, ctx->rotate.center_x, ctx->rotate.center_y, ctx->rotate.cosa, ctx->rotate.sina); pcb_poly_init_clip(PCB->Data, Layer, Polygon); if (Layer->meta.real.vis) pcb_poly_invalidate_draw(Layer, Polygon); pcb_poly_ppclear(Polygon); return Polygon; } void *pcb_polyop_change_flag(pcb_opctx_t *ctx, pcb_layer_t *Layer, pcb_poly_t *Polygon) { static pcb_flag_values_t pcb_poly_flags = 0; if (pcb_poly_flags == 0) pcb_poly_flags = pcb_obj_valid_flags(PCB_OBJ_POLY); if ((ctx->chgflag.flag & pcb_poly_flags) != ctx->chgflag.flag) return NULL; if ((ctx->chgflag.flag & PCB_FLAG_TERMNAME) && (Polygon->term == NULL)) return NULL; pcb_undo_add_obj_to_flag(Polygon); if (ctx->chgflag.flag & (PCB_FLAG_CLEARPOLY | PCB_FLAG_CLEARPOLYPOLY)) pcb_poly_restore_to_poly(ctx->chgflag.pcb->Data, PCB_OBJ_POLY, Polygon->parent.layer, Polygon); PCB_FLAG_CHANGE(ctx->chgflag.how, ctx->chgflag.flag, Polygon); if (ctx->chgflag.flag & PCB_FLAG_FULLPOLY) pcb_poly_init_clip(ctx->chgflag.pcb->Data, Polygon->parent.layer, Polygon); if (ctx->chgflag.flag & (PCB_FLAG_CLEARPOLY | PCB_FLAG_CLEARPOLYPOLY)) pcb_poly_clear_from_poly(ctx->chgflag.pcb->Data, PCB_OBJ_POLY, Polygon->parent.layer, Polygon); return Polygon; } void pcb_poly_pre(pcb_poly_t *poly) { pcb_layer_t *ly = pcb_layer_get_real(poly->parent.layer); if (ly == NULL) return; pcb_poly_pprestore(poly); if (ly->polygon_tree != NULL) rnd_rtree_delete(ly->polygon_tree, poly, (rnd_rtree_box_t *)poly); } void pcb_poly_post(pcb_poly_t *poly) { pcb_layer_t *ly = pcb_layer_get_real(poly->parent.layer); if (ly == NULL) return; if (ly->polygon_tree != NULL) rnd_rtree_insert(ly->polygon_tree, poly, (rnd_rtree_box_t *)poly); pcb_poly_ppclear(poly); } /*** iteration helpers ***/ void pcb_poly_map_contours(pcb_poly_t *p, void *ctx, pcb_poly_map_cb_t *cb) { rnd_polyarea_t *pa; rnd_pline_t *pl; pa = p->Clipped; do { int cidx; for(cidx = 0, pl = pa->contours; pl != NULL; cidx++, pl = pl->next) { rnd_vnode_t *v; cb(p, ctx, (cidx == 0 ? PCB_POLYEV_ISLAND_START : PCB_POLYEV_HOLE_START), 0, 0); v = pl->head->next; do { cb(p, ctx, (cidx == 0 ? PCB_POLYEV_ISLAND_POINT : PCB_POLYEV_HOLE_POINT), v->point[0], v->point[1]); } while ((v = v->next) != pl->head->next); cb(p, ctx, (cidx == 0 ? PCB_POLYEV_ISLAND_END : PCB_POLYEV_HOLE_END), 0, 0); } pa = pa->f; } while(pa != p->Clipped); } void *pcb_polyop_invalidate_label(pcb_opctx_t *ctx, pcb_layer_t *layer, pcb_poly_t *poly) { pcb_poly_name_invalidate_draw(poly); return poly; } /*** draw ***/ static rnd_bool is_poly_term_vert(const pcb_poly_t *poly) { rnd_coord_t dx, dy; dx = poly->BoundingBox.X2 - poly->BoundingBox.X1; if (dx < 0) dx = -dx; dy = poly->BoundingBox.Y2 - poly->BoundingBox.Y1; if (dy < 0) dy = -dy; return dx < dy; } void pcb_poly_name_invalidate_draw(pcb_poly_t *poly) { if (poly->term != NULL) { pcb_term_label_invalidate((poly->BoundingBox.X1 + poly->BoundingBox.X2)/2, (poly->BoundingBox.Y1 + poly->BoundingBox.Y2)/2, 100.0, is_poly_term_vert(poly), rnd_true, (pcb_any_obj_t *)poly); } } void pcb_poly_draw_label(pcb_draw_info_t *info, pcb_poly_t *poly, rnd_bool vis_side) { if ((poly->term != NULL) && (vis_side)) pcb_term_label_draw(info, (poly->BoundingBox.X1 + poly->BoundingBox.X2)/2, (poly->BoundingBox.Y1 + poly->BoundingBox.Y2)/2, conf_core.appearance.term_label_size, is_poly_term_vert(poly), rnd_true, (pcb_any_obj_t *)poly); if (poly->noexport && vis_side) pcb_obj_noexport_mark(poly, (poly->BoundingBox.X1 + poly->BoundingBox.X2)/2, (poly->BoundingBox.Y1 + poly->BoundingBox.Y2)/2); if (poly->ind_editpoint) { long n; pcb_obj_editpont_setup(); for(n = 0; n < poly->PointN; n++) pcb_obj_editpont_cross(poly->Points[n].X, poly->Points[n].Y); } } /* Draw an as-drawn contour loop (outline or hole); first point is 'from', last point is 'to'-1. */ static void poly_draw_annot_loop(pcb_poly_t *poly, rnd_cardinal_t from, rnd_cardinal_t to) { rnd_cardinal_t n; for(n = from + 1; n < to; n++) rnd_render->draw_line(pcb_draw_out.fgGC, poly->Points[n-1].X, poly->Points[n-1].Y, poly->Points[n].X, poly->Points[n].Y); rnd_render->draw_line(pcb_draw_out.fgGC, poly->Points[n-1].X, poly->Points[n-1].Y, poly->Points[from].X, poly->Points[from].Y); } void pcb_poly_draw_annotation(pcb_draw_info_t *info, pcb_poly_t *poly) { rnd_cardinal_t n, np; if (!conf_core.editor.as_drawn_poly) return; if (PCB_FLAG_TEST(PCB_FLAG_SELECTED, poly)) rnd_render->set_color(pcb_draw_out.fgGC, &conf_core.appearance.color.selected); else rnd_render->set_color(pcb_draw_out.fgGC, &conf_core.appearance.color.pin_name); rnd_hid_set_line_width(pcb_draw_out.fgGC, -1); rnd_hid_set_line_cap(pcb_draw_out.fgGC, rnd_cap_round); /* draw the outline */ np = (poly->HoleIndexN > 0) ? poly->HoleIndex[0] : poly->PointN; poly_draw_annot_loop(poly, 0, np); /* draw each cutout */ for(n = 0; n < poly->HoleIndexN; n++) { np = ((n+1) < poly->HoleIndexN) ? poly->HoleIndex[n+1] : poly->PointN; poly_draw_annot_loop(poly, poly->HoleIndex[n], np); } } static void pcb_poly_draw_tr_offs(pcb_poly_it_t *it, rnd_coord_t offs) { int go; long len, n; rnd_coord_t x, y; rnd_polo_t *p, p_st[256]; /* calculate length of the polyline */ for(go = pcb_poly_vect_first(it, &x, &y), len = 0; go; go = pcb_poly_vect_next(it, &x, &y)) len++; if (len >= sizeof(p_st) / sizeof(p_st[0])) p = malloc(sizeof(rnd_polo_t) * len); else p = p_st; for(go = pcb_poly_vect_first(it, &x, &y), n = 0; go; go = pcb_poly_vect_next(it, &x, &y), n++) { p[n].x = x; p[n].y = y; } rnd_polo_norms(p, len); rnd_polo_offs(offs, p, len); for(go = pcb_poly_vect_first(it, &x, &y), n = 0; go; go = pcb_poly_vect_next(it, &x, &y), n++) { it->v->point[0] = rnd_round(p[n].x); it->v->point[1] = rnd_round(p[n].y); } if (p != p_st) free(p); } static pcb_poly_t *pcb_poly_draw_tr(pcb_draw_info_t *info, pcb_poly_t *polygon) { pcb_poly_t *np = pcb_poly_alloc(polygon->parent.layer); pcb_poly_it_t it; rnd_polyarea_t *pa; rnd_coord_t offs = info->xform->bloat / 2; pcb_poly_copy(np, polygon, 0, 0); rnd_polyarea_copy0(&np->Clipped, polygon->Clipped); /* iterate over all islands of a polygon */ for(pa = pcb_poly_island_first(np, &it); pa != NULL; pa = pcb_poly_island_next(&it)) { rnd_pline_t *pl; /* check if we have a contour for the given island */ pl = pcb_poly_contour(&it); if (pl == NULL) continue; /* iterate over the vectors of the contour */ pcb_poly_draw_tr_offs(&it, offs); /* iterate over all holes within this island */ for(pl = pcb_poly_hole_first(&it); pl != NULL; pl = pcb_poly_hole_next(&it)) pcb_poly_draw_tr_offs(&it, offs); } return np; } void pcb_poly_draw_(pcb_draw_info_t *info, pcb_poly_t *polygon, int allow_term_gfx) { pcb_poly_t *trpoly = NULL; int draw_lab; if (delayed_terms_enabled && (polygon->term != NULL)) { pcb_draw_delay_obj_add((pcb_any_obj_t *)polygon); return; } if (conf_core.editor.as_drawn_poly) pcb_draw_annotation_add((pcb_any_obj_t *)polygon); if ((info != NULL) && (info->xform != NULL) && (info->xform->bloat != 0)) { /* Slow dupping and recalculation every time; if we ever need this on-screen, we should cache */ trpoly = pcb_poly_draw_tr(info, polygon); polygon = trpoly; } if ((info->xform->thin_draw || info->xform->thin_draw_poly || info->xform->wireframe)) { pcb_dhlp_thindraw_pcb_polygon(pcb_draw_out.fgGC, polygon, info->drawn_area); } else { if ((allow_term_gfx) && pcb_draw_term_need_gfx(polygon) && pcb_draw_term_hid_permission()) { rnd_vnode_t *n, *head; int i; pcb_dhlp_fill_pcb_polygon(pcb_draw_out.active_padGC, polygon, info->drawn_area); head = polygon->Clipped->contours->head; rnd_hid_set_line_cap(pcb_draw_out.fgGC, rnd_cap_square); for(n = head, i = 0; (n != head) || (i == 0); n = n->next, i++) { rnd_coord_t x, y, r; rnd_vector_t v; v[0] = x = (n->prev->point[0] + n->point[0] + n->next->point[0]) / 3; v[1] = y = (n->prev->point[1] + n->point[1] + n->next->point[1]) / 3; if (rnd_poly_contour_inside(polygon->Clipped->contours, v)) { r = PCB_DRAW_TERM_GFX_WIDTH; rnd_hid_set_line_width(pcb_draw_out.fgGC, r); rnd_hid_set_line_cap(pcb_draw_out.fgGC, rnd_cap_square); rnd_render->draw_line(pcb_draw_out.fgGC, x, y, x, y); } } } else pcb_dhlp_fill_pcb_polygon(pcb_draw_out.fgGC, polygon, info->drawn_area); } /* If checking planes, thin-draw any pieces which have been clipped away */ if (info->xform->check_planes && !PCB_FLAG_TEST(PCB_FLAG_FULLPOLY, polygon)) { pcb_poly_t poly = *polygon; for (poly.Clipped = polygon->Clipped->f; poly.Clipped != polygon->Clipped; poly.Clipped = poly.Clipped->f) pcb_dhlp_thindraw_pcb_polygon(pcb_draw_out.fgGC, &poly, info->drawn_area); } draw_lab = (polygon->term != NULL) && ((pcb_draw_force_termlab) || PCB_FLAG_TEST(PCB_FLAG_TERMNAME, polygon)); draw_lab |= polygon->ind_editpoint; if (draw_lab) pcb_draw_delay_label_add((pcb_any_obj_t *)polygon); if (trpoly != NULL) pcb_poly_free(trpoly); } static void pcb_poly_draw(pcb_draw_info_t *info, pcb_poly_t *polygon, int allow_term_gfx) { static const rnd_color_t *color; rnd_color_t buf; const pcb_layer_t *layer = info->layer != NULL ? info->layer : pcb_layer_get_real(polygon->parent.layer); pcb_obj_noexport(info, polygon, return); if (layer == NULL) /* if the layer is inbound, e.g. in preview, fall back using the layer recipe */ layer = polygon->parent.layer; if (info->xform->flag_color && PCB_FLAG_TEST(PCB_FLAG_WARN, polygon)) color = &conf_core.appearance.color.warn; else if (info->xform->flag_color && PCB_FLAG_TEST(PCB_FLAG_SELECTED, polygon)) { if (layer->is_bound) PCB_OBJ_COLOR_ON_BOUND_LAYER(color, layer, 1); else color = &conf_core.appearance.color.selected; } else if (info->xform->flag_color && PCB_FLAG_TEST(PCB_FLAG_FOUND, polygon)) color = &conf_core.appearance.color.connected; else if (info->xform->flag_color && polygon->ind_onpoint) { pcb_lighten_color(color, &buf, 1.75); color = &buf; } else if (PCB_HAS_COLOROVERRIDE(polygon)) { color = polygon->override_color; } else if (layer->is_bound) PCB_OBJ_COLOR_ON_BOUND_LAYER(color, layer, 0); else color = &layer->meta.real.color; rnd_render->set_color(pcb_draw_out.fgGC, color); pcb_poly_draw_(info, polygon, allow_term_gfx); } rnd_rtree_dir_t pcb_poly_draw_callback(void *cl, void *obj, const rnd_rtree_box_t *box) { pcb_draw_info_t *i = cl; pcb_poly_t *polygon = (pcb_poly_t *)obj; if (pcb_hidden_floater((pcb_any_obj_t*)obj, i) || pcb_partial_export((pcb_any_obj_t*)obj, i)) return rnd_RTREE_DIR_FOUND_CONT; if (!polygon->Clipped) return rnd_RTREE_DIR_NOT_FOUND_CONT; if (!PCB->SubcPartsOn && pcb_lobj_parent_subc(polygon->parent_type, &polygon->parent)) return rnd_RTREE_DIR_NOT_FOUND_CONT; pcb_poly_draw(i, polygon, 0); return rnd_RTREE_DIR_FOUND_CONT; } rnd_rtree_dir_t pcb_poly_draw_term_callback(void *cl, void *obj, const rnd_rtree_box_t *box) { pcb_draw_info_t *i = cl; pcb_poly_t *polygon = (pcb_poly_t *)obj; if (pcb_hidden_floater((pcb_any_obj_t*)obj, i) || pcb_partial_export((pcb_any_obj_t*)obj, i)) return rnd_RTREE_DIR_FOUND_CONT; if (polygon->Clipped == NULL) { /* if poly is cleared out of existence, it may still have some annotations visible. Do it the cheap way: if it has term, it's not cleared, so the only thing affected is the as-drawn contour */ if (conf_core.editor.as_drawn_poly) pcb_draw_annotation_add((pcb_any_obj_t *)polygon); return rnd_RTREE_DIR_NOT_FOUND_CONT; } if (!PCB->SubcPartsOn && pcb_lobj_parent_subc(polygon->parent_type, &polygon->parent)) return rnd_RTREE_DIR_NOT_FOUND_CONT; pcb_poly_draw(i, polygon, 1); return rnd_RTREE_DIR_FOUND_CONT; } /* erases a polygon on a layer */ void pcb_poly_invalidate_erase(pcb_poly_t *Polygon) { pcb_draw_invalidate(Polygon); } void pcb_poly_invalidate_draw(pcb_layer_t *Layer, pcb_poly_t *Polygon) { pcb_draw_invalidate(Polygon); } void pcb_poly_uninit(void) { pcb_dhlp_uninit(); }