Root/eeshow/kicad/delta.c

1/*
2 * kicad/delta.c - Find differences in .sch files
3 *
4 * Written 2016 by Werner Almesberger
5 * Copyright 2016 by Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <stddef.h>
15#include <stdbool.h>
16#include <stdlib.h>
17#include <string.h>
18
19#include "misc/util.h"
20#include "gfx/text.h"
21#include "kicad/lib.h"
22#include "kicad/sch.h"
23#include "kicad/delta.h"
24
25
26/*
27 * @@@ we should clone components and compare them, too
28 *
29 * if two components or sub-sheets mismatch in their field lists, we should
30 * remove matching items from the lists.
31 *
32 * @@@@ line(A, B) == line(B, A), etc.
33 */
34
35
36/* ----- Components -------------------------------------------------------- */
37
38
39static bool comp_eq_obj(const struct lib_obj *a, const struct lib_obj *b)
40{
41    int i;
42
43    if (a->type != b->type)
44        return 0;
45    if (a->unit != b->unit || a->convert != b->convert)
46            return 0;
47    switch (a->type) {
48    case lib_obj_poly:
49        if (a->u.poly.thick != b->u.poly.thick &&
50            a->u.poly.fill != b->u.poly.fill)
51            return 0;
52        if (a->u.poly.points != b->u.poly.points)
53            return 0;
54        for (i = 0; i != a->u.poly.points; i++)
55            if (a->u.poly.x[i] != b->u.poly.x[i] ||
56                a->u.poly.y[i] != b->u.poly.y[i])
57                return 0;
58        return 1;
59    case lib_obj_rect:
60        return a->u.rect.sx == b->u.rect.sx &&
61            a->u.rect.sy == b->u.rect.sy &&
62            a->u.rect.ex == b->u.rect.ex &&
63            a->u.rect.ey == b->u.rect.ey &&
64            a->u.rect.thick == b->u.rect.thick &&
65            a->u.rect.fill == b->u.rect.fill;
66    case lib_obj_circ:
67        return a->u.circ.x == b->u.circ.x &&
68            a->u.circ.y == b->u.circ.y &&
69            a->u.circ.r == b->u.circ.r &&
70            a->u.circ.thick == b->u.circ.thick &&
71            a->u.circ.fill == b->u.circ.fill;
72    case lib_obj_arc:
73        return a->u.arc.x == b->u.arc.x &&
74            a->u.arc.y == b->u.arc.y &&
75            a->u.arc.r == b->u.arc.r &&
76            a->u.arc.start_a == b->u.arc.start_a &&
77            a->u.arc.end_a == b->u.arc.end_a &&
78            a->u.arc.thick == b->u.arc.thick &&
79            a->u.arc.fill == b->u.arc.fill;
80    case lib_obj_text:
81        return a->u.text.x == b->u.text.x &&
82            a->u.text.y == b->u.text.y &&
83            a->u.text.dim == b->u.text.dim &&
84            a->u.text.orient == b->u.text.orient &&
85            a->u.text.style == b->u.text.style &&
86            a->u.text.hor_align == b->u.text.hor_align &&
87            a->u.text.vert_align == b->u.text.vert_align &&
88            !strcmp(a->u.text.s, b->u.text.s);
89    case lib_obj_pin:
90        return a->u.pin.x == b->u.pin.x &&
91            a->u.pin.y == b->u.pin.y &&
92            a->u.pin.length == b->u.pin.length &&
93            a->u.pin.orient == b->u.pin.orient &&
94            a->u.pin.number_size == b->u.pin.number_size &&
95            a->u.pin.name_size == b->u.pin.name_size &&
96            a->u.pin.etype == b->u.pin.etype &&
97            !strcmp(a->u.pin.name, b->u.pin.name) &&
98            !strcmp(a->u.pin.number, b->u.pin.number);
99    default:
100        abort();
101    }
102}
103
104
105static bool comp_eq_objs(const struct lib_obj *a, const struct lib_obj *b)
106{
107    /*
108     * @@@ over-simplify a little. We don't search to find objects that
109     * have merely been reordered.
110     */
111    while (a && b) {
112        if (!comp_eq_obj(a, b))
113            return 0;
114        a = a->next;
115        b = b->next;
116    }
117    return a == b;
118}
119
120
121static bool comp_eq_aliases(const struct comp_alias *a,
122    const struct comp_alias *b)
123{
124    while (a && b) {
125        if (strcmp(a->name, b->name))
126            return 0;
127        a = a->next;
128        b = b->next;
129    }
130    return a == b;
131}
132
133
134static bool comp_eq(const struct comp *a, const struct comp *b)
135{
136    if (a == b)
137        return 1;
138    if (!(a && b))
139        return 0;
140    if (strcmp(a->name, b->name))
141        return 0;
142    if (a->units != b->units)
143        return 0;
144#if 0
145    /* @@@ in-sheet settings override this */
146    if (a->visible != b->visible)
147        return 0;
148#endif
149    if (a->show_pin_name != b->show_pin_name)
150        return 0;
151    if (a->show_pin_num != b->show_pin_num)
152        return 0;
153    if (a->name_offset != b->name_offset)
154        return 0;
155    if (!comp_eq_aliases(a->aliases, b->aliases))
156        return 0;
157    return comp_eq_objs(a->objs, b->objs);
158}
159
160
161/* ----- Sheets ------------------------------------------------------------ */
162
163
164static struct sch_obj *objs_clone(const struct sch_obj *objs)
165{
166    struct sch_obj *new_objs = NULL;
167    struct sch_obj **next = &new_objs;
168    const struct sch_obj *obj;
169
170    for (obj = objs; obj; obj = obj->next) {
171        *next = alloc_type(struct sch_obj);
172        **next = *obj;
173        next = &(*next)->next;
174    }
175    *next = NULL;
176    return new_objs;
177}
178
179
180static void add_obj(struct sheet *sch, struct sch_obj *obj)
181{
182    *sch->next_obj = obj;
183    sch->next_obj = &obj->next;
184    obj->next = NULL;
185}
186
187
188static bool comp_fields_eq(const struct comp_field *a,
189    const struct comp_field *b)
190{
191    while (a && b) {
192        if (a->txt.x != b->txt.x || a->txt.y != b->txt.y)
193            return 0;
194        if (a->txt.size != b->txt.size)
195            return 0;
196        if (a->txt.rot != b->txt.rot)
197            return 0;
198        if (a->txt.hor != b->txt.hor || a->txt.vert != b->txt.vert)
199            return 0;
200        if (strcmp(a->txt.s, b->txt.s))
201            return 0;
202        a = a->next;
203        b = b->next;
204    }
205    return a == b;
206}
207
208
209static bool sheet_fields_eq(const struct sheet_field *a,
210    const struct sheet_field *b)
211{
212    const struct sheet_field *ta;
213    const struct sheet_field *tb;
214
215    for (ta = a; ta; ta = ta->next) {
216        for (tb = b; tb; tb = tb->next) {
217            if (ta->x != tb->x || ta->y != tb->y)
218                continue;
219            if (ta->dim != tb->dim)
220                continue;
221            if (ta->dim != tb->dim)
222                continue;
223            if (ta->shape != tb->shape)
224                continue;
225            if (strcmp(ta->s, tb->s))
226                continue;
227            goto match;
228        }
229        return 0;
230match:
231        continue;
232    }
233
234    while (a && b) {
235        a = a->next;
236        b = b->next;
237    }
238    return a == b;
239}
240
241
242/*
243 * @@@ idea: if we send all strings through a "unique" function, we can
244 * memcmp things like "struct sch_text" without having to go through the
245 * fields individually.
246 */
247
248static bool obj_eq(const struct sch_obj *a, const struct sch_obj *b,
249    bool recurse)
250{
251    if (a->type != b->type)
252        return 0;
253
254    /* Special treatment because we support direction reversal */
255    if (a->type == sch_obj_wire) {
256        if (a->u.wire.fn != b->u.wire.fn)
257            return 0;
258        if (a->x == b->x && a->y == b->y &&
259            a->u.wire.ex == b->u.wire.ex &&
260            a->u.wire.ey == b->u.wire.ey)
261            return 1;
262        if (a->x == b->u.wire.ex && a->y == b->u.wire.ey &&
263            a->u.wire.ex == b->x && a->u.wire.ey == b->y)
264            return 1;
265        return 0;
266    }
267
268    if (a->x != b->x || a->y != b->y)
269        return 0;
270
271    switch (a->type) {
272    case sch_obj_junction:
273        return 1;
274    case sch_obj_noconn:
275        return 1;
276    case sch_obj_glabel:
277    case sch_obj_text:
278        if (a->u.text.fn != b->u.text.fn)
279            return 0;
280        if (a->u.text.dir != b->u.text.dir)
281            return 0;
282        if (a->u.text.dim != b->u.text.dim)
283            return 0;
284        if (a->u.text.shape != b->u.text.shape)
285            return 0;
286        if (strcmp(a->u.text.s, b->u.text.s))
287            return 0;
288        return 1;
289    case sch_obj_comp:
290        if (!comp_eq(a->u.comp.comp, b->u.comp.comp))
291            return 0;
292        if (a->u.comp.unit != b->u.comp.unit)
293            return 0;
294        if (a->u.comp.convert != b->u.comp.convert)
295            return 0;
296        if (memcmp(a->u.comp.m, b->u.comp.m, sizeof(a->u.comp.m)))
297            return 0;
298        return comp_fields_eq(a->u.comp.fields, b->u.comp.fields);
299    case sch_obj_sheet:
300        if (a->u.sheet.w != b->u.sheet.w)
301            return 0;
302        if (a->u.sheet.h != b->u.sheet.h)
303            return 0;
304        if (a->u.sheet.name_dim != b->u.sheet.name_dim)
305            return 0;
306        if (a->u.sheet.file_dim != b->u.sheet.file_dim)
307            return 0;
308        if (a->u.sheet.rotated != b->u.sheet.rotated)
309            return 0;
310        if (strcmp(a->u.sheet.name, b->u.sheet.name))
311            return 0;
312        if (strcmp(a->u.sheet.file, b->u.sheet.file))
313            return 0;
314        if (!sheet_fields_eq(a->u.sheet.fields, b->u.sheet.fields))
315            return 0;
316        if (!recurse)
317            return 1;
318        if (a->u.sheet.error != b->u.sheet.error)
319            return 0;
320        return sheet_eq(a->u.sheet.sheet, b->u.sheet.sheet);
321    default:
322        abort();
323    }
324}
325
326
327bool sheet_eq(const struct sheet *a, const struct sheet *b)
328{
329    const struct sch_obj *obj_a, *obj_b;
330
331    if (a == NULL && b == NULL)
332        return 1;
333    if (!(a && b))
334        return 0;
335
336    if (a->title != b->title) {
337        if (!a->title || !b->title)
338            return 0;
339        if (strcmp(a->title, b->title))
340            return 0;
341    }
342
343    obj_a = a->objs;
344    obj_b = b->objs;
345    while (obj_a && obj_b) {
346        if (!obj_eq(obj_a, obj_b, 1))
347            return 0;
348        obj_a = obj_a->next;
349        obj_b = obj_b->next;
350    }
351    return obj_a == obj_b;
352}
353
354
355/* ----- Merge wires ------------------------------------------------------- */
356
357
358static int min(int a, int b)
359{
360    return a < b ? a : b;
361}
362
363
364static int max(int a, int b)
365{
366    return a > b ? a : b;
367}
368
369
370static void merge_coord(int *sa, int *ea, int sb, int eb)
371{
372    int tmp;
373
374    tmp = min(min(*sa, *ea), min(sb, eb));
375    *ea = max(max(*sa, *ea), max(sb, eb));
376    *sa = tmp;
377}
378
379
380static bool merge_wire(struct sch_obj *a, const struct sch_obj *b)
381{
382    struct sch_wire *wa = &a->u.wire;
383    const struct sch_wire *wb = &b->u.wire;
384
385    if (a->x == b->x && a->x == wa->ex && a->x == wb->ex &&
386        (a->y == b->y || a->y == wb->ey || wa->ey == b->y ||
387        wa->ey == wb->ey)) {
388        merge_coord(&a->y, &wa->ey, b->y, wb->ey);
389        return 1;
390    }
391    if (a->y == b->y && a->y == wa->ey && a->y == wb->ey &&
392        (a->x == b->x || a->x == wb->ex || wa->ex == b->x ||
393        wa->ex == wb->ex)) {
394        merge_coord(&a->x, &wa->ex, b->x, wb->ex);
395        return 1;
396    }
397    return 0;
398}
399
400
401static void merge_wires(struct sch_obj *a)
402{
403    struct sch_obj **b, **next;
404
405    for (; a; a = a->next) {
406        if (a->type != sch_obj_wire)
407            continue;
408        for (b = &a->next; *b; b = next) {
409            next = &(*b)->next;
410            if ((*b)->type != sch_obj_wire)
411                continue;
412            if (a->u.wire.fn != (*b)->u.wire.fn)
413                continue;
414            if (merge_wire(a, *b)) {
415                struct sch_obj *tmp = *b;
416
417                *b = *next;
418                free(tmp);
419                next = b;
420            }
421        }
422    }
423}
424
425
426/* ----- Split objects from A and B into only-A, only-B, and A-and-B ------- */
427
428
429static void free_obj(struct sch_obj *obj)
430{
431    /* there may be more to free once we get into cloning components */
432    free(obj);
433}
434
435
436static void init_res(struct sheet *res)
437{
438    res->title = NULL;
439        res->objs = NULL;
440        res->next_obj = &res->objs;
441        res->next = NULL;
442}
443
444
445void delta(const struct sheet *a, const struct sheet *b,
446    struct sheet *res_a, struct sheet *res_b, struct sheet *res_ab)
447{
448    struct sch_obj *objs_a, *objs_b;
449    struct sch_obj *next_a;
450    struct sch_obj **obj_b;
451
452    init_res(res_a);
453    init_res(res_b);
454    init_res(res_ab);
455
456    if (!strcmp(a->title, b->title)) {
457        res_ab->title = a->title;
458    } else {
459        res_a->title = a->title;
460        res_b->title = b->title;
461    }
462
463    objs_a = objs_clone(a->objs);
464    objs_b = objs_clone(b->objs);
465
466    merge_wires(objs_a);
467    merge_wires(objs_b);
468
469    while (objs_a) {
470        next_a = objs_a->next;
471        for (obj_b = &objs_b; *obj_b; obj_b = &(*obj_b)->next)
472            if (obj_eq(objs_a, *obj_b, 0)) {
473                struct sch_obj *tmp = *obj_b;
474
475                add_obj(res_ab, objs_a);
476                *obj_b = tmp->next;
477                free_obj(tmp);
478                goto found;
479            }
480        add_obj(res_a, objs_a);
481found:
482        objs_a = next_a;
483    }
484    res_b->objs = objs_b;
485}
486
487
488void delta_free(struct sheet *d)
489{
490    struct sch_obj *next;
491
492    while (d->objs) {
493        next = d->objs->next;
494        free_obj(d->objs);
495        d->objs = next;
496    }
497}
498

Archive Download this file

Branches:
master



interactive