mirror of
git://projects.qi-hardware.com/eda-tools.git
synced 2024-12-23 09:15:31 +02:00
501 lines
9.9 KiB
C
501 lines
9.9 KiB
C
/*
|
|
* kicad/dwg.c - Complex drawing functions for KiCad items
|
|
*
|
|
* Written 2016 by Werner Almesberger
|
|
* Copyright 2016 by Werner Almesberger
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
|
|
#define _GNU_SOURCE /* for asprintf */
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
|
|
#include "misc/util.h"
|
|
#include "gfx/misc.h"
|
|
#include "gfx/style.h"
|
|
#include "gfx/text.h"
|
|
#include "gfx/gfx.h"
|
|
#include "kicad/dwg.h"
|
|
|
|
|
|
/* ----- Helper functions -------------------------------------------------- */
|
|
|
|
|
|
static void bbox_from_poly(struct dwg_bbox *bbox, unsigned n,
|
|
const int *vx, const int *vy)
|
|
{
|
|
unsigned i;
|
|
int xmax, ymax;
|
|
|
|
bbox->x = xmax = *vx;
|
|
bbox->y = ymax = *vy;
|
|
for (i = 1; i != n; i++) {
|
|
if (vx[i] < bbox->x)
|
|
bbox->x = vx[i];
|
|
if (vy[i] < bbox->y)
|
|
bbox->y = vy[i];
|
|
if (vx[i] > xmax)
|
|
xmax = vx[i];
|
|
if (vy[i] > ymax)
|
|
ymax = vy[i];
|
|
}
|
|
bbox->w = xmax - bbox->x + 1;
|
|
bbox->h = ymax - bbox->y + 1;
|
|
}
|
|
|
|
|
|
/* ----- Labels ------------------------------------------------------------ */
|
|
|
|
|
|
enum box_type { // ___
|
|
box_simple, // [___]
|
|
box_left, // <___]
|
|
box_right, // [___>
|
|
box_both, // <___>
|
|
};
|
|
|
|
|
|
static enum box_type flip_box(enum box_type box)
|
|
{
|
|
switch (box) {
|
|
case box_simple:
|
|
return box_simple;
|
|
case box_left:
|
|
return box_right;
|
|
case box_right:
|
|
return box_left;
|
|
case box_both:
|
|
return box_both;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
|
|
void dwg_label(int x, int y, const char *s, int dir, int dim,
|
|
enum dwg_shape shape, struct dwg_bbox *bbox)
|
|
{
|
|
struct text txt = {
|
|
.s = s,
|
|
.size = dim,
|
|
.x = x,
|
|
.y = y,
|
|
.rot = 0,
|
|
.hor = 0,
|
|
.vert = text_min,
|
|
};
|
|
int dx = 0, dy = 0;
|
|
|
|
switch (dir) {
|
|
case 0: /* right */
|
|
txt.rot = 0;
|
|
txt.hor = text_min;
|
|
dy = 1;
|
|
break;
|
|
case 1: /* up */
|
|
txt.rot = 90;
|
|
txt.hor = text_min;
|
|
dx = -1;
|
|
break;
|
|
case 2: /* left */
|
|
txt.rot = 0;
|
|
txt.hor = text_max;
|
|
dy = 1;
|
|
break;
|
|
case 3: /* down */
|
|
txt.rot = 90;
|
|
txt.hor = text_max;
|
|
dx = -1;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
txt.y -= dy * LABEL_OFFSET;
|
|
txt.x += dx * LABEL_OFFSET;
|
|
text_fig(&txt, COLOR_LABEL, LAYER_LABEL);
|
|
}
|
|
|
|
|
|
void dwg_glabel(int x, int y, const char *s, int dir, int dim,
|
|
enum dwg_shape shape, struct dwg_bbox *bbox)
|
|
{
|
|
struct text txt = {
|
|
.s = s,
|
|
.size = dim,
|
|
.x = x,
|
|
.y = y,
|
|
.rot = 0,
|
|
.hor = 0,
|
|
.vert = text_mid,
|
|
};
|
|
int n = 6;
|
|
int vx[7];
|
|
int vy[7];
|
|
int half = (dim >> 1) + GLABEL_OFFSET;
|
|
enum box_type box;
|
|
int dx, shift_flat, shift_tip;
|
|
bool anchor_right = 1;
|
|
char *tag;
|
|
|
|
switch (shape) {
|
|
case dwg_unspec:
|
|
box = box_simple;
|
|
break;
|
|
case dwg_in:
|
|
box = box_right;
|
|
break;
|
|
case dwg_out:
|
|
box = box_left;
|
|
break;
|
|
case dwg_bidir:
|
|
box = box_both;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
switch (dir) {
|
|
case 0: /* left */
|
|
txt.rot = 0;
|
|
txt.hor = text_max;
|
|
dx = -1;
|
|
break;
|
|
case 1: /* up */
|
|
txt.rot = 90;
|
|
txt.hor = text_min;
|
|
dx = 1;
|
|
box = flip_box(box);
|
|
anchor_right = !anchor_right;
|
|
break;
|
|
case 2: /* right */
|
|
txt.rot = 0;
|
|
txt.hor = text_min;
|
|
dx = 1;
|
|
box = flip_box(box);
|
|
anchor_right = !anchor_right;
|
|
break;
|
|
case 3: /* down */
|
|
txt.rot = 90;
|
|
txt.hor = text_max;
|
|
dx = -1;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
shift_flat = dx * GLABEL_OFFSET;
|
|
shift_tip = dx * (GLABEL_OFFSET + half);
|
|
|
|
switch (box) {
|
|
case box_simple:
|
|
n = 5;
|
|
text_shift(&txt, txt.hor, text_mid, shift_flat, 0);
|
|
text_rel(&txt, text_min, text_min,
|
|
-GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
|
|
text_rel(&txt, text_max, text_min,
|
|
GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
|
|
text_rel(&txt, text_max, text_max,
|
|
GLABEL_OFFSET, -GLABEL_OFFSET, vx + 3, vy + 3);
|
|
text_rel(&txt, text_min, text_max,
|
|
-GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
|
|
break;
|
|
case box_right:
|
|
text_shift(&txt, txt.hor, text_mid,
|
|
anchor_right ? shift_tip : shift_flat, 0);
|
|
text_rel(&txt, text_min, text_min,
|
|
-GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
|
|
text_rel(&txt, text_max, text_min,
|
|
GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
|
|
text_rel(&txt, text_max, text_mid, GLABEL_OFFSET + half, 0,
|
|
vx + 3, vy + 3);
|
|
text_rel(&txt, text_max, text_max,
|
|
GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
|
|
text_rel(&txt, text_min, text_max,
|
|
-GLABEL_OFFSET, -GLABEL_OFFSET, vx + 5, vy + 5);
|
|
break;
|
|
case box_left:
|
|
text_shift(&txt, txt.hor, text_mid,
|
|
anchor_right ? shift_flat : shift_tip, 0);
|
|
text_rel(&txt, text_min, text_min,
|
|
-GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
|
|
text_rel(&txt, text_max, text_min,
|
|
GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
|
|
text_rel(&txt, text_max, text_max,
|
|
GLABEL_OFFSET, -GLABEL_OFFSET, vx + 3, vy + 3);
|
|
text_rel(&txt, text_min, text_max,
|
|
-GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
|
|
text_rel(&txt, text_min, text_mid, -GLABEL_OFFSET- half, 0,
|
|
vx + 5, vy + 5);
|
|
break;
|
|
case box_both:
|
|
n = 7;
|
|
text_shift(&txt, txt.hor, text_mid, shift_tip, 0);
|
|
text_rel(&txt, text_min, text_min,
|
|
-GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
|
|
text_rel(&txt, text_max, text_min,
|
|
GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
|
|
text_rel(&txt, text_max, text_mid, GLABEL_OFFSET + half, 0,
|
|
vx + 3, vy + 3);
|
|
text_rel(&txt, text_max, text_max,
|
|
GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
|
|
text_rel(&txt, text_min, text_max,
|
|
-GLABEL_OFFSET, -GLABEL_OFFSET, vx + 5, vy + 5);
|
|
text_rel(&txt, text_min, text_mid, -GLABEL_OFFSET- half, 0,
|
|
vx + 6, vy + 6);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
text_fig(&txt, COLOR_GLABEL, LAYER_GLABEL);
|
|
|
|
vx[0] = vx[n - 1];
|
|
vy[0] = vy[n - 1];
|
|
gfx_poly(n, vx, vy, COLOR_GLABEL, COLOR_NONE, LAYER_GLABEL);
|
|
|
|
if (bbox)
|
|
bbox_from_poly(bbox, n, vx, vy);
|
|
|
|
if (asprintf(&tag, "G:%s", s)) {}
|
|
gfx_tag(tag, n, vx, vy);
|
|
}
|
|
|
|
|
|
static int make_box(enum box_type box, int h, int *vx, int *vy)
|
|
{
|
|
int r = h / 2;
|
|
|
|
switch (box) {
|
|
case box_simple:
|
|
vx[0] = 0;
|
|
vy[0] = -r;
|
|
vx[1] = 2 * r;
|
|
vy[1] = -r;
|
|
vx[2] = 2 * r;
|
|
vy[2] = r;
|
|
vx[3] = 0;
|
|
vy[3] = r;
|
|
return 4;
|
|
case box_right:
|
|
vx[0] = 0;
|
|
vy[0] = -r;
|
|
vx[1] = r;
|
|
vy[1] = -r;
|
|
vx[2] = 2 * r;
|
|
vy[2] = 0;
|
|
vx[3] = r;
|
|
vy[3] = r;
|
|
vx[4] = 0;
|
|
vy[4] = r;
|
|
return 5;
|
|
case box_left:
|
|
vx[0] = r;
|
|
vy[0] = -r;
|
|
vx[1] = 2 * r;
|
|
vy[1] = -r;
|
|
vx[2] = 2 * r;
|
|
vy[2] = r;
|
|
vx[3] = r;
|
|
vy[3] = r;
|
|
vx[4] = 0;
|
|
vy[4] = 0;
|
|
return 5;
|
|
case box_both:
|
|
vx[0] = 0;
|
|
vy[0] = 0;
|
|
vx[1] = r;
|
|
vy[1] = -r;
|
|
vx[2] = 2 * r;
|
|
vy[2] = 0;
|
|
vx[3] = r;
|
|
vy[3] = r;
|
|
return 4;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
|
|
void dwg_hlabel(int x, int y, const char *s, int dir, int dim,
|
|
enum dwg_shape shape, struct dwg_bbox *bbox)
|
|
{
|
|
struct text txt = {
|
|
.s = s,
|
|
.size = dim,
|
|
.x = x,
|
|
.y = y,
|
|
.rot = 0,
|
|
.hor = 0,
|
|
.vert = text_mid,
|
|
};
|
|
int vx[6], vy[6];
|
|
int rot;
|
|
int n, i;
|
|
|
|
switch (shape) {
|
|
case dwg_unspec:
|
|
n = make_box(box_simple, dim, vx, vy);
|
|
break;
|
|
case dwg_in:
|
|
n = make_box(box_left, dim, vx, vy);
|
|
break;
|
|
case dwg_out:
|
|
n = make_box(box_right, dim, vx, vy);
|
|
break;
|
|
case dwg_tri:
|
|
case dwg_bidir:
|
|
n = make_box(box_both, dim, vx, vy);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
switch (dir) {
|
|
case 0: /* right */
|
|
rot = 180;
|
|
txt.hor = text_max;
|
|
break;
|
|
case 1: /* up */
|
|
rot = 90;
|
|
txt.hor = text_min;
|
|
break;
|
|
case 2: /* left */
|
|
rot = 0;
|
|
txt.hor = text_min;
|
|
break;
|
|
case 3: /* down */
|
|
rot = 270;
|
|
txt.hor = text_max;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
txt.x += rx((1 + HLABEL_OFFSET_F) * dim, 0, rot);
|
|
txt.y += ry((1 + HLABEL_OFFSET_F) * dim, 0, rot);
|
|
|
|
for (i = 0; i != n; i++) {
|
|
int tmp;
|
|
|
|
tmp = x + rx(vx[i], vy[i], rot);
|
|
vy[i] = y + ry(vx[i], vy[i], rot);
|
|
vx[i] = tmp;
|
|
}
|
|
|
|
vx[n] = vx[0];
|
|
vy[n] = vy[0];
|
|
|
|
txt.rot = rot % 180;
|
|
|
|
text_fig(&txt, COLOR_HLABEL, LAYER_HLABEL);
|
|
gfx_poly(n + 1, vx, vy, COLOR_HLABEL, COLOR_NONE, LAYER_HLABEL);
|
|
}
|
|
|
|
|
|
/* ----- Text -------------------------------------------------------------- */
|
|
|
|
|
|
void dwg_text(int x, int y, const char *s, int dir, int dim,
|
|
enum dwg_shape shape, struct dwg_bbox *bbox)
|
|
{
|
|
struct text txt = {
|
|
.s = s,
|
|
.size = dim,
|
|
.x = x,
|
|
.y = y,
|
|
.rot = 0,
|
|
.hor = text_min,
|
|
.vert = text_min,
|
|
};
|
|
|
|
switch (dir) {
|
|
case 0: /* right */
|
|
break;
|
|
case 1: /* up */
|
|
text_rot(&txt, 90);
|
|
break;
|
|
case 2: /* left */
|
|
txt.hor = text_max;
|
|
break;
|
|
case 3: /* down */
|
|
text_rot(&txt, 90);
|
|
txt.hor = text_max;
|
|
break;
|
|
default:
|
|
assert(2 + 2 == 5);
|
|
}
|
|
|
|
text_fig(&txt, COLOR_TEXT, LAYER_TEXT);
|
|
}
|
|
|
|
|
|
/* ----- Connections ------------------------------------------------------- */
|
|
|
|
|
|
void dwg_junction(int x, int y)
|
|
{
|
|
gfx_circ(x, y, JUNCTION_R, COLOR_NONE, COLOR_WIRE, LAYER_WIRES);
|
|
}
|
|
|
|
|
|
void dwg_noconn(int x, int y)
|
|
{
|
|
int vx[2] = { x - NOCONN_LEN, x + NOCONN_LEN };
|
|
int vy[2] = { y - NOCONN_LEN, y + NOCONN_LEN };
|
|
|
|
gfx_poly(2, vx, vy, COLOR_NOCONN, COLOR_NONE, LAYER_NOCONN);
|
|
swap(vy[0], vy[1]);
|
|
gfx_poly(2, vx, vy, COLOR_NOCONN, COLOR_NONE, LAYER_NOCONN);
|
|
}
|
|
|
|
|
|
/* ----- Lines ------------------------------------------------------------- */
|
|
|
|
/*
|
|
* We can't use gfx_poly because lines are dashed and we don't have that
|
|
* property at the gfx_poly API.
|
|
*
|
|
* Since dashing may produce different results between going from A to B and
|
|
* going from B to A, we enforce a common direction, so that pixel diffs will
|
|
* treat reversed lines as still equal.
|
|
*/
|
|
|
|
void dwg_line(int sx, int sy, int ex, int ey)
|
|
{
|
|
if (sx < ex || (sx == ex && sy < ey))
|
|
gfx_line(sx, sy, ex, ey, COLOR_SHEET_DWG, LAYER_LINES);
|
|
else
|
|
gfx_line(ex, ey, sx, sy, COLOR_SHEET_DWG, LAYER_LINES);
|
|
}
|
|
|
|
|
|
/* ----- Wires and busses -------------------------------------------------- */
|
|
|
|
|
|
void dwg_wire(int sx, int sy, int ex, int ey)
|
|
{
|
|
int vx[] = { sx, ex };
|
|
int vy[] = { sy, ey };
|
|
|
|
// WIDTH_WIRE
|
|
gfx_poly(2, vx, vy, COLOR_WIRE, COLOR_NONE, LAYER_WIRES);
|
|
}
|
|
|
|
|
|
void dwg_bus(int sx, int sy, int ex, int ey)
|
|
{
|
|
int vx[] = { sx, ex };
|
|
int vy[] = { sy, ey };
|
|
|
|
// WIDTH_BUS
|
|
gfx_poly(2, vx, vy, COLOR_BUS, COLOR_NONE, LAYER_BUSSES);
|
|
}
|