X-Authentication-Warning: delorie.com: mail set sender to geda-user-bounces using -f X-Recipient: geda-user AT delorie DOT com X-Original-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=date:from:to:subject:message-id:mime-version:content-type; bh=JWGyVToPesoRScZpt80YRl17HCn0wfRO0klcD7/Am4Y=; b=FigsSz8IokZ/wwX/a5wo1td4T+oFuWzhJPLtvHsq7n+wqeVlcc0OVRYOVV0qbs0Rp8 nRT7mXDINVO57eNW2JFRPdl1gQ77CjsSh6Um/Y4dxTQC9ph5R3EnO0hp1ikcQa+IFcGp P6V183DlgSUbTChToeMFgPHTXEnwVKjPhDpsp3leIM5Sucbur4YwPv8tk4FoSz0W7Vng sBx1dFDXomGvJhjy2qQC9zgrm2XOn4SYbU3M2ZLagzuVvQFhMuXxlHIhxRnxWFym2K/9 wKu02RFEhxXL7YUFm5K2egkvs7dR4MVDLbS+a6mzCOpeWik8mGT/jaWTYiH8JxGz/By1 fpVA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:date:from:to:subject:message-id:mime-version :content-type; bh=JWGyVToPesoRScZpt80YRl17HCn0wfRO0klcD7/Am4Y=; b=ed2kIOIHlpUz5gJpS0f3dbLgzV6c+XPzqRZNKlFrZZiYzKWOYPg1VldBqivkDzpD9S sD9IYOIp9zMpwTfMcEOnrsTY5x9WluzGErLvf8iON0BwC/RsMI54mav6q/StHPtenf0C LYPVNCHdDyqLGqfZAskKV5QVnh/waToO+mzEIEqXYS4AH8jEpKJhv9/1GB30uMVIWfJI XrSbZhd5azaadWmw9Au7ePwXryFeJFFnAFcq/KetAA+Ic/WXRmWKoS+D4mFG8pxSIgiS GFR30AumEZbLJbXryaN/MR8M7mH3nZRJZtZgyaNGSZ2p8iLyVQTQPD8bnwWQ4gatNSto Qs2A== X-Gm-Message-State: AG10YOTBVFIEynzJNB0AgJ+KUx9/l1WOCZaMHmzvS3rzAEBLrf/B8DLR7axt3eEPJucfSg== X-Received: by 10.28.148.82 with SMTP id w79mr659431wmd.71.1452892561131; Fri, 15 Jan 2016 13:16:01 -0800 (PST) Date: Fri, 15 Jan 2016 22:15:55 +0100 From: "Nicklas Karlsson (nicklas DOT karlsson17 AT gmail DOT com) [via geda-user AT delorie DOT com]" To: geda-user AT delorie DOT com Subject: [geda-user] pcb DRC and clearance Message-Id: <20160115221555.f0a13b6d52b63876b734d3a3@gmail.com> X-Mailer: Sylpheed 3.5.0beta1 (GTK+ 2.24.25; x86_64-pc-linux-gnu) Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="Multipart=_Fri__15_Jan_2016_22_15_55_+0100_0VnwYXnTWHmfK_18" Reply-To: geda-user AT delorie DOT com This is a multi-part message in MIME format. --Multipart=_Fri__15_Jan_2016_22_15_55_+0100_0VnwYXnTWHmfK_18 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit I had an hour two to spend on DRC check. In the attached file "find." the drcall(...) function was quite long so I broke out the different loops into separate functions. The variables where reused in each loop but I think everything is OK except one row info.flag = SELECTEDFLAG; which I suspect is used for something. Changed file is attached, do anybody if the row info.flag = SELECTEDFLAG; is needed? Interestingly enough the attached source code also contained the name of a rather hot place down under which caught the attention of the anti spam filter. To implement clearance value per net I need to find all copper objects connected to each net. It is possible to select nets with the "PCB Netlist" dialog in pcb. Can anybody give a hint about there the code to find the nets are located? Nicklas Karlsson --Multipart=_Fri__15_Jan_2016_22_15_55_+0100_0VnwYXnTWHmfK_18 Content-Type: text/x-csrc; name="find.c" Content-Disposition: attachment; filename="find.c" Content-Transfer-Encoding: 7bit /*! * \file src/find.c * * \brief Routines to find connections between pins, vias, lines ... * * Short description:\n * * * PV: means pin or via (objects that connect layers).\n * LO: all non PV objects (layer objects like lines, arcs, polygons, * pads). * *
    *
  1. First, the LO or PV at the given coordinates is looked * up.
  2. *
  3. All LO connections to that PV are looked up next.
  4. *
  5. Lookup of all LOs connected to LOs from (2).\n * This step is repeated until no more new connections are found.
  6. *
  7. Lookup all PVs connected to the LOs from (2) and (3).
  8. *
  9. Start again with (1) for all new PVs from (4).
  10. *
* * Intersection of line <--> circle:\n * * * Intersection of line <--> line:\n * * *
* *

Copyright.

\n * * PCB, interactive printed circuit board design * * Copyright (C) 1994,1995,1996, 2005 Thomas Nau * * 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 addresses for paper mail and Email: * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany * Thomas DOT Nau AT rz DOT uni-ulm DOT de */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "global.h" #include "data.h" #include "draw.h" #include "error.h" #include "find.h" #include "misc.h" #include "rtree.h" #include "polygon.h" #include "pcb-printf.h" #include "search.h" #include "set.h" #include "undo.h" #include "rats.h" #ifdef HAVE_LIBDMALLOC #include #endif #undef DEBUG /* --------------------------------------------------------------------------- * some local macros */ #define SEPARATE(FP) \ { \ int i; \ fputc('#', (FP)); \ for (i = Settings.CharPerLine; i; i--) \ fputc('=', (FP)); \ fputc('\n', (FP)); \ } #define LIST_ENTRY(list,I) (((AnyObjectType **)list->Data)[(I)]) #define PADLIST_ENTRY(L,I) (((PadType **)PadList[(L)].Data)[(I)]) #define LINELIST_ENTRY(L,I) (((LineType **)LineList[(L)].Data)[(I)]) #define ARCLIST_ENTRY(L,I) (((ArcType **)ArcList[(L)].Data)[(I)]) #define RATLIST_ENTRY(I) (((RatType **)RatList.Data)[(I)]) #define POLYGONLIST_ENTRY(L,I) (((PolygonType **)PolygonList[(L)].Data)[(I)]) #define PVLIST_ENTRY(I) (((PinType **)PVList.Data)[(I)]) #define IS_PV_ON_RAT(PV, Rat) \ (IsPointOnLineEnd((PV)->X,(PV)->Y, (Rat))) #define IS_PV_ON_ARC(PV, Arc) \ (TEST_FLAG(SQUAREFLAG, (PV)) ? \ IsArcInRectangle( \ (PV)->X -MAX(((PV)->Thickness+1)/2 +Bloat,0), (PV)->Y -MAX(((PV)->Thickness+1)/2 +Bloat,0), \ (PV)->X +MAX(((PV)->Thickness+1)/2 +Bloat,0), (PV)->Y +MAX(((PV)->Thickness+1)/2 +Bloat,0), \ (Arc)) : \ IsPointOnArc((PV)->X,(PV)->Y,MAX((PV)->Thickness/2.0 + Bloat,0.0), (Arc))) #define IS_PV_ON_PAD(PV,Pad) \ ( IsPointInPad((PV)->X, (PV)->Y, MAX((PV)->Thickness/2 +Bloat,0), (Pad))) static DrcViolationType *pcb_drc_violation_new (const char *title, const char *explanation, Coord x, Coord y, Angle angle, bool have_measured, Coord measured_value, Coord required_value, int object_count, long int *object_id_list, int *object_type_list) { DrcViolationType *violation = (DrcViolationType *)malloc (sizeof (DrcViolationType)); violation->title = strdup (title); violation->explanation = strdup (explanation); violation->x = x; violation->y = y; violation->angle = angle; violation->have_measured = have_measured; violation->measured_value = measured_value; violation->required_value = required_value; violation->object_count = object_count; violation->object_id_list = object_id_list; violation->object_type_list = object_type_list; return violation; } static bool checkClearanceAndOverlap(int* nopastecnt); static bool checkLineThin(void); static bool checkArcThin(void); static bool checkPinThin(void); static bool checkPadThin(void); static bool checkViaRingAndDrillSize(void); static bool checkSilkscreeenMinimumWidthOutsideElements(void); static bool checkSilkscreeenMinimumWidthInsideElements(void); static void pcb_drc_violation_free (DrcViolationType *violation) { free (violation->title); free (violation->explanation); free (violation); } static GString *drc_dialog_message; static void reset_drc_dialog_message(void) { if (drc_dialog_message) g_string_free (drc_dialog_message, FALSE); drc_dialog_message = g_string_new (""); if (gui->drc_gui != NULL) { gui->drc_gui->reset_drc_dialog_message (); } } static void append_drc_dialog_message(const char *fmt, ...) { gchar *new_str; va_list ap; va_start (ap, fmt); new_str = pcb_vprintf (fmt, ap); g_string_append (drc_dialog_message, new_str); va_end (ap); g_free (new_str); } static void GotoError (void); static void append_drc_violation (DrcViolationType *violation) { if (gui->drc_gui != NULL) { gui->drc_gui->append_drc_violation (violation); } else { /* Fallback to formatting the violation message as text */ append_drc_dialog_message ("%s\n", violation->title); append_drc_dialog_message (_("%m+near %$mD\n"), Settings.grid_unit->allow, violation->x, violation->y); GotoError (); } if (gui->drc_gui == NULL || gui->drc_gui->log_drc_violations ) { Message (_("WARNING! Design Rule error - %s\n"), violation->title); Message (_("%m+near location %$mD\n"), Settings.grid_unit->allow, violation->x, violation->y); } } #define DRC_CONTINUE _("Press Next to continue DRC checking") #define DRC_NEXT _("Next") #define DRC_CANCEL _("Cancel") /*! * \brief Message when asked about continuing DRC checks after next * violation is found. */ static int throw_drc_dialog(void) { int r; if (gui->drc_gui != NULL) { r = gui->drc_gui->throw_drc_dialog (); } else { /* Fallback to formatting the violation message as text */ append_drc_dialog_message (DRC_CONTINUE); r = gui->confirm_dialog (drc_dialog_message->str, DRC_CANCEL, DRC_NEXT); reset_drc_dialog_message(); } return r; } /*! * \brief Some local types. * * The two 'dummy' structs for PVs and Pads are necessary for creating * connection lists which include the element's name. */ typedef struct { void **Data; /*!< Pointer to index data. */ Cardinal Location, /*!< Currently used position. */ DrawLocation, Number, /*!< Number of objects in list. */ Size; } ListType; /* --------------------------------------------------------------------------- * some local identifiers */ static Coord Bloat = 0; static void *thing_ptr1, *thing_ptr2, *thing_ptr3; static int thing_type; static bool User = false; /*!< User action causing this. */ static bool drc = false; /*!< Whether to stop if finding something not found. */ static Cardinal drcerr_count; /*!< Count of drc errors */ static Cardinal TotalP, TotalV; static ListType LineList[MAX_LAYER], /*!< List of objects to. */ PolygonList[MAX_LAYER], ArcList[MAX_LAYER], PadList[2], RatList, PVList; /* --------------------------------------------------------------------------- * some local prototypes */ static bool LookupLOConnectionsToLine (LineType *, Cardinal, int, bool, bool); static bool LookupLOConnectionsToPad (PadType *, Cardinal, int, bool); static bool LookupLOConnectionsToPolygon (PolygonType *, Cardinal, int, bool); static bool LookupLOConnectionsToArc (ArcType *, Cardinal, int, bool); static bool LookupLOConnectionsToRatEnd (PointType *, Cardinal, int); static bool IsRatPointOnLineEnd (PointType *, LineType *); static bool ArcArcIntersect (ArcType *, ArcType *); static bool PrepareNextLoop (FILE *); static void DrawNewConnections (void); static void DumpList (void); static void LocateError (Coord *, Coord *); static void BuildObjectList (int *, long int **, int **); static bool SetThing (int, void *, void *, void *); static bool IsArcInPolygon (ArcType *, PolygonType *); static bool IsLineInPolygon (LineType *, PolygonType *); static bool IsPadInPolygon (PadType *, PolygonType *); static bool IsPolygonInPolygon (PolygonType *, PolygonType *); /*! * \brief. * * Some of the 'pad' routines are the same as for lines because the 'pad' * struct starts with a line struct. See global.h for details. */ bool LinePadIntersect (LineType *Line, PadType *Pad) { return LineLineIntersect ((Line), (LineType *)Pad); } bool ArcPadIntersect (ArcType *Arc, PadType *Pad) { return LineArcIntersect ((LineType *) (Pad), (Arc)); } static bool add_object_to_list (ListType *list, int type, void *ptr1, void *ptr2, void *ptr3, int flag) { AnyObjectType *object = (AnyObjectType *)ptr2; if (User) AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3); SET_FLAG (flag, object); LIST_ENTRY (list, list->Number) = object; list->Number++; #ifdef DEBUG if (list.Number > list.Size) printf ("add_object_to_list overflow! type=%i num=%d size=%d\n", type, list.Number, list.Size); #endif if (drc && !TEST_FLAG (SELECTEDFLAG, object)) return (SetThing (type, ptr1, ptr2, ptr3)); return false; } static bool ADD_PV_TO_LIST (PinType *Pin, int flag) { return add_object_to_list (&PVList, Pin->Element ? PIN_TYPE : VIA_TYPE, Pin->Element ? Pin->Element : Pin, Pin, Pin, flag); } static bool ADD_PAD_TO_LIST (Cardinal L, PadType *Pad, int flag) { return add_object_to_list (&PadList[L], PAD_TYPE, Pad->Element, Pad, Pad, flag); } static bool ADD_LINE_TO_LIST (Cardinal L, LineType *Ptr, int flag) { return add_object_to_list (&LineList[L], LINE_TYPE, LAYER_PTR (L), Ptr, Ptr, flag); } static bool ADD_ARC_TO_LIST (Cardinal L, ArcType *Ptr, int flag) { return add_object_to_list (&ArcList[L], ARC_TYPE, LAYER_PTR (L), Ptr, Ptr, flag); } static bool ADD_RAT_TO_LIST (RatType *Ptr, int flag) { return add_object_to_list (&RatList, RATLINE_TYPE, Ptr, Ptr, Ptr, flag); } static bool ADD_POLYGON_TO_LIST (Cardinal L, PolygonType *Ptr, int flag) { return add_object_to_list (&PolygonList[L], POLYGON_TYPE, LAYER_PTR (L), Ptr, Ptr, flag); } static BoxType expand_bounds (BoxType *box_in) { BoxType box_out = *box_in; if (Bloat > 0) { box_out.X1 -= Bloat; box_out.X2 += Bloat; box_out.Y1 -= Bloat; box_out.Y2 += Bloat; } return box_out; } bool PinLineIntersect (PinType *PV, LineType *Line) { /* IsLineInRectangle already has Bloat factor */ return TEST_FLAG (SQUAREFLAG, PV) ? IsLineInRectangle (PV->X - (PIN_SIZE (PV) + 1) / 2, PV->Y - (PIN_SIZE (PV) + 1) / 2, PV->X + (PIN_SIZE (PV) + 1) / 2, PV->Y + (PIN_SIZE (PV) + 1) / 2, Line) : IsPointInPad (PV->X, PV->Y, MAX (PIN_SIZE (PV) / 2.0 + Bloat, 0.0), (PadType *)Line); } bool SetThing (int type, void *ptr1, void *ptr2, void *ptr3) { thing_ptr1 = ptr1; thing_ptr2 = ptr2; thing_ptr3 = ptr3; thing_type = type; return true; } bool BoxBoxIntersection (BoxType *b1, BoxType *b2) { if (b2->X2 < b1->X1 || b2->X1 > b1->X2) return false; if (b2->Y2 < b1->Y1 || b2->Y1 > b1->Y2) return false; return true; } static bool PadPadIntersect (PadType *p1, PadType *p2) { return LinePadIntersect ((LineType *) p1, p2); } static inline bool PV_TOUCH_PV (PinType *PV1, PinType *PV2) { double t1, t2; BoxType b1, b2; t1 = MAX (PV1->Thickness / 2.0 + Bloat, 0); t2 = MAX (PV2->Thickness / 2.0 + Bloat, 0); if (IsPointOnPin (PV1->X, PV1->Y, t1, PV2) || IsPointOnPin (PV2->X, PV2->Y, t2, PV1)) return true; if (!TEST_FLAG (SQUAREFLAG, PV1) || !TEST_FLAG (SQUAREFLAG, PV2)) return false; /* check for square/square overlap */ b1.X1 = PV1->X - t1; b1.X2 = PV1->X + t1; b1.Y1 = PV1->Y - t1; b1.Y2 = PV1->Y + t1; t2 = PV2->Thickness / 2.0; b2.X1 = PV2->X - t2; b2.X2 = PV2->X + t2; b2.Y1 = PV2->Y - t2; b2.Y2 = PV2->Y + t2; return BoxBoxIntersection (&b1, &b2); } /*! * \brief Releases all allocated memory. */ static void FreeLayoutLookupMemory (void) { Cardinal i; for (i = 0; i < max_copper_layer; i++) { free (LineList[i].Data); LineList[i].Data = NULL; free (ArcList[i].Data); ArcList[i].Data = NULL; free (PolygonList[i].Data); PolygonList[i].Data = NULL; } free (PVList.Data); PVList.Data = NULL; free (RatList.Data); RatList.Data = NULL; } static void FreeComponentLookupMemory (void) { free (PadList[0].Data); PadList[0].Data = NULL; free (PadList[1].Data); PadList[1].Data = NULL; } /*! * \brief Allocates memory for component related stacks ... * * Initializes index and sorts it by X1 and X2. */ static void InitComponentLookup (void) { Cardinal NumberOfPads[2]; Cardinal i; /* initialize pad data; start by counting the total number * on each of the two possible layers */ NumberOfPads[TOP_SIDE] = NumberOfPads[BOTTOM_SIDE] = 0; ALLPAD_LOOP (PCB->Data); { if (TEST_FLAG (ONSOLDERFLAG, pad)) NumberOfPads[BOTTOM_SIDE]++; else NumberOfPads[TOP_SIDE]++; } ENDALL_LOOP; for (i = 0; i < 2; i++) { /* allocate memory for working list */ PadList[i].Data = (void **)calloc (NumberOfPads[i], sizeof (PadType *)); /* clear some struct members */ PadList[i].Location = 0; PadList[i].DrawLocation = 0; PadList[i].Number = 0; PadList[i].Size = NumberOfPads[i]; } } /*! * \brief Allocates memory for layout related stacks ... * * Initializes index and sorts it by X1 and X2. */ static void InitLayoutLookup (void) { Cardinal i; /* initialize line arc and polygon data */ for (i = 0; i < max_copper_layer; i++) { LayerType *layer = LAYER_PTR (i); if (layer->LineN) { /* allocate memory for line pointer lists */ LineList[i].Data = (void **)calloc (layer->LineN, sizeof (LineType *)); LineList[i].Size = layer->LineN; } if (layer->ArcN) { ArcList[i].Data = (void **)calloc (layer->ArcN, sizeof (ArcType *)); ArcList[i].Size = layer->ArcN; } /* allocate memory for polygon list */ if (layer->PolygonN) { PolygonList[i].Data = (void **)calloc (layer->PolygonN, sizeof (PolygonType *)); PolygonList[i].Size = layer->PolygonN; } /* clear some struct members */ LineList[i].Location = 0; LineList[i].DrawLocation = 0; LineList[i].Number = 0; ArcList[i].Location = 0; ArcList[i].DrawLocation = 0; ArcList[i].Number = 0; PolygonList[i].Location = 0; PolygonList[i].DrawLocation = 0; PolygonList[i].Number = 0; } if (PCB->Data->pin_tree) TotalP = PCB->Data->pin_tree->size; else TotalP = 0; if (PCB->Data->via_tree) TotalV = PCB->Data->via_tree->size; else TotalV = 0; /* allocate memory for 'new PV to check' list and clear struct */ PVList.Data = (void **)calloc (TotalP + TotalV, sizeof (PinType *)); PVList.Size = TotalP + TotalV; PVList.Location = 0; PVList.DrawLocation = 0; PVList.Number = 0; /* Initialize ratline data */ RatList.Data = (void **)calloc (PCB->Data->RatN, sizeof (RatType *)); RatList.Size = PCB->Data->RatN; RatList.Location = 0; RatList.DrawLocation = 0; RatList.Number = 0; } struct pv_info { Cardinal layer; PinType *pv; int flag; jmp_buf env; }; static int LOCtoPVline_callback (const BoxType * b, void *cl) { LineType *line = (LineType *) b; struct pv_info *i = (struct pv_info *) cl; if (!TEST_FLAG (i->flag, line) && PinLineIntersect (i->pv, line) && !TEST_FLAG (HOLEFLAG, i->pv)) { if (ADD_LINE_TO_LIST (i->layer, line, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoPVarc_callback (const BoxType * b, void *cl) { ArcType *arc = (ArcType *) b; struct pv_info *i = (struct pv_info *) cl; if (!TEST_FLAG (i->flag, arc) && IS_PV_ON_ARC (i->pv, arc) && !TEST_FLAG (HOLEFLAG, i->pv)) { if (ADD_ARC_TO_LIST (i->layer, arc, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoPVpad_callback (const BoxType * b, void *cl) { PadType *pad = (PadType *) b; struct pv_info *i = (struct pv_info *) cl; if (!TEST_FLAG (i->flag, pad) && IS_PV_ON_PAD (i->pv, pad) && !TEST_FLAG (HOLEFLAG, i->pv) && ADD_PAD_TO_LIST (TEST_FLAG (ONSOLDERFLAG, pad) ? BOTTOM_SIDE : TOP_SIDE, pad, i->flag)) longjmp (i->env, 1); return 0; } static int LOCtoPVrat_callback (const BoxType * b, void *cl) { RatType *rat = (RatType *) b; struct pv_info *i = (struct pv_info *) cl; if (!TEST_FLAG (i->flag, rat) && IS_PV_ON_RAT (i->pv, rat) && ADD_RAT_TO_LIST (rat, i->flag)) longjmp (i->env, 1); return 0; } static int LOCtoPVpoly_callback (const BoxType * b, void *cl) { PolygonType *polygon = (PolygonType *) b; struct pv_info *i = (struct pv_info *) cl; /* if the pin doesn't have a therm and polygon is clearing * then it can't touch due to clearance, so skip the expensive * test. If it does have a therm, you still need to test * because it might not be inside the polygon, or it could * be on an edge such that it doesn't actually touch. */ if (!TEST_FLAG (i->flag, polygon) && !TEST_FLAG (HOLEFLAG, i->pv) && (TEST_THERM (i->layer, i->pv) || !TEST_FLAG (CLEARPOLYFLAG, polygon) || !i->pv->Clearance)) { double wide = MAX (0.5 * i->pv->Thickness + Bloat, 0); if (TEST_FLAG (SQUAREFLAG, i->pv)) { Coord x1 = i->pv->X - (i->pv->Thickness + 1 + Bloat) / 2; Coord x2 = i->pv->X + (i->pv->Thickness + 1 + Bloat) / 2; Coord y1 = i->pv->Y - (i->pv->Thickness + 1 + Bloat) / 2; Coord y2 = i->pv->Y + (i->pv->Thickness + 1 + Bloat) / 2; if (IsRectangleInPolygon (x1, y1, x2, y2, polygon) && ADD_POLYGON_TO_LIST (i->layer, polygon, i->flag)) longjmp (i->env, 1); } else if (TEST_FLAG (OCTAGONFLAG, i->pv)) { POLYAREA *oct = OctagonPoly (i->pv->X, i->pv->Y, i->pv->Thickness / 2); if (isects (oct, polygon, true) && ADD_POLYGON_TO_LIST (i->layer, polygon, i->flag)) longjmp (i->env, 1); } else if (IsPointInPolygon (i->pv->X, i->pv->Y, wide, polygon) && ADD_POLYGON_TO_LIST (i->layer, polygon, i->flag)) longjmp (i->env, 1); } return 0; } /*! * \brief Checks if a PV is connected to LOs, if it is, the LO is added * to the appropriate list and the 'used' flag is set. */ static bool LookupLOConnectionsToPVList (int flag, bool AndRats) { Cardinal layer_no; struct pv_info info; info.flag = flag; /* loop over all PVs currently on list */ while (PVList.Location < PVList.Number) { BoxType search_box; /* get pointer to data */ info.pv = PVLIST_ENTRY (PVList.Location); search_box = expand_bounds (&info.pv->BoundingBox); /* check pads */ if (setjmp (info.env) == 0) r_search (PCB->Data->pad_tree, &search_box, NULL, LOCtoPVpad_callback, &info); else return true; /* now all lines, arcs and polygons of the several layers */ for (layer_no = 0; layer_no < max_copper_layer; layer_no++) { LayerType *layer = LAYER_PTR (layer_no); if (layer->no_drc) continue; info.layer = layer_no; /* add touching lines */ if (setjmp (info.env) == 0) r_search (layer->line_tree, &search_box, NULL, LOCtoPVline_callback, &info); else return true; /* add touching arcs */ if (setjmp (info.env) == 0) r_search (layer->arc_tree, &search_box, NULL, LOCtoPVarc_callback, &info); else return true; /* check all polygons */ if (setjmp (info.env) == 0) r_search (layer->polygon_tree, &search_box, NULL, LOCtoPVpoly_callback, &info); else return true; } /* Check for rat-lines that may intersect the PV */ if (AndRats) { if (setjmp (info.env) == 0) r_search (PCB->Data->rat_tree, &search_box, NULL, LOCtoPVrat_callback, &info); else return true; } PVList.Location++; } return false; } /*! * \brief Find all connections between LO at the current list position * and new LOs. */ static bool LookupLOConnectionsToLOList (int flag, bool AndRats) { bool done; Cardinal i, group, layer, ratposition, lineposition[MAX_LAYER], polyposition[MAX_LAYER], arcposition[MAX_LAYER], padposition[2]; /* copy the current LO list positions; the original data is changed * by 'LookupPVConnectionsToLOList()' which has to check the same * list entries plus the new ones */ for (i = 0; i < max_copper_layer; i++) { lineposition[i] = LineList[i].Location; polyposition[i] = PolygonList[i].Location; arcposition[i] = ArcList[i].Location; } for (i = 0; i < 2; i++) padposition[i] = PadList[i].Location; ratposition = RatList.Location; /* loop over all new LOs in the list; recurse until no * more new connections in the layergroup were found */ do { Cardinal *position; if (AndRats) { position = &ratposition; for (; *position < RatList.Number; (*position)++) { group = RATLIST_ENTRY (*position)->group1; if (LookupLOConnectionsToRatEnd (&(RATLIST_ENTRY (*position)->Point1), group, flag)) return (true); group = RATLIST_ENTRY (*position)->group2; if (LookupLOConnectionsToRatEnd (&(RATLIST_ENTRY (*position)->Point2), group, flag)) return (true); } } /* loop over all layergroups */ for (group = 0; group < max_group; group++) { Cardinal entry; for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++) { layer = PCB->LayerGroups.Entries[group][entry]; /* be aware that the layer number equal max_copper_layer * and max_copper_layer+1 have a special meaning for pads */ if (layer < max_copper_layer) { /* try all new lines */ position = &lineposition[layer]; for (; *position < LineList[layer].Number; (*position)++) if (LookupLOConnectionsToLine (LINELIST_ENTRY (layer, *position), group, flag, true, AndRats)) return (true); /* try all new arcs */ position = &arcposition[layer]; for (; *position < ArcList[layer].Number; (*position)++) if (LookupLOConnectionsToArc (ARCLIST_ENTRY (layer, *position), group, flag, AndRats)) return (true); /* try all new polygons */ position = &polyposition[layer]; for (; *position < PolygonList[layer].Number; (*position)++) if (LookupLOConnectionsToPolygon (POLYGONLIST_ENTRY (layer, *position), group, flag, AndRats)) return (true); } else { /* try all new pads */ layer -= max_copper_layer; if (layer > 1) { Message (_("bad layer number %d max_copper_layer=%d in find.c\n"), layer, max_copper_layer); return false; } position = &padposition[layer]; for (; *position < PadList[layer].Number; (*position)++) if (LookupLOConnectionsToPad (PADLIST_ENTRY (layer, *position), group, flag, AndRats)) return (true); } } } /* check if all lists are done; Later for-loops * may have changed the prior lists */ done = !AndRats || ratposition >= RatList.Number; done = done && padposition[0] >= PadList[0].Number && padposition[1] >= PadList[1].Number; for (layer = 0; layer < max_copper_layer; layer++) done = done && lineposition[layer] >= LineList[layer].Number && arcposition[layer] >= ArcList[layer].Number && polyposition[layer] >= PolygonList[layer].Number; } while (!done); return (false); } static int pv_pv_callback (const BoxType * b, void *cl) { PinType *pin = (PinType *) b; struct pv_info *i = (struct pv_info *) cl; if (!TEST_FLAG (i->flag, pin) && PV_TOUCH_PV (i->pv, pin)) { if (TEST_FLAG (HOLEFLAG, pin) || TEST_FLAG (HOLEFLAG, i->pv)) { SET_FLAG (WARNFLAG, pin); Settings.RatWarn = true; if (pin->Element) Message (_("WARNING: Hole too close to pin.\n")); else Message (_("WARNING: Hole too close to via.\n")); } else if (ADD_PV_TO_LIST (pin, i->flag)) longjmp (i->env, 1); } return 0; } /*! * \brief Searches for new PVs that are connected to PVs on the list. */ static bool LookupPVConnectionsToPVList (int flag) { Cardinal save_place; struct pv_info info; info.flag = flag; /* loop over all PVs on list */ save_place = PVList.Location; while (PVList.Location < PVList.Number) { BoxType search_box; /* get pointer to data */ info.pv = PVLIST_ENTRY (PVList.Location); search_box = expand_bounds ((BoxType *)info.pv); if (setjmp (info.env) == 0) r_search (PCB->Data->via_tree, &search_box, NULL, pv_pv_callback, &info); else return true; if (setjmp (info.env) == 0) r_search (PCB->Data->pin_tree, &search_box, NULL, pv_pv_callback, &info); else return true; PVList.Location++; } PVList.Location = save_place; return (false); } struct lo_info { Cardinal layer; LineType *line; PadType *pad; ArcType *arc; PolygonType *polygon; RatType *rat; int flag; jmp_buf env; }; static int pv_line_callback (const BoxType * b, void *cl) { PinType *pv = (PinType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, pv) && PinLineIntersect (pv, i->line)) { if (TEST_FLAG (HOLEFLAG, pv)) { SET_FLAG (WARNFLAG, pv); Settings.RatWarn = true; Message (_("WARNING: Hole too close to line.\n")); } else if (ADD_PV_TO_LIST (pv, i->flag)) longjmp (i->env, 1); } return 0; } static int pv_pad_callback (const BoxType * b, void *cl) { PinType *pv = (PinType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, pv) && IS_PV_ON_PAD (pv, i->pad)) { if (TEST_FLAG (HOLEFLAG, pv)) { SET_FLAG (WARNFLAG, pv); Settings.RatWarn = true; Message (_("WARNING: Hole too close to pad.\n")); } else if (ADD_PV_TO_LIST (pv, i->flag)) longjmp (i->env, 1); } return 0; } static int pv_arc_callback (const BoxType * b, void *cl) { PinType *pv = (PinType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, pv) && IS_PV_ON_ARC (pv, i->arc)) { if (TEST_FLAG (HOLEFLAG, pv)) { SET_FLAG (WARNFLAG, pv); Settings.RatWarn = true; Message (_("WARNING: Hole touches arc.\n")); } else if (ADD_PV_TO_LIST (pv, i->flag)) longjmp (i->env, 1); } return 0; } static int pv_poly_callback (const BoxType * b, void *cl) { PinType *pv = (PinType *) b; struct lo_info *i = (struct lo_info *) cl; /* note that holes in polygons are ok, so they don't generate warnings. */ if (!TEST_FLAG (i->flag, pv) && !TEST_FLAG (HOLEFLAG, pv) && (TEST_THERM (i->layer, pv) || !TEST_FLAG (CLEARPOLYFLAG, i->polygon) || !pv->Clearance)) { if (TEST_FLAG (SQUAREFLAG, pv)) { Coord x1, x2, y1, y2; x1 = pv->X - (PIN_SIZE (pv) + 1 + Bloat) / 2; x2 = pv->X + (PIN_SIZE (pv) + 1 + Bloat) / 2; y1 = pv->Y - (PIN_SIZE (pv) + 1 + Bloat) / 2; y2 = pv->Y + (PIN_SIZE (pv) + 1 + Bloat) / 2; if (IsRectangleInPolygon (x1, y1, x2, y2, i->polygon) && ADD_PV_TO_LIST (pv, i->flag)) longjmp (i->env, 1); } else if (TEST_FLAG (OCTAGONFLAG, pv)) { POLYAREA *oct = OctagonPoly (pv->X, pv->Y, PIN_SIZE (pv) / 2); if (isects (oct, i->polygon, true) && ADD_PV_TO_LIST (pv, i->flag)) longjmp (i->env, 1); } else { if (IsPointInPolygon (pv->X, pv->Y, PIN_SIZE (pv) * 0.5 + Bloat, i->polygon) && ADD_PV_TO_LIST (pv, i->flag)) longjmp (i->env, 1); } } return 0; } static int pv_rat_callback (const BoxType * b, void *cl) { PinType *pv = (PinType *) b; struct lo_info *i = (struct lo_info *) cl; /* rats can't cause DRC so there is no early exit */ if (!TEST_FLAG (i->flag, pv) && IS_PV_ON_RAT (pv, i->rat)) ADD_PV_TO_LIST (pv, i->flag); return 0; } /*! * \brief Searches for new PVs that are connected to NEW LOs on the list. * * This routine updates the position counter of the lists too. */ static bool LookupPVConnectionsToLOList (int flag, bool AndRats) { Cardinal layer_no; struct lo_info info; info.flag = flag; /* loop over all layers */ for (layer_no = 0; layer_no < max_copper_layer; layer_no++) { LayerType *layer = LAYER_PTR (layer_no); if (layer->no_drc) continue; /* do nothing if there are no PV's */ if (TotalP + TotalV == 0) { LineList[layer_no].Location = LineList[layer_no].Number; ArcList[layer_no].Location = ArcList[layer_no].Number; PolygonList[layer_no].Location = PolygonList[layer_no].Number; continue; } /* check all lines */ while (LineList[layer_no].Location < LineList[layer_no].Number) { BoxType search_box; info.line = LINELIST_ENTRY (layer_no, LineList[layer_no].Location); search_box = expand_bounds ((BoxType *)info.line); if (setjmp (info.env) == 0) r_search (PCB->Data->via_tree, &search_box, NULL, pv_line_callback, &info); else return true; if (setjmp (info.env) == 0) r_search (PCB->Data->pin_tree, &search_box, NULL, pv_line_callback, &info); else return true; LineList[layer_no].Location++; } /* check all arcs */ while (ArcList[layer_no].Location < ArcList[layer_no].Number) { BoxType search_box; info.arc = ARCLIST_ENTRY (layer_no, ArcList[layer_no].Location); search_box = expand_bounds ((BoxType *)info.arc); if (setjmp (info.env) == 0) r_search (PCB->Data->via_tree, &search_box, NULL, pv_arc_callback, &info); else return true; if (setjmp (info.env) == 0) r_search (PCB->Data->pin_tree, &search_box, NULL, pv_arc_callback, &info); else return true; ArcList[layer_no].Location++; } /* now all polygons */ info.layer = layer_no; while (PolygonList[layer_no].Location < PolygonList[layer_no].Number) { BoxType search_box; info.polygon = POLYGONLIST_ENTRY (layer_no, PolygonList[layer_no].Location); search_box = expand_bounds ((BoxType *)info.polygon); if (setjmp (info.env) == 0) r_search (PCB->Data->via_tree, &search_box, NULL, pv_poly_callback, &info); else return true; if (setjmp (info.env) == 0) r_search (PCB->Data->pin_tree, &search_box, NULL, pv_poly_callback, &info); else return true; PolygonList[layer_no].Location++; } } /* loop over all pad-layers */ for (layer_no = 0; layer_no < 2; layer_no++) { /* do nothing if there are no PV's */ if (TotalP + TotalV == 0) { PadList[layer_no].Location = PadList[layer_no].Number; continue; } /* check all pads; for a detailed description see * the handling of lines in this subroutine */ while (PadList[layer_no].Location < PadList[layer_no].Number) { BoxType search_box; info.pad = PADLIST_ENTRY (layer_no, PadList[layer_no].Location); search_box = expand_bounds ((BoxType *)info.pad); if (setjmp (info.env) == 0) r_search (PCB->Data->via_tree, &search_box, NULL, pv_pad_callback, &info); else return true; if (setjmp (info.env) == 0) r_search (PCB->Data->pin_tree, &search_box, NULL, pv_pad_callback, &info); else return true; PadList[layer_no].Location++; } } /* do nothing if there are no PV's */ if (TotalP + TotalV == 0) RatList.Location = RatList.Number; /* check all rat-lines */ if (AndRats) { while (RatList.Location < RatList.Number) { info.rat = RATLIST_ENTRY (RatList.Location); r_search_pt (PCB->Data->via_tree, & info.rat->Point1, 1, NULL, pv_rat_callback, &info); r_search_pt (PCB->Data->via_tree, & info.rat->Point2, 1, NULL, pv_rat_callback, &info); r_search_pt (PCB->Data->pin_tree, & info.rat->Point1, 1, NULL, pv_rat_callback, &info); r_search_pt (PCB->Data->pin_tree, & info.rat->Point2, 1, NULL, pv_rat_callback, &info); RatList.Location++; } } return (false); } /*! * \brief Reduce arc start angle and delta to 0..360. */ static void normalize_angles (Angle *sa, Angle *d) { if (*d < 0) { *sa += *d; *d = - *d; } if (*d > 360) /* full circle */ *d = 360; *sa = NormalizeAngle (*sa); } static int radius_crosses_arc (double x, double y, ArcType *arc) { double alpha = atan2 (y - arc->Y, -x + arc->X) * RAD_TO_DEG; Angle sa = arc->StartAngle, d = arc->Delta; normalize_angles (&sa, &d); if (alpha < 0) alpha += 360; if (sa <= alpha) return (sa + d) >= alpha; return (sa + d - 360) >= alpha; } static void get_arc_ends (Coord *box, ArcType *arc) { box[0] = arc->X - arc->Width * cos (M180 * arc->StartAngle); box[1] = arc->Y + arc->Height * sin (M180 * arc->StartAngle); box[2] = arc->X - arc->Width * cos (M180 * (arc->StartAngle + arc->Delta)); box[3] = arc->Y + arc->Height * sin (M180 * (arc->StartAngle + arc->Delta)); } /*! * \brief Check if two arcs intersect. * * First we check for circle intersections, * then find the actual points of intersection * and test them to see if they are on arcs. * * Consider a, the distance from the center of arc 1 * to the point perpendicular to the intersecting points. * * \ta = (r1^2 - r2^2 + l^2)/(2l) * * The perpendicular distance to the point of intersection * is then: * * \td = sqrt(r1^2 - a^2) * * The points of intersection would then be: * * \tx = X1 + a/l dx +- d/l dy * * \ty = Y1 + a/l dy -+ d/l dx * * Where dx = X2 - X1 and dy = Y2 - Y1. */ static bool ArcArcIntersect (ArcType *Arc1, ArcType *Arc2) { double x, y, dx, dy, r1, r2, a, d, l, t, t1, t2, dl; Coord pdx, pdy; Coord box[8]; t = 0.5 * Arc1->Thickness + Bloat; t2 = 0.5 * Arc2->Thickness; t1 = t2 + Bloat; /* too thin arc */ if (t < 0 || t1 < 0) return false; /* try the end points first */ get_arc_ends (&box[0], Arc1); get_arc_ends (&box[4], Arc2); if (IsPointOnArc (box[0], box[1], t, Arc2) || IsPointOnArc (box[2], box[3], t, Arc2) || IsPointOnArc (box[4], box[5], t, Arc1) || IsPointOnArc (box[6], box[7], t, Arc1)) return true; pdx = Arc2->X - Arc1->X; pdy = Arc2->Y - Arc1->Y; dl = Distance (Arc1->X, Arc1->Y, Arc2->X, Arc2->Y); /* concentric arcs, simpler intersection conditions */ if (dl < 0.5) { if ((Arc1->Width - t >= Arc2->Width - t2 && Arc1->Width - t <= Arc2->Width + t2) || (Arc1->Width + t >= Arc2->Width - t2 && Arc1->Width + t <= Arc2->Width + t2)) { Angle sa1 = Arc1->StartAngle, d1 = Arc1->Delta; Angle sa2 = Arc2->StartAngle, d2 = Arc2->Delta; /* NB the endpoints have already been checked, so we just compare the angles */ normalize_angles (&sa1, &d1); normalize_angles (&sa2, &d2); /* sa1 == sa2 was caught when checking endpoints */ if (sa1 > sa2) if (sa1 < sa2 + d2 || sa1 + d1 - 360 > sa2) return true; if (sa2 > sa1) if (sa2 < sa1 + d1 || sa2 + d2 - 360 > sa1) return true; } return false; } r1 = Arc1->Width; r2 = Arc2->Width; /* arcs centerlines are too far or too near */ if (dl > r1 + r2 || dl + r1 < r2 || dl + r2 < r1) { /* check the nearest to the other arc's center point */ dx = pdx * r1 / dl; dy = pdy * r1 / dl; if (dl + r1 < r2) /* Arc1 inside Arc2 */ { dx = - dx; dy = - dy; } if (radius_crosses_arc (Arc1->X + dx, Arc1->Y + dy, Arc1) && IsPointOnArc (Arc1->X + dx, Arc1->Y + dy, t, Arc2)) return true; dx = - pdx * r2 / dl; dy = - pdy * r2 / dl; if (dl + r2 < r1) /* Arc2 inside Arc1 */ { dx = - dx; dy = - dy; } if (radius_crosses_arc (Arc2->X + dx, Arc2->Y + dy, Arc2) && IsPointOnArc (Arc2->X + dx, Arc2->Y + dy, t1, Arc1)) return true; return false; } l = dl * dl; r1 *= r1; r2 *= r2; a = 0.5 * (r1 - r2 + l) / l; r1 = r1 / l; d = r1 - a * a; /* the circles are too far apart to touch or probably just touch: check the nearest point */ if (d < 0) d = 0; else d = sqrt (d); x = Arc1->X + a * pdx; y = Arc1->Y + a * pdy; dx = d * pdx; dy = d * pdy; if (radius_crosses_arc (x + dy, y - dx, Arc1) && IsPointOnArc (x + dy, y - dx, t, Arc2)) return true; if (radius_crosses_arc (x + dy, y - dx, Arc2) && IsPointOnArc (x + dy, y - dx, t1, Arc1)) return true; if (radius_crosses_arc (x - dy, y + dx, Arc1) && IsPointOnArc (x - dy, y + dx, t, Arc2)) return true; if (radius_crosses_arc (x - dy, y + dx, Arc2) && IsPointOnArc (x - dy, y + dx, t1, Arc1)) return true; return false; } /*! * \brief Tests if point is same as line end point. */ static bool IsRatPointOnLineEnd (PointType *Point, LineType *Line) { if ((Point->X == Line->Point1.X && Point->Y == Line->Point1.Y) || (Point->X == Line->Point2.X && Point->Y == Line->Point2.Y)) return (true); return (false); } /*! * \brief Writes vertices of a squared line. */ static void form_slanted_rectangle (PointType p[4], LineType *l) { double dwx = 0, dwy = 0; if (l->Point1.Y == l->Point2.Y) dwx = l->Thickness / 2.0; else if (l->Point1.X == l->Point2.X) dwy = l->Thickness / 2.0; else { Coord dX = l->Point2.X - l->Point1.X; Coord dY = l->Point2.Y - l->Point1.Y; double r = Distance (l->Point1.X, l->Point1.Y, l->Point2.X, l->Point2.Y); dwx = l->Thickness / 2.0 / r * dX; dwy = l->Thickness / 2.0 / r * dY; } p[0].X = l->Point1.X - dwx + dwy; p[0].Y = l->Point1.Y - dwy - dwx; p[1].X = l->Point1.X - dwx - dwy; p[1].Y = l->Point1.Y - dwy + dwx; p[2].X = l->Point2.X + dwx - dwy; p[2].Y = l->Point2.Y + dwy + dwx; p[3].X = l->Point2.X + dwx + dwy; p[3].Y = l->Point2.Y + dwy - dwx; } /*! * \brief Checks if two lines intersect. * *
 * From news FAQ:
 *
 * Let A,B,C,D be 2-space position vectors.
 *
 * Then the directed line segments AB & CD are given by:
 *
 *      AB=A+r(B-A), r in [0,1]
 *
 *      CD=C+s(D-C), s in [0,1]
 *
 * If AB & CD intersect, then
 *
 *      A+r(B-A)=C+s(D-C), or
 *
 *      XA+r(XB-XA)=XC+s*(XD-XC)
 *
 *      YA+r(YB-YA)=YC+s(YD-YC)  for some r,s in [0,1]
 *
 * Solving the above for r and s yields
 *
 *          (YA-YC)(XD-XC)-(XA-XC)(YD-YC)
 *      r = -----------------------------  (eqn 1)
 *          (XB-XA)(YD-YC)-(YB-YA)(XD-XC)
 *
 *          (YA-YC)(XB-XA)-(XA-XC)(YB-YA)
 *      s = -----------------------------  (eqn 2)
 *          (XB-XA)(YD-YC)-(YB-YA)(XD-XC)
 *
 * Let I be the position vector of the intersection point, then:
 *
 *      I=A+r(B-A) or
 *
 *      XI=XA+r(XB-XA)
 *
 *      YI=YA+r(YB-YA)
 *
 * By examining the values of r & s, you can also determine some
 * other limiting conditions:
 *
 *      If 0<=r<=1 & 0<=s<=1, intersection exists
 *
 *          r<0 or r>1 or s<0 or s>1 line segments do not intersect
 *
 *      If the denominator in eqn 1 is zero, AB & CD are parallel
 *
 *      If the numerator in eqn 1 is also zero, AB & CD are coincident
 *
 * If the intersection point of the 2 lines are needed (lines in this
 * context mean infinite lines) regardless whether the two line
 * segments intersect, then
 *
 *      If r>1, I is located on extension of AB
 *      If r<0, I is located on extension of BA
 *      If s>1, I is located on extension of CD
 *      If s<0, I is located on extension of DC
 *
 * Also note that the denominators of eqn 1 & 2 are identical.
 * 
*/ bool LineLineIntersect (LineType *Line1, LineType *Line2) { double s, r; double line1_dx, line1_dy, line2_dx, line2_dy, point1_dx, point1_dy; if (TEST_FLAG (SQUAREFLAG, Line1))/* pretty reckless recursion */ { PointType p[4]; form_slanted_rectangle (p, Line1); return IsLineInQuadrangle (p, Line2); } /* here come only round Line1 because IsLineInQuadrangle() calls LineLineIntersect() with first argument rounded*/ if (TEST_FLAG (SQUAREFLAG, Line2)) { PointType p[4]; form_slanted_rectangle (p, Line2); return IsLineInQuadrangle (p, Line1); } /* now all lines are round */ /* Check endpoints: this provides a quick exit, catches * cases where the "real" lines don't intersect but the * thick lines touch, and ensures that the dx/dy business * below does not cause a divide-by-zero. */ if (IsPointInPad (Line2->Point1.X, Line2->Point1.Y, MAX (Line2->Thickness / 2 + Bloat, 0), (PadType *) Line1) || IsPointInPad (Line2->Point2.X, Line2->Point2.Y, MAX (Line2->Thickness / 2 + Bloat, 0), (PadType *) Line1) || IsPointInPad (Line1->Point1.X, Line1->Point1.Y, MAX (Line1->Thickness / 2 + Bloat, 0), (PadType *) Line2) || IsPointInPad (Line1->Point2.X, Line1->Point2.Y, MAX (Line1->Thickness / 2 + Bloat, 0), (PadType *) Line2)) return true; /* setup some constants */ line1_dx = Line1->Point2.X - Line1->Point1.X; line1_dy = Line1->Point2.Y - Line1->Point1.Y; line2_dx = Line2->Point2.X - Line2->Point1.X; line2_dy = Line2->Point2.Y - Line2->Point1.Y; point1_dx = Line1->Point1.X - Line2->Point1.X; point1_dy = Line1->Point1.Y - Line2->Point1.Y; /* If either line is a point, we have failed already, since the * endpoint check above will have caught an "intersection". */ if ((line1_dx == 0 && line1_dy == 0) || (line2_dx == 0 && line2_dy == 0)) return false; /* set s to cross product of Line1 and the line * Line1.Point1--Line2.Point1 (as vectors) */ s = point1_dy * line1_dx - point1_dx * line1_dy; /* set r to cross product of both lines (as vectors) */ r = line1_dx * line2_dy - line1_dy * line2_dx; /* No cross product means parallel lines, or maybe Line2 is * zero-length. In either case, since we did a bounding-box * check before getting here, the above IsPointInPad() checks * will have caught any intersections. */ if (r == 0.0) return false; s /= r; r = (point1_dy * line2_dx - point1_dx * line2_dy) / r; /* intersection is at least on AB */ if (r >= 0.0 && r <= 1.0) return (s >= 0.0 && s <= 1.0); /* intersection is at least on CD */ /* [removed this case since it always returns false --asp] */ return false; } /*! * \brief Check for line intersection with an arc. * * Mostly this is like the circle/line intersection * found in IsPointOnLine (search.c) see the detailed * discussion for the basics there. * * Since this is only an arc, not a full circle we need * to find the actual points of intersection with the * circle, and see if they are on the arc. * * To do this, we translate along the line from the point Q * plus or minus a distance delta = sqrt(Radius^2 - d^2) * but it's handy to normalize with respect to l, the line * length so a single projection is done (e.g. we don't first * find the point Q. * *
 * The projection is now of the form:
 *
 *      Px = X1 + (r +- r2)(X2 - X1)
 *      Py = Y1 + (r +- r2)(Y2 - Y1)
 * 
* * Where r2 sqrt(Radius^2 l^2 - d^2)/l^2 * note that this is the variable d, not the symbol d described in * IsPointOnLine (variable d = symbol d * l). * * The end points are so they are checked individually. */ bool LineArcIntersect (LineType *Line, ArcType *Arc) { double dx, dy, dx1, dy1, l, d, r, r2, Radius; BoxType *box; dx = Line->Point2.X - Line->Point1.X; dy = Line->Point2.Y - Line->Point1.Y; dx1 = Line->Point1.X - Arc->X; dy1 = Line->Point1.Y - Arc->Y; l = dx * dx + dy * dy; d = dx * dy1 - dy * dx1; d *= d; /* use the larger diameter circle first */ Radius = Arc->Width + MAX (0.5 * (Arc->Thickness + Line->Thickness) + Bloat, 0.0); Radius *= Radius; r2 = Radius * l - d; /* projection doesn't even intersect circle when r2 < 0 */ if (r2 < 0) return (false); /* check the ends of the line in case the projected point */ /* of intersection is beyond the line end */ if (IsPointOnArc (Line->Point1.X, Line->Point1.Y, MAX (0.5 * Line->Thickness + Bloat, 0.0), Arc)) return (true); if (IsPointOnArc (Line->Point2.X, Line->Point2.Y, MAX (0.5 * Line->Thickness + Bloat, 0.0), Arc)) return (true); if (l == 0.0) return (false); r2 = sqrt (r2); Radius = -(dx * dx1 + dy * dy1); r = (Radius + r2) / l; if (r >= 0 && r <= 1 && IsPointOnArc (Line->Point1.X + r * dx, Line->Point1.Y + r * dy, MAX (0.5 * Line->Thickness + Bloat, 0.0), Arc)) return (true); r = (Radius - r2) / l; if (r >= 0 && r <= 1 && IsPointOnArc (Line->Point1.X + r * dx, Line->Point1.Y + r * dy, MAX (0.5 * Line->Thickness + Bloat, 0.0), Arc)) return (true); /* check arc end points */ box = GetArcEnds (Arc); if (IsPointInPad (box->X1, box->Y1, Arc->Thickness * 0.5 + Bloat, (PadType *)Line)) return true; if (IsPointInPad (box->X2, box->Y2, Arc->Thickness * 0.5 + Bloat, (PadType *)Line)) return true; return false; } static int LOCtoArcLine_callback (const BoxType * b, void *cl) { LineType *line = (LineType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, line) && LineArcIntersect (line, i->arc)) { if (ADD_LINE_TO_LIST (i->layer, line, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoArcArc_callback (const BoxType * b, void *cl) { ArcType *arc = (ArcType *) b; struct lo_info *i = (struct lo_info *) cl; if (!arc->Thickness) return 0; if (!TEST_FLAG (i->flag, arc) && ArcArcIntersect (i->arc, arc)) { if (ADD_ARC_TO_LIST (i->layer, arc, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoArcPad_callback (const BoxType * b, void *cl) { PadType *pad = (PadType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, pad) && i->layer == (TEST_FLAG (ONSOLDERFLAG, pad) ? BOTTOM_SIDE : TOP_SIDE) && ArcPadIntersect (i->arc, pad) && ADD_PAD_TO_LIST (i->layer, pad, i->flag)) longjmp (i->env, 1); return 0; } /*! * \brief Searches all LOs that are connected to the given arc on the * given layergroup. * * All found connections are added to the list. * * The notation that is used is:\n * Xij means Xj at arc i. */ static bool LookupLOConnectionsToArc (ArcType *Arc, Cardinal LayerGroup, int flag, bool AndRats) { Cardinal entry; struct lo_info info; BoxType search_box; info.flag = flag; info.arc = Arc; search_box = expand_bounds ((BoxType *)info.arc); /* loop over all layers of the group */ for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) { Cardinal layer_no; LayerType *layer; GList *i; layer_no = PCB->LayerGroups.Entries[LayerGroup][entry]; layer = LAYER_PTR (layer_no); /* handle normal layers */ if (layer_no < max_copper_layer) { info.layer = layer_no; /* add arcs */ if (setjmp (info.env) == 0) r_search (layer->line_tree, &search_box, NULL, LOCtoArcLine_callback, &info); else return true; if (setjmp (info.env) == 0) r_search (layer->arc_tree, &search_box, NULL, LOCtoArcArc_callback, &info); else return true; /* now check all polygons */ for (i = layer->Polygon; i != NULL; i = g_list_next (i)) { PolygonType *polygon = i->data; if (!TEST_FLAG (flag, polygon) && IsArcInPolygon (Arc, polygon) && ADD_POLYGON_TO_LIST (layer_no, polygon, flag)) return true; } } else { info.layer = layer_no - max_copper_layer; if (setjmp (info.env) == 0) r_search (PCB->Data->pad_tree, &search_box, NULL, LOCtoArcPad_callback, &info); else return true; } } return (false); } static int LOCtoLineLine_callback (const BoxType * b, void *cl) { LineType *line = (LineType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, line) && LineLineIntersect (i->line, line)) { if (ADD_LINE_TO_LIST (i->layer, line, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoLineArc_callback (const BoxType * b, void *cl) { ArcType *arc = (ArcType *) b; struct lo_info *i = (struct lo_info *) cl; if (!arc->Thickness) return 0; if (!TEST_FLAG (i->flag, arc) && LineArcIntersect (i->line, arc)) { if (ADD_ARC_TO_LIST (i->layer, arc, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoLineRat_callback (const BoxType * b, void *cl) { RatType *rat = (RatType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, rat)) { if ((rat->group1 == i->layer) && IsRatPointOnLineEnd (&rat->Point1, i->line)) { if (ADD_RAT_TO_LIST (rat, i->flag)) longjmp (i->env, 1); } else if ((rat->group2 == i->layer) && IsRatPointOnLineEnd (&rat->Point2, i->line)) { if (ADD_RAT_TO_LIST (rat, i->flag)) longjmp (i->env, 1); } } return 0; } static int LOCtoLinePad_callback (const BoxType * b, void *cl) { PadType *pad = (PadType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, pad) && i->layer == (TEST_FLAG (ONSOLDERFLAG, pad) ? BOTTOM_SIDE : TOP_SIDE) && LinePadIntersect (i->line, pad) && ADD_PAD_TO_LIST (i->layer, pad, i->flag)) longjmp (i->env, 1); return 0; } /*! * \brief Searches all LOs that are connected to the given line on the * given layergroup. * * All found connections are added to the list. * * The notation that is used is: * Xij means Xj at line i. */ static bool LookupLOConnectionsToLine (LineType *Line, Cardinal LayerGroup, int flag, bool PolysTo, bool AndRats) { Cardinal entry; struct lo_info info; BoxType search_box; info.flag = flag; info.layer = LayerGroup; info.line = Line; search_box = expand_bounds ((BoxType *)info.line); if (AndRats) { /* add the new rat lines */ if (setjmp (info.env) == 0) r_search (PCB->Data->rat_tree, &search_box, NULL, LOCtoLineRat_callback, &info); else return true; } /* loop over all layers of the group */ for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) { Cardinal layer_no; LayerType *layer; layer_no = PCB->LayerGroups.Entries[LayerGroup][entry]; layer = LAYER_PTR (layer_no); /* handle normal layers */ if (layer_no < max_copper_layer) { info.layer = layer_no; /* add lines */ if (setjmp (info.env) == 0) r_search (layer->line_tree, &search_box, NULL, LOCtoLineLine_callback, &info); else return true; /* add arcs */ if (setjmp (info.env) == 0) r_search (layer->arc_tree, &search_box, NULL, LOCtoLineArc_callback, &info); else return true; /* now check all polygons */ if (PolysTo) { GList *i; for (i = layer->Polygon; i != NULL; i = g_list_next (i)) { PolygonType *polygon = i->data; if (!TEST_FLAG (flag, polygon) && IsLineInPolygon (Line, polygon) && ADD_POLYGON_TO_LIST (layer_no, polygon, flag)) return true; } } } else { /* handle special 'pad' layers */ info.layer = layer_no - max_copper_layer; if (setjmp (info.env) == 0) r_search (PCB->Data->pad_tree, &search_box, NULL, LOCtoLinePad_callback, &info); else return true; } } return (false); } struct rat_info { Cardinal layer; PointType *Point; int flag; jmp_buf env; }; static int LOCtoRat_callback (const BoxType * b, void *cl) { LineType *line = (LineType *) b; struct rat_info *i = (struct rat_info *) cl; if (!TEST_FLAG (i->flag, line) && ((line->Point1.X == i->Point->X && line->Point1.Y == i->Point->Y) || (line->Point2.X == i->Point->X && line->Point2.Y == i->Point->Y))) { if (ADD_LINE_TO_LIST (i->layer, line, i->flag)) longjmp (i->env, 1); } return 0; } static int PolygonToRat_callback (const BoxType * b, void *cl) { PolygonType *polygon = (PolygonType *) b; struct rat_info *i = (struct rat_info *) cl; if (!TEST_FLAG (i->flag, polygon) && polygon->Clipped && (i->Point->X == polygon->Clipped->contours->head.point[0]) && (i->Point->Y == polygon->Clipped->contours->head.point[1])) { if (ADD_POLYGON_TO_LIST (i->layer, polygon, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoPad_callback (const BoxType * b, void *cl) { PadType *pad = (PadType *) b; struct rat_info *i = (struct rat_info *) cl; if (!TEST_FLAG (i->flag, pad) && i->layer == (TEST_FLAG (ONSOLDERFLAG, pad) ? BOTTOM_SIDE : TOP_SIDE) && ((pad->Point1.X == i->Point->X && pad->Point1.Y == i->Point->Y) || (pad->Point2.X == i->Point->X && pad->Point2.Y == i->Point->Y) || ((pad->Point1.X + pad->Point2.X) / 2 == i->Point->X && (pad->Point1.Y + pad->Point2.Y) / 2 == i->Point->Y)) && ADD_PAD_TO_LIST (i->layer, pad, i->flag)) longjmp (i->env, 1); return 0; } /*! * \brief Searches all LOs that are connected to the given rat-line on * the given layergroup. * * All found connections are added to the list. * * The notation that is used is: * Xij means Xj at line i. */ static bool LookupLOConnectionsToRatEnd (PointType *Point, Cardinal LayerGroup, int flag) { Cardinal entry; struct rat_info info; info.flag = flag; info.Point = Point; /* loop over all layers of this group */ for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) { Cardinal layer_no; LayerType *layer; layer_no = PCB->LayerGroups.Entries[LayerGroup][entry]; layer = LAYER_PTR (layer_no); /* handle normal layers rats don't ever touch arcs by definition */ if (layer_no < max_copper_layer) { info.layer = layer_no; if (setjmp (info.env) == 0) r_search_pt (layer->line_tree, Point, 1, NULL, LOCtoRat_callback, &info); else return true; if (setjmp (info.env) == 0) r_search_pt (layer->polygon_tree, Point, 1, NULL, PolygonToRat_callback, &info); } else { /* handle special 'pad' layers */ info.layer = layer_no - max_copper_layer; if (setjmp (info.env) == 0) r_search_pt (PCB->Data->pad_tree, Point, 1, NULL, LOCtoPad_callback, &info); else return true; } } return (false); } static int LOCtoPadLine_callback (const BoxType * b, void *cl) { LineType *line = (LineType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, line) && LinePadIntersect (line, i->pad)) { if (ADD_LINE_TO_LIST (i->layer, line, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoPadArc_callback (const BoxType * b, void *cl) { ArcType *arc = (ArcType *) b; struct lo_info *i = (struct lo_info *) cl; if (!arc->Thickness) return 0; if (!TEST_FLAG (i->flag, arc) && ArcPadIntersect (arc, i->pad)) { if (ADD_ARC_TO_LIST (i->layer, arc, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoPadPoly_callback (const BoxType * b, void *cl) { PolygonType *polygon = (PolygonType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, polygon) && (!TEST_FLAG (CLEARPOLYFLAG, polygon) || !i->pad->Clearance)) { if (IsPadInPolygon (i->pad, polygon) && ADD_POLYGON_TO_LIST (i->layer, polygon, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoPadRat_callback (const BoxType * b, void *cl) { RatType *rat = (RatType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, rat)) { if (rat->group1 == i->layer && ((rat->Point1.X == i->pad->Point1.X && rat->Point1.Y == i->pad->Point1.Y) || (rat->Point1.X == i->pad->Point2.X && rat->Point1.Y == i->pad->Point2.Y) || (rat->Point1.X == (i->pad->Point1.X + i->pad->Point2.X) / 2 && rat->Point1.Y == (i->pad->Point1.Y + i->pad->Point2.Y) / 2))) { if (ADD_RAT_TO_LIST (rat, i->flag)) longjmp (i->env, 1); } else if (rat->group2 == i->layer && ((rat->Point2.X == i->pad->Point1.X && rat->Point2.Y == i->pad->Point1.Y) || (rat->Point2.X == i->pad->Point2.X && rat->Point2.Y == i->pad->Point2.Y) || (rat->Point2.X == (i->pad->Point1.X + i->pad->Point2.X) / 2 && rat->Point2.Y == (i->pad->Point1.Y + i->pad->Point2.Y) / 2))) { if (ADD_RAT_TO_LIST (rat, i->flag)) longjmp (i->env, 1); } } return 0; } static int LOCtoPadPad_callback (const BoxType * b, void *cl) { PadType *pad = (PadType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, pad) && i->layer == (TEST_FLAG (ONSOLDERFLAG, pad) ? BOTTOM_SIDE : TOP_SIDE) && PadPadIntersect (pad, i->pad) && ADD_PAD_TO_LIST (i->layer, pad, i->flag)) longjmp (i->env, 1); return 0; } /*! * \brief Searches all LOs that are connected to the given pad on the * given layergroup. * * All found connections are added to the list. */ static bool LookupLOConnectionsToPad (PadType *Pad, Cardinal LayerGroup, int flag, bool AndRats) { Cardinal entry; struct lo_info info; BoxType search_box; if (!TEST_FLAG (SQUAREFLAG, Pad)) return (LookupLOConnectionsToLine ((LineType *) Pad, LayerGroup, flag, false, AndRats)); info.flag = flag; info.pad = Pad; search_box = expand_bounds ((BoxType *)info.pad); /* add the new rat lines */ info.layer = LayerGroup; if (AndRats) { if (setjmp (info.env) == 0) r_search (PCB->Data->rat_tree, &search_box, NULL, LOCtoPadRat_callback, &info); else return true; } /* loop over all layers of the group */ for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) { Cardinal layer_no; LayerType *layer; layer_no = PCB->LayerGroups.Entries[LayerGroup][entry]; layer = LAYER_PTR (layer_no); /* handle normal layers */ if (layer_no < max_copper_layer) { info.layer = layer_no; /* add lines */ if (setjmp (info.env) == 0) r_search (layer->line_tree, &search_box, NULL, LOCtoPadLine_callback, &info); else return true; /* add arcs */ if (setjmp (info.env) == 0) r_search (layer->arc_tree, &search_box, NULL, LOCtoPadArc_callback, &info); else return true; /* add polygons */ if (setjmp (info.env) == 0) r_search (layer->polygon_tree, &search_box, NULL, LOCtoPadPoly_callback, &info); else return true; } else { /* handle special 'pad' layers */ info.layer = layer_no - max_copper_layer; if (setjmp (info.env) == 0) r_search (PCB->Data->pad_tree, &search_box, NULL, LOCtoPadPad_callback, &info); else return true; } } return (false); } static int LOCtoPolyLine_callback (const BoxType * b, void *cl) { LineType *line = (LineType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, line) && IsLineInPolygon (line, i->polygon)) { if (ADD_LINE_TO_LIST (i->layer, line, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoPolyArc_callback (const BoxType * b, void *cl) { ArcType *arc = (ArcType *) b; struct lo_info *i = (struct lo_info *) cl; if (!arc->Thickness) return 0; if (!TEST_FLAG (i->flag, arc) && IsArcInPolygon (arc, i->polygon)) { if (ADD_ARC_TO_LIST (i->layer, arc, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoPolyPad_callback (const BoxType * b, void *cl) { PadType *pad = (PadType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, pad) && i->layer == (TEST_FLAG (ONSOLDERFLAG, pad) ? BOTTOM_SIDE : TOP_SIDE) && IsPadInPolygon (pad, i->polygon)) { if (ADD_PAD_TO_LIST (i->layer, pad, i->flag)) longjmp (i->env, 1); } return 0; } static int LOCtoPolyRat_callback (const BoxType * b, void *cl) { RatType *rat = (RatType *) b; struct lo_info *i = (struct lo_info *) cl; if (!TEST_FLAG (i->flag, rat)) { if ((rat->Point1.X == (i->polygon->Clipped->contours->head.point[0]) && rat->Point1.Y == (i->polygon->Clipped->contours->head.point[1]) && rat->group1 == i->layer) || (rat->Point2.X == (i->polygon->Clipped->contours->head.point[0]) && rat->Point2.Y == (i->polygon->Clipped->contours->head.point[1]) && rat->group2 == i->layer)) if (ADD_RAT_TO_LIST (rat, i->flag)) longjmp (i->env, 1); } return 0; } /*! * \brief Looks up LOs that are connected to the given polygon on the * given layergroup. * * All found connections are added to the list. */ static bool LookupLOConnectionsToPolygon (PolygonType *Polygon, Cardinal LayerGroup, int flag, bool AndRats) { Cardinal entry; struct lo_info info; BoxType search_box; if (!Polygon->Clipped) return false; info.flag = flag; info.polygon = Polygon; search_box = expand_bounds ((BoxType *)info.polygon); info.layer = LayerGroup; /* check rats */ if (AndRats) { if (setjmp (info.env) == 0) r_search (PCB->Data->rat_tree, &search_box, NULL, LOCtoPolyRat_callback, &info); else return true; } /* loop over all layers of the group */ for (entry = 0; entry < PCB->LayerGroups.Number[LayerGroup]; entry++) { Cardinal layer_no; LayerType *layer; layer_no = PCB->LayerGroups.Entries[LayerGroup][entry]; layer = LAYER_PTR (layer_no); /* handle normal layers */ if (layer_no < max_copper_layer) { GList *i; /* check all polygons */ for (i = layer->Polygon; i != NULL; i = g_list_next (i)) { PolygonType *polygon = i->data; if (!TEST_FLAG (flag, polygon) && IsPolygonInPolygon (polygon, Polygon) && ADD_POLYGON_TO_LIST (layer_no, polygon, flag)) return true; } info.layer = layer_no; /* check all lines */ if (setjmp (info.env) == 0) r_search (layer->line_tree, &search_box, NULL, LOCtoPolyLine_callback, &info); else return true; /* check all arcs */ if (setjmp (info.env) == 0) r_search (layer->arc_tree, &search_box, NULL, LOCtoPolyArc_callback, &info); else return true; } else { info.layer = layer_no - max_copper_layer; if (setjmp (info.env) == 0) r_search (PCB->Data->pad_tree, &search_box, NULL, LOCtoPolyPad_callback, &info); else return true; } } return (false); } /*! * \brief Checks if an arc has a connection to a polygon. * * - first check if the arc can intersect with the polygon by * evaluating the bounding boxes. * - check the two end points of the arc. If none of them matches * - check all segments of the polygon against the arc. */ static bool IsArcInPolygon (ArcType *Arc, PolygonType *Polygon) { BoxType *Box = (BoxType *) Arc; /* arcs with clearance never touch polys */ if (TEST_FLAG (CLEARPOLYFLAG, Polygon) && TEST_FLAG (CLEARLINEFLAG, Arc)) return false; if (!Polygon->Clipped) return false; if (Box->X1 <= Polygon->Clipped->contours->xmax + Bloat && Box->X2 >= Polygon->Clipped->contours->xmin - Bloat && Box->Y1 <= Polygon->Clipped->contours->ymax + Bloat && Box->Y2 >= Polygon->Clipped->contours->ymin - Bloat) { POLYAREA *ap; if (!(ap = ArcPoly (Arc, Arc->Thickness + Bloat))) return false; /* error */ return isects (ap, Polygon, true); } return false; } /*! * \brief Checks if a line has a connection to a polygon. * * - first check if the line can intersect with the polygon by * evaluating the bounding boxes * - check the two end points of the line. If none of them matches * - check all segments of the polygon against the line. */ static bool IsLineInPolygon (LineType *Line, PolygonType *Polygon) { BoxType *Box = (BoxType *) Line; POLYAREA *lp; /* lines with clearance never touch polygons */ if (TEST_FLAG (CLEARPOLYFLAG, Polygon) && TEST_FLAG (CLEARLINEFLAG, Line)) return false; if (!Polygon->Clipped) return false; if (TEST_FLAG(SQUAREFLAG,Line)&&(Line->Point1.X==Line->Point2.X||Line->Point1.Y==Line->Point2.Y)) { Coord wid = (Line->Thickness + Bloat + 1) / 2; Coord x1, x2, y1, y2; x1 = MIN (Line->Point1.X, Line->Point2.X) - wid; y1 = MIN (Line->Point1.Y, Line->Point2.Y) - wid; x2 = MAX (Line->Point1.X, Line->Point2.X) + wid; y2 = MAX (Line->Point1.Y, Line->Point2.Y) + wid; return IsRectangleInPolygon (x1, y1, x2, y2, Polygon); } if (Box->X1 <= Polygon->Clipped->contours->xmax + Bloat && Box->X2 >= Polygon->Clipped->contours->xmin - Bloat && Box->Y1 <= Polygon->Clipped->contours->ymax + Bloat && Box->Y2 >= Polygon->Clipped->contours->ymin - Bloat) { if (!(lp = LinePoly (Line, Line->Thickness + Bloat))) return FALSE; /* error */ return isects (lp, Polygon, true); } return false; } /*! * \brief Checks if a pad connects to a non-clearing polygon. * * The polygon is assumed to already have been proven non-clearing. */ static bool IsPadInPolygon (PadType *pad, PolygonType *polygon) { return IsLineInPolygon ((LineType *) pad, polygon); } /*! * \brief Checks if a polygon has a connection to a second one. * * First check all points out of P1 against P2 and vice versa. * If both fail check all lines of P1 against the ones of P2. */ static bool IsPolygonInPolygon (PolygonType *P1, PolygonType *P2) { if (!P1->Clipped || !P2->Clipped) return false; assert (P1->Clipped->contours); assert (P2->Clipped->contours); /* first check if both bounding boxes intersect. If not, return quickly */ if (P1->Clipped->contours->xmin - Bloat > P2->Clipped->contours->xmax || P1->Clipped->contours->xmax + Bloat < P2->Clipped->contours->xmin || P1->Clipped->contours->ymin - Bloat > P2->Clipped->contours->ymax || P1->Clipped->contours->ymax + Bloat < P2->Clipped->contours->ymin) return false; /* first check un-bloated case */ if (isects (P1->Clipped, P2, false)) return TRUE; /* now the difficult case of bloated */ if (Bloat > 0) { PLINE *c; for (c = P1->Clipped->contours; c; c = c->next) { LineType line; VNODE *v = &c->head; if (c->xmin - Bloat <= P2->Clipped->contours->xmax && c->xmax + Bloat >= P2->Clipped->contours->xmin && c->ymin - Bloat <= P2->Clipped->contours->ymax && c->ymax + Bloat >= P2->Clipped->contours->ymin) { line.Point1.X = v->point[0]; line.Point1.Y = v->point[1]; line.Thickness = 2 * Bloat; line.Clearance = 0; line.Flags = NoFlags (); for (v = v->next; v != &c->head; v = v->next) { line.Point2.X = v->point[0]; line.Point2.Y = v->point[1]; SetLineBoundingBox (&line); if (IsLineInPolygon (&line, P2)) return (true); line.Point1.X = line.Point2.X; line.Point1.Y = line.Point2.Y; } } } } return (false); } /*! * \brief Writes the several names of an element to a file. */ static void PrintElementNameList (ElementType *Element, FILE * FP) { static DynamicStringType cname, pname, vname; CreateQuotedString (&cname, (char *)EMPTY (DESCRIPTION_NAME (Element))); CreateQuotedString (&pname, (char *)EMPTY (NAMEONPCB_NAME (Element))); CreateQuotedString (&vname, (char *)EMPTY (VALUE_NAME (Element))); fprintf (FP, "(%s %s %s)\n", cname.Data, pname.Data, vname.Data); } /*! * \brief Writes the several names of an element to a file. */ static void PrintConnectionElementName (ElementType *Element, FILE * FP) { fputs ("Element", FP); PrintElementNameList (Element, FP); fputs ("{\n", FP); } /*! * \brief Prints one {pin,pad,via}/element entry of connection lists. */ static void PrintConnectionListEntry (char *ObjName, ElementType *Element, bool FirstOne, FILE * FP) { static DynamicStringType oname; CreateQuotedString (&oname, ObjName); if (FirstOne) fprintf (FP, "\t%s\n\t{\n", oname.Data); else { fprintf (FP, "\t\t%s ", oname.Data); if (Element) PrintElementNameList (Element, FP); else fputs ("(__VIA__)\n", FP); } } /*! * \brief Prints all found connections of a pads to file FP * the connections are stacked in 'PadList'. */ static void PrintPadConnections (Cardinal Layer, FILE * FP, bool IsFirst) { Cardinal i; PadType *ptr; if (!PadList[Layer].Number) return; /* the starting pad */ if (IsFirst) { ptr = PADLIST_ENTRY (Layer, 0); if (ptr != NULL) PrintConnectionListEntry ((char *)UNKNOWN (ptr->Name), NULL, true, FP); else printf ("Skipping NULL ptr in 1st part of PrintPadConnections\n"); } /* we maybe have to start with i=1 if we are handling the * starting-pad itself */ for (i = IsFirst ? 1 : 0; i < PadList[Layer].Number; i++) { ptr = PADLIST_ENTRY (Layer, i); if (ptr != NULL) PrintConnectionListEntry ((char *)EMPTY (ptr->Name), (ElementType *)ptr->Element, false, FP); else printf ("Skipping NULL ptr in 2nd part of PrintPadConnections\n"); } } /*! * \brief Prints all found connections of a pin to file FP * the connections are stacked in 'PVList'. */ static void PrintPinConnections (FILE * FP, bool IsFirst) { Cardinal i; PinType *pv; if (!PVList.Number) return; if (IsFirst) { /* the starting pin */ pv = PVLIST_ENTRY (0); PrintConnectionListEntry ((char *)EMPTY (pv->Name), NULL, true, FP); } /* we maybe have to start with i=1 if we are handling the * starting-pin itself */ for (i = IsFirst ? 1 : 0; i < PVList.Number; i++) { /* get the elements name or assume that its a via */ pv = PVLIST_ENTRY (i); PrintConnectionListEntry ((char *)EMPTY (pv->Name), (ElementType *)pv->Element, false, FP); } } /*! * \brief Checks if all lists of new objects are handled. */ static bool ListsEmpty (bool AndRats) { bool empty; int i; empty = (PVList.Location >= PVList.Number); if (AndRats) empty = empty && (RatList.Location >= RatList.Number); for (i = 0; i < max_copper_layer && empty; i++) if (!LAYER_PTR (i)->no_drc) empty = empty && LineList[i].Location >= LineList[i].Number && ArcList[i].Location >= ArcList[i].Number && PolygonList[i].Location >= PolygonList[i].Number; return (empty); } static void reassign_no_drc_flags (void) { int layer; for (layer = 0; layer < max_copper_layer; layer++) { LayerType *l = LAYER_PTR (layer); l->no_drc = AttributeGet (l, "PCB::skip-drc") != NULL; } } /*! * \brief Loops till no more connections are found. */ static bool DoIt (int flag, bool AndRats, bool AndDraw) { bool newone = false; reassign_no_drc_flags (); do { /* lookup connections; these are the steps (2) to (4) * from the description */ newone = LookupPVConnectionsToPVList (flag) || LookupLOConnectionsToPVList (flag, AndRats) || LookupLOConnectionsToLOList (flag, AndRats) || LookupPVConnectionsToLOList (flag, AndRats); if (AndDraw) DrawNewConnections (); } while (!newone && !ListsEmpty (AndRats)); if (AndDraw) Draw (); return (newone); } /*! * \brief Prints all unused pins of an element to file FP. */ static bool PrintAndSelectUnusedPinsAndPadsOfElement (ElementType *Element, FILE * FP, int flag) { bool first = true; Cardinal number; static DynamicStringType oname; /* check all pins in element */ PIN_LOOP (Element); { if (!TEST_FLAG (HOLEFLAG, pin)) { /* pin might have bee checked before, add to list if not */ if (!TEST_FLAG (flag, pin) && FP) { int i; if (ADD_PV_TO_LIST (pin, flag)) return true; DoIt (flag, true, true); number = PadList[TOP_SIDE].Number + PadList[BOTTOM_SIDE].Number + PVList.Number; /* the pin has no connection if it's the only * list entry; don't count vias */ for (i = 0; i < PVList.Number; i++) if (!PVLIST_ENTRY (i)->Element) number--; if (number == 1) { /* output of element name if not already done */ if (first) { PrintConnectionElementName (Element, FP); first = false; } /* write name to list and draw selected object */ CreateQuotedString (&oname, (char *)EMPTY (pin->Name)); fprintf (FP, "\t%s\n", oname.Data); SET_FLAG (SELECTEDFLAG, pin); DrawPin (pin); } /* reset found objects for the next pin */ if (PrepareNextLoop (FP)) return (true); } } } END_LOOP; /* check all pads in element */ PAD_LOOP (Element); { /* lookup pad in list */ /* pad might has bee checked before, add to list if not */ if (!TEST_FLAG (flag, pad) && FP) { int i; if (ADD_PAD_TO_LIST (TEST_FLAG (ONSOLDERFLAG, pad) ? BOTTOM_SIDE : TOP_SIDE, pad, flag)) return true; DoIt (flag, true, true); number = PadList[TOP_SIDE].Number + PadList[BOTTOM_SIDE].Number + PVList.Number; /* the pin has no connection if it's the only * list entry; don't count vias */ for (i = 0; i < PVList.Number; i++) if (!PVLIST_ENTRY (i)->Element) number--; if (number == 1) { /* output of element name if not already done */ if (first) { PrintConnectionElementName (Element, FP); first = false; } /* write name to list and draw selected object */ CreateQuotedString (&oname, (char *)EMPTY (pad->Name)); fprintf (FP, "\t%s\n", oname.Data); SET_FLAG (SELECTEDFLAG, pad); DrawPad (pad); } /* reset found objects for the next pin */ if (PrepareNextLoop (FP)) return (true); } } END_LOOP; /* print separator if element has unused pins or pads */ if (!first) { fputs ("}\n\n", FP); SEPARATE (FP); } return (false); } /*! * \brief Resets some flags for looking up the next pin/pad. */ static bool PrepareNextLoop (FILE * FP) { Cardinal layer; /* reset found LOs for the next pin */ for (layer = 0; layer < max_copper_layer; layer++) { LineList[layer].Location = LineList[layer].Number = 0; ArcList[layer].Location = ArcList[layer].Number = 0; PolygonList[layer].Location = PolygonList[layer].Number = 0; } /* reset found pads */ for (layer = 0; layer < 2; layer++) PadList[layer].Location = PadList[layer].Number = 0; /* reset PVs */ PVList.Number = PVList.Location = 0; RatList.Number = RatList.Location = 0; return (false); } /*! * \brief Finds all connections to the pins of the passed element. * * The result is written to file FP. * * \return true if operation was aborted. */ static bool PrintElementConnections (ElementType *Element, FILE * FP, int flag, bool AndDraw) { PrintConnectionElementName (Element, FP); /* check all pins in element */ PIN_LOOP (Element); { /* pin might have been checked before, add to list if not */ if (TEST_FLAG (flag, pin)) { PrintConnectionListEntry ((char *)EMPTY (pin->Name), NULL, true, FP); fputs ("\t\t__CHECKED_BEFORE__\n\t}\n", FP); continue; } if (ADD_PV_TO_LIST (pin, flag)) return true; DoIt (flag, true, AndDraw); /* printout all found connections */ PrintPinConnections (FP, true); PrintPadConnections (TOP_SIDE, FP, false); PrintPadConnections (BOTTOM_SIDE, FP, false); fputs ("\t}\n", FP); if (PrepareNextLoop (FP)) return (true); } END_LOOP; /* check all pads in element */ PAD_LOOP (Element); { Cardinal layer; /* pad might have been checked before, add to list if not */ if (TEST_FLAG (flag, pad)) { PrintConnectionListEntry ((char *)EMPTY (pad->Name), NULL, true, FP); fputs ("\t\t__CHECKED_BEFORE__\n\t}\n", FP); continue; } layer = TEST_FLAG (ONSOLDERFLAG, pad) ? BOTTOM_SIDE : TOP_SIDE; if (ADD_PAD_TO_LIST (layer, pad, flag)) return true; DoIt (flag, true, AndDraw); /* print all found connections */ PrintPadConnections (layer, FP, true); PrintPadConnections (layer == (TOP_SIDE ? BOTTOM_SIDE : TOP_SIDE), FP, false); PrintPinConnections (FP, false); fputs ("\t}\n", FP); if (PrepareNextLoop (FP)) return (true); } END_LOOP; fputs ("}\n\n", FP); return (false); } /*! * \brief Draws all new connections which have been found since the * routine was called the last time. */ static void DrawNewConnections (void) { int i; Cardinal position; /* decrement 'i' to keep layerstack order */ for (i = max_copper_layer - 1; i != -1; i--) { Cardinal layer = LayerStack[i]; if (PCB->Data->Layer[layer].On) { /* draw all new lines */ position = LineList[layer].DrawLocation; for (; position < LineList[layer].Number; position++) DrawLine (LAYER_PTR (layer), LINELIST_ENTRY (layer, position)); LineList[layer].DrawLocation = LineList[layer].Number; /* draw all new arcs */ position = ArcList[layer].DrawLocation; for (; position < ArcList[layer].Number; position++) DrawArc (LAYER_PTR (layer), ARCLIST_ENTRY (layer, position)); ArcList[layer].DrawLocation = ArcList[layer].Number; /* draw all new polygons */ position = PolygonList[layer].DrawLocation; for (; position < PolygonList[layer].Number; position++) DrawPolygon (LAYER_PTR (layer), POLYGONLIST_ENTRY (layer, position)); PolygonList[layer].DrawLocation = PolygonList[layer].Number; } } /* draw all new pads */ if (PCB->PinOn) for (i = 0; i < 2; i++) { position = PadList[i].DrawLocation; for (; position < PadList[i].Number; position++) DrawPad (PADLIST_ENTRY (i, position)); PadList[i].DrawLocation = PadList[i].Number; } /* draw all new PVs; 'PVList' holds a list of pointers to the * sorted array pointers to PV data */ while (PVList.DrawLocation < PVList.Number) { PinType *pv = PVLIST_ENTRY (PVList.DrawLocation); if (TEST_FLAG (PINFLAG, pv)) { if (PCB->PinOn) DrawPin (pv); } else if (PCB->ViaOn) DrawVia (pv); PVList.DrawLocation++; } /* draw the new rat-lines */ if (PCB->RatOn) { position = RatList.DrawLocation; for (; position < RatList.Number; position++) DrawRat (RATLIST_ENTRY (position)); RatList.DrawLocation = RatList.Number; } } /*! * \brief Find all connections to pins within one element. */ void LookupElementConnections (ElementType *Element, FILE * FP) { /* reset all currently marked connections */ User = true; ClearFlagOnAllObjects (true, FOUNDFLAG); InitConnectionLookup (); PrintElementConnections (Element, FP, FOUNDFLAG, true); SetChangedFlag (true); if (Settings.RingBellWhenFinished) gui->beep (); FreeConnectionLookupMemory (); IncrementUndoSerialNumber (); User = false; Draw (); } /*! * \brief Find all connections to pins of all element. */ void LookupConnectionsToAllElements (FILE * FP) { /* reset all currently marked connections */ User = false; ClearFlagOnAllObjects (false, FOUNDFLAG); InitConnectionLookup (); ELEMENT_LOOP (PCB->Data); { /* break if abort dialog returned true */ if (PrintElementConnections (element, FP, FOUNDFLAG, false)) break; SEPARATE (FP); if (Settings.ResetAfterElement && n != 1) ClearFlagOnAllObjects (false, FOUNDFLAG); } END_LOOP; if (Settings.RingBellWhenFinished) gui->beep (); ClearFlagOnAllObjects (false, FOUNDFLAG); FreeConnectionLookupMemory (); Redraw (); } /*! * \brief Add the starting object to the list of found objects. */ static bool ListStart (int type, void *ptr1, void *ptr2, void *ptr3, int flag) { DumpList (); switch (type) { case PIN_TYPE: case VIA_TYPE: { if (ADD_PV_TO_LIST ((PinType *) ptr2, flag)) return true; break; } case RATLINE_TYPE: { if (ADD_RAT_TO_LIST ((RatType *) ptr1, flag)) return true; break; } case LINE_TYPE: { int layer = GetLayerNumber (PCB->Data, (LayerType *) ptr1); if (ADD_LINE_TO_LIST (layer, (LineType *) ptr2, flag)) return true; break; } case ARC_TYPE: { int layer = GetLayerNumber (PCB->Data, (LayerType *) ptr1); if (ADD_ARC_TO_LIST (layer, (ArcType *) ptr2, flag)) return true; break; } case POLYGON_TYPE: { int layer = GetLayerNumber (PCB->Data, (LayerType *) ptr1); if (ADD_POLYGON_TO_LIST (layer, (PolygonType *) ptr2, flag)) return true; break; } case PAD_TYPE: { PadType *pad = (PadType *) ptr2; if (ADD_PAD_TO_LIST (TEST_FLAG (ONSOLDERFLAG, pad) ? BOTTOM_SIDE : TOP_SIDE, pad, flag)) return true; break; } } return (false); } /*! * \brief Looks up all connections from the object at the given * coordinates the TheFlag (normally 'FOUNDFLAG') is set for all objects * found. * * The objects are re-drawn if AndDraw is true, also the action is * marked as undoable if AndDraw is true. */ void LookupConnection (Coord X, Coord Y, bool AndDraw, Coord Range, int flag, bool AndRats) { void *ptr1, *ptr2, *ptr3; char *name; int type; /* check if there are any pins or pads at that position */ reassign_no_drc_flags (); type = SearchObjectByLocation (LOOKUP_FIRST, &ptr1, &ptr2, &ptr3, X, Y, Range); if (type == NO_TYPE) { type = SearchObjectByLocation ( LOOKUP_MORE & ~(AndRats ? 0 : RATLINE_TYPE), &ptr1, &ptr2, &ptr3, X, Y, Range); if (type == NO_TYPE) return; if (type & SILK_TYPE) { int laynum = GetLayerNumber (PCB->Data, (LayerType *) ptr1); /* don't mess with non-conducting objects! */ if (laynum >= max_copper_layer || ((LayerType *)ptr1)->no_drc) return; } } name = ConnectionName (type, ptr1, ptr2); hid_actionl ("NetlistShow", name, NULL); User = AndDraw; InitConnectionLookup (); /* now add the object to the appropriate list and start scanning * This is step (1) from the description */ ListStart (type, ptr1, ptr2, ptr3, flag); DoIt (flag, AndRats, AndDraw); if (User) IncrementUndoSerialNumber (); User = false; /* we are done */ if (AndDraw) Draw (); if (AndDraw && Settings.RingBellWhenFinished) gui->beep (); FreeConnectionLookupMemory (); } /*! * \brief Find connections for rats nesting. * * Assumes InitConnectionLookup() has already been done. */ void RatFindHook (int type, void *ptr1, void *ptr2, void *ptr3, bool undo, int flag, bool AndRats) { User = undo; DumpList (); ListStart (type, ptr1, ptr2, ptr3, flag); DoIt (flag, AndRats, false); User = false; } /*! * \brief Find all unused pins of all elements. */ void LookupUnusedPins (FILE * FP) { /* reset all currently marked connections */ User = true; ClearFlagOnAllObjects (true, FOUNDFLAG); InitConnectionLookup (); ELEMENT_LOOP (PCB->Data); { /* break if abort dialog returned true; * passing NULL as filedescriptor discards the normal output */ if (PrintAndSelectUnusedPinsAndPadsOfElement (element, FP, FOUNDFLAG)) break; } END_LOOP; if (Settings.RingBellWhenFinished) gui->beep (); FreeConnectionLookupMemory (); IncrementUndoSerialNumber (); User = false; Draw (); } /*! * \brief Resets all used flags of pins and vias. */ bool ClearFlagOnPinsViasAndPads (bool AndDraw, int flag) { bool change = false; VIA_LOOP (PCB->Data); { if (TEST_FLAG (flag, via)) { if (AndDraw) AddObjectToFlagUndoList (VIA_TYPE, via, via, via); CLEAR_FLAG (flag, via); if (AndDraw) DrawVia (via); change = true; } } END_LOOP; ELEMENT_LOOP (PCB->Data); { PIN_LOOP (element); { if (TEST_FLAG (flag, pin)) { if (AndDraw) AddObjectToFlagUndoList (PIN_TYPE, element, pin, pin); CLEAR_FLAG (flag, pin); if (AndDraw) DrawPin (pin); change = true; } } END_LOOP; PAD_LOOP (element); { if (TEST_FLAG (flag, pad)) { if (AndDraw) AddObjectToFlagUndoList (PAD_TYPE, element, pad, pad); CLEAR_FLAG (flag, pad); if (AndDraw) DrawPad (pad); change = true; } } END_LOOP; } END_LOOP; if (change) SetChangedFlag (true); return change; } /*! * \brief Resets all used flags of LOs. */ bool ClearFlagOnLinesAndPolygons (bool AndDraw, int flag) { bool change = false; RAT_LOOP (PCB->Data); { if (TEST_FLAG (flag, line)) { if (AndDraw) AddObjectToFlagUndoList (RATLINE_TYPE, line, line, line); CLEAR_FLAG (flag, line); if (AndDraw) DrawRat (line); change = true; } } END_LOOP; COPPERLINE_LOOP (PCB->Data); { if (TEST_FLAG (flag, line)) { if (AndDraw) AddObjectToFlagUndoList (LINE_TYPE, layer, line, line); CLEAR_FLAG (flag, line); if (AndDraw) DrawLine (layer, line); change = true; } } ENDALL_LOOP; COPPERARC_LOOP (PCB->Data); { if (TEST_FLAG (flag, arc)) { if (AndDraw) AddObjectToFlagUndoList (ARC_TYPE, layer, arc, arc); CLEAR_FLAG (flag, arc); if (AndDraw) DrawArc (layer, arc); change = true; } } ENDALL_LOOP; COPPERPOLYGON_LOOP (PCB->Data); { if (TEST_FLAG (flag, polygon)) { if (AndDraw) AddObjectToFlagUndoList (POLYGON_TYPE, layer, polygon, polygon); CLEAR_FLAG (flag, polygon); if (AndDraw) DrawPolygon (layer, polygon); change = true; } } ENDALL_LOOP; if (change) SetChangedFlag (true); return change; } /*! * \brief Resets all found connections. */ bool ClearFlagOnAllObjects (bool AndDraw, int flag) { bool change = false; change = ClearFlagOnPinsViasAndPads (AndDraw, flag) || change; change = ClearFlagOnLinesAndPolygons (AndDraw, flag) || change; return change; } /*! * \brief Dumps the list contents. */ static void DumpList (void) { Cardinal i; for (i = 0; i < 2; i++) { PadList[i].Number = 0; PadList[i].Location = 0; PadList[i].DrawLocation = 0; } PVList.Number = 0; PVList.Location = 0; for (i = 0; i < max_copper_layer; i++) { LineList[i].Location = 0; LineList[i].DrawLocation = 0; LineList[i].Number = 0; ArcList[i].Location = 0; ArcList[i].DrawLocation = 0; ArcList[i].Number = 0; PolygonList[i].Location = 0; PolygonList[i].DrawLocation = 0; PolygonList[i].Number = 0; } RatList.Number = 0; RatList.Location = 0; RatList.DrawLocation = 0; } struct drc_info { int flag; }; /*! * \brief Check for DRC violations on a single net starting from the pad * or pin. * * Sees if the connectivity changes when everything is bloated, or * shrunk. */ static bool DRCFind (int What, void *ptr1, void *ptr2, void *ptr3) { Coord x, y; int object_count; long int *object_id_list; int *object_type_list; DrcViolationType *violation; int flag; if (PCB->Shrink != 0) { Bloat = -PCB->Shrink; ListStart (What, ptr1, ptr2, ptr3, DRCFLAG | SELECTEDFLAG); DoIt (DRCFLAG | SELECTEDFLAG, true, false); /* ok now the shrunk net has the SELECTEDFLAG set */ DumpList (); ListStart (What, ptr1, ptr2, ptr3, FOUNDFLAG); Bloat = 0; drc = true; /* abort the search if we find anything not already found */ if (DoIt (FOUNDFLAG, true, false)) { DumpList (); /* make the flag changes undoable */ ClearFlagOnAllObjects (false, FOUNDFLAG | SELECTEDFLAG); User = true; drc = false; Bloat = -PCB->Shrink; ListStart (What, ptr1, ptr2, ptr3, SELECTEDFLAG); DoIt (SELECTEDFLAG, true, true); DumpList (); ListStart (What, ptr1, ptr2, ptr3, FOUNDFLAG); Bloat = 0; drc = true; DoIt (FOUNDFLAG, true, true); DumpList (); User = false; drc = false; drcerr_count++; LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); violation = pcb_drc_violation_new (_("Potential for broken trace"), _("Insufficient overlap between objects can lead to broken tracks\n" "due to registration errors with old wheel style photo-plotters."), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ FALSE, /* MEASUREMENT OF ERROR UNKNOWN */ 0, /* MAGNITUDE OF ERROR UNKNOWN */ PCB->Shrink, object_count, object_id_list, object_type_list); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); if (!throw_drc_dialog()) return (true); IncrementUndoSerialNumber (); Undo (true); } DumpList (); } /* now check the bloated condition */ drc = false; ClearFlagOnAllObjects (false, FOUNDFLAG | SELECTEDFLAG); Bloat = 0; ListStart (What, ptr1, ptr2, ptr3, SELECTEDFLAG); DoIt (SELECTEDFLAG, true, false); DumpList (); flag = FOUNDFLAG; ListStart (What, ptr1, ptr2, ptr3, flag); Bloat = PCB->Bloat; drc = true; while (DoIt (flag, true, false)) { DumpList (); /* make the flag changes undoable */ ClearFlagOnAllObjects (false, FOUNDFLAG | SELECTEDFLAG); User = true; drc = false; Bloat = 0; ListStart (What, ptr1, ptr2, ptr3, SELECTEDFLAG); DoIt (SELECTEDFLAG, true, true); DumpList (); ListStart (What, ptr1, ptr2, ptr3, FOUNDFLAG); Bloat = PCB->Bloat; drc = true; DoIt (FOUNDFLAG, true, true); DumpList (); drcerr_count++; LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); violation = pcb_drc_violation_new (_("Copper areas too close"), _("Circuits that are too close may bridge during imaging, etching,\n" "plating, or soldering processes resulting in a direct short."), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ FALSE, /* MEASUREMENT OF ERROR UNKNOWN */ 0, /* MAGNITUDE OF ERROR UNKNOWN */ PCB->Bloat, object_count, object_id_list, object_type_list); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); User = false; drc = false; if (!throw_drc_dialog()) return (true); IncrementUndoSerialNumber (); Undo (true); /* highlight the rest of the encroaching net so it's not reported again */ flag = FOUNDFLAG | SELECTEDFLAG; Bloat = 0; ListStart (thing_type, thing_ptr1, thing_ptr2, thing_ptr3, flag); DoIt (flag, true, true); DumpList (); drc = true; Bloat = PCB->Bloat; ListStart (What, ptr1, ptr2, ptr3, flag); } drc = false; DumpList (); ClearFlagOnAllObjects (false, FOUNDFLAG | SELECTEDFLAG); return (false); } /*! * \brief DRC clearance callback. */ static int drc_callback (DataType *data, LayerType *layer, PolygonType *polygon, int type, void *ptr1, void *ptr2, void *userdata) { struct drc_info *i = (struct drc_info *) userdata; char *message; Coord x, y; int object_count; long int *object_id_list; int *object_type_list; DrcViolationType *violation; LineType *line = (LineType *) ptr2; ArcType *arc = (ArcType *) ptr2; PinType *pin = (PinType *) ptr2; PadType *pad = (PadType *) ptr2; SetThing (type, ptr1, ptr2, ptr2); switch (type) { case LINE_TYPE: if (line->Clearance < 2 * PCB->Bloat) { AddObjectToFlagUndoList (type, ptr1, ptr2, ptr2); SET_FLAG (i->flag, line); message = _("Line with insufficient clearance inside polygon\n"); goto doIsBad; } break; case ARC_TYPE: if (arc->Clearance < 2 * PCB->Bloat) { AddObjectToFlagUndoList (type, ptr1, ptr2, ptr2); SET_FLAG (i->flag, arc); message = _("Arc with insufficient clearance inside polygon\n"); goto doIsBad; } break; case PAD_TYPE: if (pad->Clearance && pad->Clearance < 2 * PCB->Bloat) if (IsPadInPolygon(pad,polygon)) { AddObjectToFlagUndoList (type, ptr1, ptr2, ptr2); SET_FLAG (i->flag, pad); message = _("Pad with insufficient clearance inside polygon\n"); goto doIsBad; } break; case PIN_TYPE: if (pin->Clearance && pin->Clearance < 2 * PCB->Bloat) { AddObjectToFlagUndoList (type, ptr1, ptr2, ptr2); SET_FLAG (i->flag, pin); message = _("Pin with insufficient clearance inside polygon\n"); goto doIsBad; } break; case VIA_TYPE: if (pin->Clearance && pin->Clearance < 2 * PCB->Bloat) { AddObjectToFlagUndoList (type, ptr1, ptr2, ptr2); SET_FLAG (i->flag, pin); message = _("Via with insufficient clearance inside polygon\n"); goto doIsBad; } break; default: Message ("hace: Bad Plow object in callback\n"); } return 0; doIsBad: AddObjectToFlagUndoList (POLYGON_TYPE, layer, polygon, polygon); SET_FLAG (FOUNDFLAG, polygon); DrawPolygon (layer, polygon); DrawObject (type, ptr1, ptr2); drcerr_count++; LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); violation = pcb_drc_violation_new (message, _("Circuits that are too close may bridge during imaging, etching,\n" "plating, or soldering processes resulting in a direct short."), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ FALSE, /* MEASUREMENT OF ERROR UNKNOWN */ 0, /* MAGNITUDE OF ERROR UNKNOWN */ PCB->Bloat, object_count, object_id_list, object_type_list); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); if (!throw_drc_dialog()) return 1; IncrementUndoSerialNumber (); Undo (true); return 0; } /*! * \brief Check for DRC violations. * * See if the connectivity changes when everything is bloated, or shrunk. */ int DRCAll (void) { int nopastecnt = 0; bool IsBad = false; struct drc_info info; reset_drc_dialog_message(); IsBad = false; drcerr_count = 0; SaveStackAndVisibility (); ResetStackAndVisibility (); hid_action ("LayersChanged"); InitConnectionLookup (); if (ClearFlagOnAllObjects (true, FOUNDFLAG | DRCFLAG | SELECTEDFLAG)) { IncrementUndoSerialNumber (); Draw (); } User = false; if (!IsBad) { IsBad = checkClearanceAndOverlap(&nopastecnt); } ClearFlagOnAllObjects (false, IsBad ? DRCFLAG : (FOUNDFLAG | DRCFLAG | SELECTEDFLAG)); info.flag = SELECTEDFLAG; /* check minimum widths and polygon clearances */ if (!IsBad) { IsBad = checkLineThin(); } if (!IsBad) { IsBad = checkArcThin(); } if (!IsBad) { IsBad = checkPinThin(); } if (!IsBad) { IsBad = checkPadThin(); } if (!IsBad) { IsBad = checkViaRingAndDrillSize(); } FreeConnectionLookupMemory (); Bloat = 0; /* check silkscreen minimum widths outside of elements */ /* XXX - need to check text and polygons too! */ if (!IsBad) { IsBad = checkSilkscreeenMinimumWidthOutsideElements(); } /* check silkscreen minimum widths inside of elements */ /* XXX - need to check text and polygons too! */ if (!IsBad) { IsBad = checkSilkscreeenMinimumWidthInsideElements(); } if (IsBad) { IncrementUndoSerialNumber (); } RestoreStackAndVisibility (); hid_action ("LayersChanged"); gui->invalidate_all (); if (nopastecnt > 0) { Message (ngettext ("Warning: %d pad has the nopaste flag set.\n", "Warning: %d pads have the nopaste flag set.\n", nopastecnt), nopastecnt); } return IsBad ? -drcerr_count : drcerr_count; } /*! * \brief Locate the coordinatates of offending item (thing). */ static void LocateError (Coord *x, Coord *y) { switch (thing_type) { case LINE_TYPE: { LineType *line = (LineType *) thing_ptr3; *x = (line->Point1.X + line->Point2.X) / 2; *y = (line->Point1.Y + line->Point2.Y) / 2; break; } case ARC_TYPE: { ArcType *arc = (ArcType *) thing_ptr3; *x = arc->X; *y = arc->Y; break; } case POLYGON_TYPE: { PolygonType *polygon = (PolygonType *) thing_ptr3; *x = (polygon->Clipped->contours->xmin + polygon->Clipped->contours->xmax) / 2; *y = (polygon->Clipped->contours->ymin + polygon->Clipped->contours->ymax) / 2; break; } case PIN_TYPE: case VIA_TYPE: { PinType *pin = (PinType *) thing_ptr3; *x = pin->X; *y = pin->Y; break; } case PAD_TYPE: { PadType *pad = (PadType *) thing_ptr3; *x = (pad->Point1.X + pad->Point2.X) / 2; *y = (pad->Point1.Y + pad->Point2.Y) / 2; break; } case ELEMENT_TYPE: { ElementType *element = (ElementType *) thing_ptr3; *x = element->MarkX; *y = element->MarkY; break; } default: return; } } /*! * \brief Build a list of the of offending items by ID. * * (Currently just "thing"). */ static void BuildObjectList (int *object_count, long int **object_id_list, int **object_type_list) { *object_count = 0; *object_id_list = NULL; *object_type_list = NULL; switch (thing_type) { case LINE_TYPE: case ARC_TYPE: case POLYGON_TYPE: case PIN_TYPE: case VIA_TYPE: case PAD_TYPE: case ELEMENT_TYPE: case RATLINE_TYPE: *object_count = 1; *object_id_list = (long int *)malloc (sizeof (long int)); *object_type_list = (int *)malloc (sizeof (int)); **object_id_list = ((AnyObjectType *)thing_ptr3)->ID; **object_type_list = thing_type; return; default: fprintf (stderr, _("Internal error in BuildObjectList: unknown object type %i\n"), thing_type); } } /*! * \brief Center the display to show the offending item (thing). */ static void GotoError (void) { Coord X, Y; LocateError (&X, &Y); switch (thing_type) { case LINE_TYPE: case ARC_TYPE: case POLYGON_TYPE: ChangeGroupVisibility ( GetLayerNumber (PCB->Data, (LayerType *) thing_ptr1), true, true); } CenterDisplay (X, Y, false); } void InitConnectionLookup (void) { InitComponentLookup (); InitLayoutLookup (); } void FreeConnectionLookupMemory (void) { FreeComponentLookupMemory (); FreeLayoutLookupMemory (); } static bool checkClearanceAndOverlap(int* nopastecnt) { bool IsBad = false; ELEMENT_LOOP (PCB->Data); { PIN_LOOP (element); { if (!TEST_FLAG (DRCFLAG, pin) && DRCFind (PIN_TYPE, (void *) element, (void *) pin, (void *) pin)) { IsBad = true; break; } } END_LOOP; if (IsBad) break; PAD_LOOP (element); { /* count up how many pads have no solderpaste openings */ if (TEST_FLAG (NOPASTEFLAG, pad)) nopastecnt++; if (!TEST_FLAG (DRCFLAG, pad) && DRCFind (PAD_TYPE, (void *) element, (void *) pad, (void *) pad)) { IsBad = true; break; } } END_LOOP; if (IsBad) break; } END_LOOP; if (!IsBad) VIA_LOOP (PCB->Data); { if (!TEST_FLAG (DRCFLAG, via) && DRCFind (VIA_TYPE, (void *) via, (void *) via, (void *) via)) { IsBad = true; break; } } END_LOOP; return IsBad; } static bool checkLineThin(void) { bool IsBad = false; struct drc_info info; COPPERLINE_LOOP (PCB->Data); { /* check line clearances in polygons */ if (PlowsPolygon (PCB->Data, LINE_TYPE, layer, line, drc_callback, &info)) { IsBad = true; break; } if (line->Thickness < PCB->minWid) { Coord x, y; int object_count; long int *object_id_list; int *object_type_list; DrcViolationType *violation; AddObjectToFlagUndoList (LINE_TYPE, layer, line, line); SET_FLAG (SELECTEDFLAG, line); DrawLine (layer, line); drcerr_count++; SetThing (LINE_TYPE, layer, line, line); LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); violation = pcb_drc_violation_new (_("Line width is too thin"), _("Process specifications dictate a minimum feature-width\n" "that can reliably be reproduced"), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ TRUE, /* MEASUREMENT OF ERROR KNOWN */ line->Thickness, PCB->minWid, object_count, object_id_list, object_type_list); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); if (!throw_drc_dialog()) { IsBad = true; break; } IncrementUndoSerialNumber (); Undo (false); } } ENDALL_LOOP; return IsBad; } static bool checkArcThin(void) { bool IsBad = false; struct drc_info info; COPPERARC_LOOP (PCB->Data); { if (PlowsPolygon (PCB->Data, ARC_TYPE, layer, arc, drc_callback, &info)) { IsBad = true; break; } if (arc->Thickness < PCB->minWid) { Coord x, y; int object_count; long int *object_id_list; int *object_type_list; DrcViolationType *violation; AddObjectToFlagUndoList (ARC_TYPE, layer, arc, arc); SET_FLAG (SELECTEDFLAG, arc); DrawArc (layer, arc); drcerr_count++; SetThing (ARC_TYPE, layer, arc, arc); LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); violation = pcb_drc_violation_new (_("Arc width is too thin"), _("Process specifications dictate a minimum feature-width\n" "that can reliably be reproduced"), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ TRUE, /* MEASUREMENT OF ERROR KNOWN */ arc->Thickness, PCB->minWid, object_count, object_id_list, object_type_list); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); if (!throw_drc_dialog()) { IsBad = true; break; } IncrementUndoSerialNumber (); Undo (false); } } ENDALL_LOOP; return IsBad; } static bool checkPinThin(void) { bool IsBad = false; struct drc_info info; ALLPIN_LOOP (PCB->Data); { if (PlowsPolygon (PCB->Data, PIN_TYPE, element, pin, drc_callback, &info)) { IsBad = true; break; } if (!TEST_FLAG (HOLEFLAG, pin) && pin->Thickness - pin->DrillingHole < 2 * PCB->minRing) { Coord x, y; int object_count; long int *object_id_list; int *object_type_list; DrcViolationType *violation; AddObjectToFlagUndoList (PIN_TYPE, element, pin, pin); SET_FLAG (SELECTEDFLAG, pin); DrawPin (pin); drcerr_count++; SetThing (PIN_TYPE, element, pin, pin); LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); violation = pcb_drc_violation_new (_("Pin annular ring too small"), _("Annular rings that are too small may erode during etching,\n" "resulting in a broken connection"), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ TRUE, /* MEASUREMENT OF ERROR KNOWN */ (pin->Thickness - pin->DrillingHole) / 2, PCB->minRing, object_count, object_id_list, object_type_list); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); if (!throw_drc_dialog()) { IsBad = true; break; } IncrementUndoSerialNumber (); Undo (false); } if (pin->DrillingHole < PCB->minDrill) { Coord x, y; int object_count; long int *object_id_list; int *object_type_list; DrcViolationType *violation; AddObjectToFlagUndoList (PIN_TYPE, element, pin, pin); SET_FLAG (SELECTEDFLAG, pin); DrawPin (pin); drcerr_count++; SetThing (PIN_TYPE, element, pin, pin); LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); violation = pcb_drc_violation_new (_("Pin drill size is too small"), _("Process rules dictate the minimum drill size which can be used"), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ TRUE, /* MEASUREMENT OF ERROR KNOWN */ pin->DrillingHole, PCB->minDrill, object_count, object_id_list, object_type_list); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); if (!throw_drc_dialog()) { IsBad = true; break; } IncrementUndoSerialNumber (); Undo (false); } } ENDALL_LOOP; return IsBad; } static bool checkPadThin(void) { bool IsBad = false; struct drc_info info; ALLPAD_LOOP (PCB->Data); { if (PlowsPolygon (PCB->Data, PAD_TYPE, element, pad, drc_callback, &info)) { IsBad = true; break; } if (pad->Thickness < PCB->minWid) { Coord x, y; int object_count; long int *object_id_list; int *object_type_list; DrcViolationType *violation; AddObjectToFlagUndoList (PAD_TYPE, element, pad, pad); SET_FLAG (SELECTEDFLAG, pad); DrawPad (pad); drcerr_count++; SetThing (PAD_TYPE, element, pad, pad); LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); violation = pcb_drc_violation_new (_("Pad is too thin"), _("Pads which are too thin may erode during etching,\n" "resulting in a broken or unreliable connection"), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ TRUE, /* MEASUREMENT OF ERROR KNOWN */ pad->Thickness, PCB->minWid, object_count, object_id_list, object_type_list); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); if (!throw_drc_dialog()) { IsBad = true; break; } IncrementUndoSerialNumber (); Undo (false); } } ENDALL_LOOP; return IsBad; } static bool checkViaRingAndDrillSize(void) { bool IsBad = false; VIA_LOOP (PCB->Data); { struct drc_info info; if (PlowsPolygon (PCB->Data, VIA_TYPE, via, via, drc_callback, &info)) { IsBad = true; break; } if (!TEST_FLAG (HOLEFLAG, via) && via->Thickness - via->DrillingHole < 2 * PCB->minRing) { Coord x, y; int object_count; long int *object_id_list; int *object_type_list; DrcViolationType *violation; AddObjectToFlagUndoList (VIA_TYPE, via, via, via); SET_FLAG (SELECTEDFLAG, via); DrawVia (via); drcerr_count++; SetThing (VIA_TYPE, via, via, via); LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); violation = pcb_drc_violation_new (_("Via annular ring too small"), _("Annular rings that are too small may erode during etching,\n" "resulting in a broken connection"), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ TRUE, /* MEASUREMENT OF ERROR KNOWN */ (via->Thickness - via->DrillingHole) / 2, PCB->minRing, object_count, object_id_list, object_type_list); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); if (!throw_drc_dialog()) { IsBad = true; break; } IncrementUndoSerialNumber (); Undo (false); } if (via->DrillingHole < PCB->minDrill) { Coord x, y; int object_count; long int *object_id_list; int *object_type_list; DrcViolationType *violation; AddObjectToFlagUndoList (VIA_TYPE, via, via, via); SET_FLAG (SELECTEDFLAG, via); DrawVia (via); drcerr_count++; SetThing (VIA_TYPE, via, via, via); LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); violation = pcb_drc_violation_new (_("Via drill size is too small"), _("Process rules dictate the minimum drill size which can be used"), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ TRUE, /* MEASUREMENT OF ERROR KNOWN */ via->DrillingHole, PCB->minDrill, object_count, object_id_list, object_type_list); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); if (!throw_drc_dialog()) { IsBad = true; break; } IncrementUndoSerialNumber (); Undo (false); } } END_LOOP; return IsBad; } static bool checkSilkscreeenMinimumWidthOutsideElements(void) { bool IsBad = false; SILKLINE_LOOP (PCB->Data); { if (line->Thickness < PCB->minSlk) { Coord x, y; int object_count; long int *object_id_list; int *object_type_list; DrcViolationType *violation; SET_FLAG (SELECTEDFLAG, line); DrawLine (layer, line); drcerr_count++; SetThing (LINE_TYPE, layer, line, line); LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); violation = pcb_drc_violation_new (_("Silk line is too thin"), _("Process specifications dictate a minimum silkscreen\n" "feature-width that can reliably be reproduced"), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ TRUE, /* MEASUREMENT OF ERROR KNOWN */ line->Thickness, PCB->minSlk, object_count, object_id_list, object_type_list); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); if (!throw_drc_dialog()) { IsBad = true; break; } } } ENDALL_LOOP; return IsBad; } static bool checkSilkscreeenMinimumWidthInsideElements(void) { int tmpcnt; bool IsBad = false; ELEMENT_LOOP (PCB->Data); { tmpcnt = 0; ELEMENTLINE_LOOP (element); { if (line->Thickness < PCB->minSlk) tmpcnt++; } END_LOOP; if (tmpcnt > 0) { char *title; char *name; char *buffer; int buflen; Coord x, y; int object_count; long int *object_id_list; int *object_type_list; DrcViolationType *violation; SET_FLAG (SELECTEDFLAG, element); DrawElement (element); drcerr_count++; SetThing (ELEMENT_TYPE, element, element, element); LocateError (&x, &y); BuildObjectList (&object_count, &object_id_list, &object_type_list); title = _("Element %s has %i silk lines which are too thin"); name = (char *)UNKNOWN (NAMEONPCB_NAME (element)); /* -4 is for the %s and %i place-holders */ /* +11 is the max printed length for a 32 bit integer */ /* +1 is for the \0 termination */ buflen = strlen (title) - 4 + strlen (name) + 11 + 1; buffer = (char *)malloc (buflen); snprintf (buffer, buflen, title, name, tmpcnt); violation = pcb_drc_violation_new (buffer, _("Process specifications dictate a minimum silkscreen\n" "feature-width that can reliably be reproduced"), x, y, 0, /* ANGLE OF ERROR UNKNOWN */ TRUE, /* MEASUREMENT OF ERROR KNOWN */ 0, /* MINIMUM OFFENDING WIDTH UNKNOWN */ PCB->minSlk, object_count, object_id_list, object_type_list); free (buffer); append_drc_violation (violation); pcb_drc_violation_free (violation); free (object_id_list); free (object_type_list); if (!throw_drc_dialog()) { IsBad = true; break; } } } END_LOOP; return IsBad; } --Multipart=_Fri__15_Jan_2016_22_15_55_+0100_0VnwYXnTWHmfK_18--