Moved fped over to /trunk/eda

git-svn-id: http://svn.openmoko.org/trunk/eda/fped@5374 99fdad57-331a-0410-800a-d7fa5415bdb3
This commit is contained in:
werner 2009-08-03 16:12:47 +00:00
commit e455b37abb
47 changed files with 6482 additions and 0 deletions

113
Makefile Normal file
View File

@ -0,0 +1,113 @@
#
# Makefile - Makefile of fped, the footprint editor
#
# Written 2009 by Werner Almesberger
# Copyright 2009 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.
#
OBJS = fped.o expr.o coord.o obj.o inst.o util.o error.o \
unparse.o \
cpp.o lex.yy.o y.tab.o \
gui.o gui_util.o gui_style.o gui_inst.o gui_status.o gui_canvas.o \
gui_icons.o
XPMS = point.xpm vec.xpm frame.xpm \
line.xpm rect.xpm pad.xpm circ.xpm arc.xpm meas.xpm
CFLAGS_GTK = `pkg-config --cflags gtk+-2.0`
LIBS_GTK = `pkg-config --libs gtk+-2.0`
CFLAGS_WARN=-Wall -Wshadow -Wmissing-prototypes \
-Wmissing-declarations
CFLAGS=-g $(CFLAGS_GTK) -DCPP='"cpp"' $(CFLAGS_WARN)
SLOPPY=-Wno-unused -Wno-implicit-function-declaration -Wno-missing-prototypes \
-Wno-missing-declarations
LDLIBS = -lm -lfl $(LIBS_GTK)
YACC=bison -y
YYFLAGS=-v
# ----- Verbosity control -----------------------------------------------------
CPP := $(CPP) # make sure changing CC won't affect CPP
CC_normal := $(CC)
YACC_normal := $(YACC)
LEX_normal := $(LEX)
DEPEND_normal = \
$(CPP) $(CFLAGS) -MM -MG *.c >.depend || \
{ rm -f .depend; exit 1; }
CC_quiet = @echo " CC " $@ && $(CC_normal)
YACC_quiet = @echo " YACC " $@ && $(YACC_normal)
LEX_quiet = @echo " LEX " $@ && $(LEX_normal)
GEN_quiet = @echo " GENERATE " $@ &&
DEPEND_quiet = @echo " DEPENDENCIES" && $(DEPEND_normal)
ifeq ($(V),1)
CC = $(CC_normal)
LEX = $(LEX_normal)
YACC = $(YACC_normal)
GEN =
DEPEND = $(DEPEND_normal)
else
CC = $(CC_quiet)
LEX = $(LEX_quiet)
YACC = $(YACC_quiet)
GEN = $(GEN_quiet)
DEPEND = $(DEPEND_quiet)
endif
# ----- Rules -----------------------------------------------------------------
.PHONY: all dep depend clean
.SUFFIXES: .fig .xpm
# generate 26x26 pixels icons, then drop the 1-pixel frame
# this adds a magenta border
# sed '/2 2 0 1 /{s//2 2 0 15 /;s/ 0 7 / 22 7 /;}' $< | \
.fig.xpm:
fig2dev -L xpm -Z 0.32 -S 4 $< | \
convert -crop 24x24+1+1 - - | \
sed s/xpm__/xpm_`basename $@ .xpm`/ >$@
all: fped
fped: $(OBJS)
$(CC) -o $@ $(OBJS) $(LDLIBS)
lex.yy.c: fpd.l y.tab.h
$(LEX) fpd.l
lex.yy.o: lex.yy.c y.tab.h
$(CC) -c $(CFLAGS) $(SLOPPY) lex.yy.c
y.tab.c y.tab.h: fpd.y
$(YACC) $(YYFLAGS) -d fpd.y
y.tab.o: y.tab.c
$(CC) -c $(CFLAGS) $(SLOPPY) y.tab.c
gui_icons.o: $(XPMS:%=icons/%)
# ----- Dependencies ----------------------------------------------------------
dep depend .depend: lex.yy.c y.tab.h y.tab.c
$(DEPEND)
ifeq (.depend,$(wildcard .depend))
include .depend
endif
# ----- Cleanup ---------------------------------------------------------------
clean:
rm -f $(OBJS) $(XPMS:%=icons/%)
rm -f lex.yy.c y.tab.c y.tab.h y.output .depend

339
README Normal file
View File

@ -0,0 +1,339 @@
fped - Footprint editor
=======================
fped is an editor that allows the interactive creation of footprints of
electronic components. Footprint definitions are stored in a text format
that resembles a programming language.
The language is constrained such that anything that can be expressed in
the textual definition also has a straightforward equivalent operation
that can be performed through the GUI.
Motivation
----------
KiCad already includes a footprint ("module") editor, so why do we need
a new one ? The issue with footprint generation for KiCad is that the
built-in module editor is basically a drawing program that only captures
the result of the module author's interpretation of a footprint drawing,
but does not include the steps that led to this construction.
Furthermore, accurate measuring of dimensions in the drawing can only be
done manually in the module editor, which makes review difficult and
time-consuming.
In fped, the construction process is made explicit and each step can be
expressed in terms of the parameters that appear in the vendor's
drawing. Dimensions can be explicitly measured and the results can be
included in the graphical output generated by fped.
Directly using parameters and construction steps from the reference
drawing reduces the risk of mistakes. Visualizing the construction
process and verificative measurements helps efficient and accurate
review.
Footprint definition file format
--------------------------------
Footprint definitions are stored in text files. The program "fped" reads
and (soon) writes such files, visualizes their content, and provides a
graphical editor for them.
The syntax is unique and draws from elements of a variety of languages
commonly found on unix systems. One specialty is that there are no
reserved words - the language keywords appear only at the beginning of
a line and can thus be recognized as such without restricting their use
for identifiers. This reduces the risk of creating incompatibilities
with existing designs when introduction future language features.
fped uses the C preprocessor for comments, conditional compilation,
and - to a limited extent - also macros. Long lines can be split by
ending them with a backslash.
Geometry model
--------------
The geometry model consists of frames, vectors, and objects. The shape of
objects is defined by a number of points. These points are produced by
concatenating vectors.
E.g., to draw a line from (1mm, 1mm) to (2mm, 2mm), one would make a
vector from the origin to (1mm, 1mm) and one either from the origin or
from the previous vector to (2mm, 2mm), and then make a line connecting
the two points.
Units
- - -
fped can calculate in mm and mil. Units are specified by following a
number with "mm" or "mil", separated by zero or more spaces or tabs.
Examples:
1mm
2 mil
Units can be mixed in calculations, e.g.,
set a = 1mm+20mil
set b = 10*1mm
All values used as dimensions must be either mm or mil.
Vectors
- - - -
Vectors can be anonymous or they can be named for future reference:
vec <base> ( <x-expr>, <y-expr> )
<identifier>: vec <base> ( <x-expr>, <y-expr> )
The base can be one of the following items:
- @ is the origin of the frame containing the vector
- . is the end of the previous vector in this frame
- <identifier> is the name of a previous vector in the same frame
The following example would draw the line described in the previous
section:
a: vec @(1mm, 1mm)
b: vec .(1mm, 1mm)
line a b
Silk screen objects
- - - - - - - - - -
The output of fped is a footprint definition that contains pads and silk
screen drawings (we may add more layers in the future). These items are
called "objects". Their geometry is defined through points obtained with
vectors.
A line connects two points:
line <point-a> <point-b> [<width>]
The points can be specified with @, ., and an identifier, just like
a vector base. The option width specifies the thickness of the silk
screen line. If omitted, a hard-coded default of 15 mil is used.
A rectangle has sides parallel to the x and y axis and is defined
by two diagonally opposite corners:
rect <point-a> <point-b> [<width>]
A circle is defined by its center and a point on the circle:
circ <center> <point> [<width>]
This example draws a unit circle:
vec @(1mm, 0mm)
circ @ .
An arc is like a circle, but the part of the circle drawn is determined
by two points. The first point determines the radius and the starting
angle. The second point only determines the end angle but its distance
from the center is ignored.
arc <center> <radius> <end> [<width>]
The arc is drawn in a counter-clockwise direction. The following example
draws an arc of the unit circle in the x > 0, y > 0 quadrant:
from: vec @(1mm, 0mm)
to: vec @(0mm, 1mm)
arc @ from to
Pads
- -
Pads are similar to rectangles, but they also have a name.
pad "<name>" <point-a> <point-b>
Variables can be expanded in a pad's name by prefixing their name with
a dollar sign. The ${name} syntax is also available.
Example:
vec @(1mm, 1mm)
pad "1" @ .
Measurements
- - - - - -
Measurements show the distance between two points:
meas <point-a> <point-b> <offset>
The offset is the distance from the imaginary line connecting points A
and B the measurement line is draw:
- if the offset is 0mm, the line will connect A and B
- if the offset is positive, the line would be on the left-hand side when
traveling from A to B
- if the offset is negative , the line would be on the right-hand side when
traveling from A to B
Example:
a: vec @(-1mm, 1mm)
b: vec @(1mm, 1mm)
meas a b 0.2 mm
Frames
- - -
Frames are used to group things and to reuse them multiple times. Frames
must be defined before they can be used:
frame <name> {
... items ...
}
Once defined, a frame is placed at a given location with
frame <name> <point>
The frame definitions must precede all other items in a footprint
description. Frames cannot be defined inside other frames, but frames
can invoke each other recursively.
For example, this puts two unity squares, one centered at (0 mm, 0 mm),
the other at (2 mm, 0 mm):
frame unit_square {
a: vec @(-0.5mm, -0.5mm)
b: vec .(1mm, 1mm)
rect a b
}
frame unit_square @
vec @(2mm, 0mm)
frame unit_square .
Names and variables
-------------------
fped uses several name spaces:
- frame names occupy one global name space
- vector names occupy name spaces delimited by the frame they're
contained in. A vector name is only visible inside the frame in which
it is defined.
- variable names occupy name spaces delimited by the frame they're
contained in. A variable lookup starts in the frame in which the
corresponding expression appears and propagates to outer frames
until the variable is found.
- pads occupy one global name space (this is currently not enforced)
Note that names cannot be redefined. E.g., this does not work:
set a = 1
set a = a+1
The names spaces of frames, vectors, variables, and pads are separate
from each other.
Simple variables
- - - - - - - -
A variable with a single value is defined with the following
assignment syntax:
set <identifier> = <expression>
Example:
set a = b+2
Loops
- - -
A loop is a variable with a range of values:
loop <identifier> = <from>, <to>
The variable assumes all the values i for <from> <= i <= <to>, in
increments of one. E.g.,
loop n = 1, 3
and
loop n = 1, 3
both assigns the values 1, 2, and 3 to the variable "n".
When a loop is executed, the objects contained in the body of the
enclosing frame are generated for each value of the variable. If
a frame contains multiple loops, all possible combinations of the
values are generated.
The following example draws three concentric circles around the
origin, with radii 1, 2, and 3:
loop x = 1, 3
vec @(x*1mm, 0mm)
circ @ .
Tables
- - -
Tables combine values for multiple variables. Like loops, they are
used to iteratively generate objects. A table begins with a row of
variable names, followed by one or more rows with values. Rows are
enclosed in curly braces and their elements are separated by commas.
table
{ <identifier>, ... }
{ <expression>, ... }
...
Like loops, tables are iterated to generate objects. The following
example is equivalent to the one in the previous section:
table
{ x }
{ 1mm }
{ 2mm }
{ 3mm }
vec @(x, 0mm)
circ @ .
Note that we can set the unit of the values directly in this case.
Iteration is performed over rows. All variables of the table are set
to the value in the respective row at the same time. For example, in
table
{ x, y }
{ 1, 2 }
{ 3, 4 }
(x, y) assume the values (1, 2) and (3, 4).
Expressions
-----------
Expressions can contain numeric constants (in non-exponential notation),
variable names, the arithmetic operations +, -, *, /, and unary -.
Parentheses can be used to change precedence.

54
TODO Normal file
View File

@ -0,0 +1,54 @@
Missing features:
- add row selection
- populate input area (still needed: mm/mil, rezoom)
- add vec editor (need to be able to edit name, x, and y)
- add obj editor
- add table/var/loop editor (missing: add col/row, add/del var/table/loop)
- add default unit (combine with grid unit selection ?)
- consider adding auto/mm/mil selection for each dimension
- add measurements (partly done. still needed: find out how to define
non-trivial endpoints, e.g., some vector in last iteration of loop)
- add KiCad output
- add postscript output
- add option to include/omit helper vecs and frames (display and postscript)
Error detection:
- eliminate duplicate instances
Style:
- make column of entry field greedily consume all unallocated space
Bugs:
- default silk width has no business being hard-coded in obj.c
Code cleanup:
- merge edit_unique with edit_name
- merge find_var_in_frame with similar mechanisms in expr.c and fpd.y
- add regression tests
Open decisions:
- decide on table presentation (merge frame and vars into single entity ?)
- Q: should loop be (start, last) or (start, iterations) ? or start ... last ?
- change vector circle color ? (also, highlight on hover ?)
- Q: allow reassignment of vector names ?
A1: no: would cause confusion in GUI (vectors could become orphaned)
A2: yes. but we don't change the linkage.
- Q: how do we handle stacks of objects ?
A: we don't but we make it easy to avoid them, by giving a good zoom,
flexible selection, and by disallowing stacks of identical objects in the
first place.
- Q: add frame arguments ? (e.g., .frame pad(pin_num_offset) ...)
we can already approximate this by introducing an intermediate table that
sets up the arguments (provided that we don't consider vectors as well)
- Q: should we make it a requirement to generate objects only once ?
A: almost certainly yes.
Future directions:
- future: consider using cairo instead of gdk
- live update of value when entering strings and expressions ?
- advanced: non-standard solder mask
- advanced: solder paste exceptions (subtractive, additive)
- advanced: silk line width
- future: when encountering an error after a change, we could try to find the
same element in the old instance, and select it
- future: consider editing off-canvas items in place

164
coord.c Normal file
View File

@ -0,0 +1,164 @@
/*
* coord.c - Coordinate representation and basic operations
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <math.h>
#include "coord.h"
/* ----- unit conversion --------------------------------------------------- */
double mm_to_mil(double mm, int exponent)
{
return mm*pow(MIL_IN_MM, -exponent);
}
double mil_to_mm(double mil, int exponent)
{
return mil*pow(MIL_IN_MM, exponent);
}
/* ----- vector operations ------------------------------------------------- */
struct coord normalize(struct coord v, unit_type len)
{
double f;
f = len/hypot(v.x, v.y);
v.x *= f;
v.y *= f;
return v;
}
struct coord rotate(struct coord v, double angle)
{
double rad = M_PI*angle/180.0;
struct coord res;
res.x = v.x*cos(rad)-v.y*sin(rad);
res.y = v.y*cos(rad)+v.x*sin(rad);
return res;
}
struct coord add_vec(struct coord a, struct coord b)
{
a.x += b.x;
a.y += b.y;
return a;
}
struct coord sub_vec(struct coord a, struct coord b)
{
a.x -= b.x;
a.y -= b.y;
return a;
}
struct coord neg_vec(struct coord v)
{
v.x = -v.x;
v.y = -v.y;
return v;
}
/* ----- distance calculations --------------------------------------------- */
unit_type dist_point(struct coord a, struct coord b)
{
return hypot(a.x-b.x, a.y-b.y);
}
static unit_type dist_line_xy(unit_type px, unit_type py,
unit_type ax, unit_type ay, unit_type bx, unit_type by)
{
unit_type d_min, d;
double a, f;
d_min = hypot(ax-px, ay-py);
d = hypot(bx-px, by-py);
if (d < d_min)
d_min = d;
if (ax != bx || ay != by) {
/*
* We make a the line vector from point B and b the vector from
* B to point P. Then we calculate the projection of b on a.
*/
ax -= bx;
ay -= by;
bx = px-bx;
by = py-by;
a = hypot(ax, ay);
f = ((double) ax*bx+(double) ay*by)/a/a;
if (f >= 0 && f <= 1) {
bx -= f*ax;
by -= f*ay;
d = hypot(bx, by);
if (d < d_min)
d_min = d;
}
}
return d_min;
}
unit_type dist_line(struct coord p, struct coord a, struct coord b)
{
return dist_line_xy(p.x, p.y, a.x, a.y, b.x, b.y);
}
unit_type dist_rect(struct coord p, struct coord min, struct coord max)
{
unit_type d_min, d;
d_min = dist_line_xy(p.x, p.y, min.x, min.y, max.x, min.y);
d = dist_line_xy(p.x, p.y, min.x, min.y, min.x, max.y);
if (d < d_min)
d_min = d;
d = dist_line_xy(p.x, p.y, min.x, max.y, max.x, max.y);
if (d < d_min)
d_min = d;
d = dist_line_xy(p.x, p.y, max.x, min.y, max.x, max.y);
if (d < d_min)
d_min = d;
return d_min;
}
int inside_rect(struct coord p, struct coord min, struct coord max)
{
if (p.x < min.x || p.x > max.x)
return 0;
if (p.y < min.y || p.y > max.y)
return 0;
return 1;
}
unit_type dist_circle(struct coord p, struct coord c, unit_type r)
{
unit_type d;
d = hypot(p.x-c.x, p.y-c.y);
return fabs(d-r);
}

78
coord.h Normal file
View File

@ -0,0 +1,78 @@
/*
* coord.h - Coordinate representation and basic operations
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef COORD_H
#define COORD_H
#include <stdint.h>
#define MICRON_UNITS 10
#define MIL_UNITS (25.4*MICRON_UNITS)
#define MM_UNITS (1000.0*MICRON_UNITS)
#define KICAD_UNIT (10.0*MIL_UNITS)
#define MIL_IN_MM 0.0254
typedef int32_t unit_type;
#define UNIT_ERROR ((unit_type) 1 << (sizeof(unit_type)*8-1))
struct coord {
unit_type x, y;
};
static inline unit_type mil_to_units(double mil)
{
return mil*MIL_UNITS;
}
static inline unit_type mm_to_units(double mm)
{
return mm*MM_UNITS;
}
static inline double units_to_mm(unit_type u)
{
return (double) u/MM_UNITS;
}
static inline double units_to_kicad(unit_type u)
{
return (double) u/KICAD_UNIT;
}
double mm_to_mil(double mm, int exponent);
double mil_to_mm(double mil, int exponent);
struct coord normalize(struct coord v, unit_type len);
struct coord rotate(struct coord v, double angle);
struct coord add_vec(struct coord a, struct coord b);
struct coord sub_vec(struct coord a, struct coord b);
struct coord neg_vec(struct coord v);
unit_type dist_point(struct coord a, struct coord b);
unit_type dist_line(struct coord p, struct coord a, struct coord b);
unit_type dist_rect(struct coord p, struct coord min, struct coord max);
int inside_rect(struct coord p, struct coord min, struct coord max);
unit_type dist_circle(struct coord p, struct coord c, unit_type r);
#endif /* !COORD_H */

217
cpp.c Normal file
View File

@ -0,0 +1,217 @@
/*
* cpp.c - CPP subprocess
*
* Written 2002-2004, 2006, 2008 by Werner Almesberger
* Copyright 2002, 2003 California Institute of Technology
* Copyright 2004, 2006 Werner Almesberger
* Copyright 2008 by OpenMoko, Inc.
*
* 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "cpp.h"
const char *cpp_command = CPP;
static pid_t cpp_pid;
static int cpp_argc = 0;
static const char **cpp_argv = NULL;
static int real_stdin = -1;
void add_cpp_arg(const char *arg)
{
if (!cpp_argc)
cpp_argc = 1;
cpp_argv = realloc(cpp_argv,sizeof(const char *)*(cpp_argc+1));
if (!cpp_argv) {
perror("realloc");
exit(1);
}
if (cpp_argc == 1)
cpp_argv[0] = cpp_command;
if (arg) {
arg = strdup(arg);
if (!arg) {
perror("strdup");
exit(1);
}
}
cpp_argv[cpp_argc++] = arg;
}
void add_cpp_Wp(const char *arg)
{
char *tmp = strdup(arg);
char *curr,*end;
if (!tmp) {
perror("strdup");
exit(1);
}
curr = tmp;
do {
end = strchr(curr,',');
if (end)
*end++ = 0;
add_cpp_arg(curr);
curr = end;
}
while (end);
free(tmp);
}
static void kill_cpp(void)
{
if (cpp_pid)
(void) kill(cpp_pid,SIGTERM);
}
static void run_cpp(const char *name,int fd,int close_fd)
{
char **arg;
int fds[2];
if (pipe(fds) < 0) {
perror("pipe");
exit(1);
}
if (name)
add_cpp_arg(name);
add_cpp_arg(NULL);
cpp_pid = fork();
if (cpp_pid < 0) {
perror("fork");
exit(1);
}
if (!cpp_pid) {
if (close(fds[0]) < 0) {
perror("close");
exit(1);
}
if (close_fd != -1 && close(close_fd) < 0) {
perror("close");
exit(1);
}
if (fd != -1 && dup2(fd,0) < 0) {
perror("dup2");
exit(1);
}
if (dup2(fds[1],1) < 0) {
perror("dup2");
exit(1);
}
if (execvp(cpp_command,(char **) cpp_argv) < 0) {
/* prototype is weird */
perror(cpp_command);
exit(1);
}
/* not reached */
}
if (close(fds[1]) < 0) {
perror("close");
exit(1);
}
real_stdin = dup(0);
if (real_stdin < 0) {
perror("dup");
exit(1);
}
if (fd != -1 && close(fd) < 0) {
perror("close");
exit(1);
}
if (dup2(fds[0],0) < 0) {
perror("dup2");
exit(1);
}
for (arg = (char **) cpp_argv+1; *arg; arg++)
free(*arg);
free(cpp_argv);
cpp_argv = NULL;
cpp_argc = 0;
}
void run_cpp_on_file(const char *name)
{
run_cpp(name,name ? -1 : 0,-1);
atexit(kill_cpp);
}
void run_cpp_on_string(const char *str)
{
int fds[2];
pid_t pid;
int left,wrote;
if (pipe(fds) < 0) {
perror("pipe");
exit(1);
}
run_cpp(NULL,fds[0],fds[1]);
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (!pid) {
for (left = strlen(str); left; left -= wrote) {
wrote = write(fds[1],str,left);
if (wrote < 0)
break; /* die silently */
str += wrote;
}
exit(0);
}
if (close(fds[1]) < 0) {
perror("close");
exit(1);
}
atexit(kill_cpp);
}
void reap_cpp(void)
{
int status;
cpp_pid = 0;
if (waitpid(cpp_pid,&status,0) < 0) {
perror("waitpid");
exit(1);
}
if (!status) {
if (dup2(real_stdin,0) < 0) {
perror("dup2");
exit(1);
}
return;
}
if (WIFEXITED(status))
exit(WEXITSTATUS(status));
if (WIFSIGNALED(status))
fprintf(stderr,"cpp terminated with signal %d\n",WTERMSIG(status));
else
fprintf(stderr,"cpp terminated with incomprehensible status %d\n",
status);
exit(1);
}

26
cpp.h Normal file
View File

@ -0,0 +1,26 @@
/*
* cpp.h - CPP subprocess
*
* Written 2002, 2003, 2008 by Werner Almesberger
* Copyright 2002, 2003 Caltech Netlab FAST project
* Copyright 2008 by OpenMoko, Inc.
*
* 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.
*/
#ifndef CPP_H
#define CPP_H
extern const char *cpp_command;
void add_cpp_arg(const char *arg);
void add_cpp_Wp(const char *arg);
void run_cpp_on_file(const char *name); /* NULL for stdin */
void run_cpp_on_string(const char *str);
void reap_cpp(void);
#endif /* CPP_H */

76
error.c Normal file
View File

@ -0,0 +1,76 @@
/*
* error.c - Error reporting
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include "util.h"
#include "error.h"
extern char *yytext;
int lineno = 1;
void (*reporter)(const char *s) = report_to_stderr;
void yyerrorf(const char *fmt, ...)
{
va_list ap;
char *buf;
int n;
va_start(ap, fmt);
n = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
buf = alloc_size(n+1);
va_start(ap, fmt);
vsnprintf(buf, n+1, fmt, ap);
va_end(ap);
fail(buf);
free(buf);
}
void yyerror(const char *s)
{
yyerrorf("%s", s);
}
void report_parse_error(const char *s)
{
fprintf(stderr, "%d: %s near \"%s\" ", lineno, s, yytext);
exit(1);
}
void report_to_stderr(const char *s)
{
fprintf(stderr, "%s\n", s);
exit(1);
}
void fail(const char *fmt, ...)
{
va_list ap;
char *s;
va_start(ap, fmt);
s = stralloc_vprintf(fmt, ap);
va_end(ap);
reporter(s);
free(s);
}

30
error.h Normal file
View File

@ -0,0 +1,30 @@
/*
* error.h - Error reporting
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef ERROR_H
#define ERROR_H
extern int lineno;
extern void (*reporter)(const char *s);
void yyerrorf(const char *fmt, ...);
void yyerror(const char *s);
void report_to_stderr(const char *s);
void report_parse_error(const char *s);
void fail(const char *fmt, ...);
#endif /* !ERROR_H */

423
expr.c Normal file
View File

@ -0,0 +1,423 @@
/*
* expr.c - Expressions and values
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <stdlib.h>
#include <math.h>
#include "util.h"
#include "error.h"
#include "obj.h"
#include "unparse.h"
#include "expr.h"
struct num undef = { .type = nt_none };
/* ----- error reporting --------------------------------------------------- */
void fail_expr(const struct expr *expr)
{
char *s;
s = unparse(expr);
fail("in \"%s\" at line %d", s, expr->lineno);
free(s);
}
/* ----- unit conversion --------------------------------------------------- */
const char *str_unit(struct num n)
{
if (n.exponent == 0)
return "";
if (n.type == nt_mm) {
switch (n.exponent) {
case -2:
return "mm^-2";
case -1:
return "mm^-1";
case 1:
return "mm";
case 2:
return "mm^2";
default:
abort();
}
}
if (n.type == nt_mil) {
switch (n.exponent) {
case -2:
return "mil^(-2)";
case -1:
return "mil^(-1)";
case 1:
return "mil";
case 2:
return "mil^2";
default:
abort();
}
}
abort();
}
int to_unit(struct num *n)
{
if (!is_distance(*n)) {
fail("%s^%d is not a distance",
n->type == nt_mm ? "mm" : n->type == nt_mil ? "mil" : "?",
n->exponent);
return 0;
}
switch (n->type) {
case nt_mil:
n->n = mil_to_units(n->n);
break;
case nt_mm:
n->n = mm_to_units(n->n);
break;
default:
abort();
}
return 1;
}
/* ----- primary expressions ----------------------------------------------- */
struct num op_num(const struct expr *self, const struct frame *frame)
{
return self->u.num;
}
struct num eval_var(const struct frame *frame, const char *name)
{
const struct table *table;
const struct loop *loop;
const struct value *value;
struct var *var;
struct num res;
for (table = frame->tables; table; table = table->next) {
value = table->curr_row->values;
for (var = table->vars; var; var = var->next) {
if (var->name == name) {
if (var->visited) {
fail("recursive evaluation through "
"\"%s\"", name);
return undef;
}
var->visited = 1;
res = eval_num(value->expr, frame);
var->visited = 0;
return res;
}
value = value->next;
}
}
for (loop = frame->loops; loop; loop = loop->next)
if (loop->var.name == name) {
if (!loop->initialized) {
fail("uninitialized loop \"%s\"", name);
return undef;
}
return make_num(loop->curr_value);
}
if (frame->curr_parent)
return eval_var(frame->curr_parent, name);
return undef;
}
struct num op_var(const struct expr *self, const struct frame *frame)
{
struct num res;
res = eval_var(frame, self->u.var);
if (is_undef(res))
fail("undefined variable \"%s\"", self->u.var);
return res;
}
/* ----- arithmetic -------------------------------------------------------- */
static struct num compatible_sum(struct num *a, struct num *b)
{
struct num res;
if (a->type != b->type) {
if (a->type == nt_mil) {
a->type = nt_mm;
a->n = mil_to_mm(a->n, a->exponent);
}
if (b->type == nt_mil) {
b->type = nt_mm;
b->n = mil_to_mm(b->n, a->exponent);
}
}
if (a->exponent != b->exponent) {
fail("incompatible exponents (%d, %d)",
a->exponent, b->exponent);
return undef;
}
res.type = a->type;
res.exponent = a->exponent;
return res;
}
static struct num compatible_mult(struct num *a, struct num *b,
int exponent)
{
struct num res;
if (a->type != b->type) {
if (a->type == nt_mil) {
a->type = nt_mm;
a->n = mil_to_mm(a->n, a->exponent);
}
if (b->type == nt_mil) {
b->type = nt_mm;
b->n = mil_to_mm(b->n, b->exponent);
}
}
res.type = a->type;
res.exponent = exponent;
return res;
}
struct num op_minus(const struct expr *self, const struct frame *frame)
{
struct num res;
res = eval_num(self->u.op.a, frame);
if (!is_undef(res))
res.n = -res.n;
return res;
}
#define BINARY \
struct num a, b, res; \
\
a = eval_num(self->u.op.a, frame); \
if (is_undef(a)) \
return undef; \
b = eval_num(self->u.op.b, frame); \
if (is_undef(b)) \
return undef;
struct num op_add(const struct expr *self, const struct frame *frame)
{
BINARY;
res = compatible_sum(&a, &b);
if (is_undef(res))
return undef;
res.n = a.n+b.n;
return res;
}
struct num op_sub(const struct expr *self, const struct frame *frame)
{
BINARY;
res = compatible_sum(&a, &b);
if (is_undef(res))
return undef;
res.n = a.n-b.n;
return res;
}
struct num op_mult(const struct expr *self, const struct frame *frame)
{
BINARY;
res = compatible_mult(&a, &b, a.exponent+b.exponent);
res.n = a.n*b.n;
return res;
}
struct num op_div(const struct expr *self, const struct frame *frame)
{
BINARY;
if (!b.n) {
fail("Division by zero");
return undef;
}
res = compatible_mult(&a, &b, a.exponent-b.exponent);
res.n = a.n/b.n;
return res;
}
/* ----- expression construction ------------------------------------------- */
struct expr *new_op(op_type op)
{
struct expr *expr;
expr = alloc_type(struct expr);
expr->op = op;
expr->lineno = lineno;
return expr;
}
struct expr *binary_op(op_type op, struct expr *a, struct expr *b)
{
struct expr *expr;
expr = new_op(op);
expr->u.op.a = a;
expr->u.op.b = b;
return expr;
}
char *eval_str(const struct frame *frame, const struct expr *expr)
{
abort();
}
struct num eval_num(const struct expr *expr, const struct frame *frame)
{
return expr->op(expr, frame);
}
/* ----- string expansion -------------------------------------------------- */
char *expand(const char *name, const struct frame *frame)
{
int len = strlen(name);
char *buf = alloc_size(len+1);
char num_buf[100]; /* enough :-) */
const char *s, *s0;
char *var;
const char *var_unique;
struct num value;
int i, value_len;
i = 0;
for (s = name; *s; s++) {
if (*s != '$') {
buf[i++] = *s;
continue;
}
s0 = ++s;
if (*s != '{') {
while (is_id_char(*s, s == s0))
s++;
if (s == s0)
goto invalid;
var = strnalloc(s0, s-s0);
len -= s-s0+1;
s--;
} else {
s++;
while (*s != '}') {
if (!*s) {
fail("unfinished \"${...}\"");
goto fail;
}
if (!is_id_char(*s, s == s0+1))
goto invalid;
s++;
}
var = strnalloc(s0+1, s-s0-1);
len -= s-s0+2;
}
if (!frame)
continue;
var_unique = unique(var);
free(var);
value = eval_var(frame, var_unique);
if (is_undef(value)) {
fail("undefined variable \"%s\"", var_unique);
goto fail;
}
value_len = snprintf(num_buf, sizeof(num_buf), "%lg%s",
value.n, str_unit(value));
len += value_len;
buf = realloc(buf, len);
if (!buf)
abort();
strcpy(buf+i, num_buf);
i += value_len;
}
buf[i] = 0;
return buf;
invalid:
fail("invalid character in variable name");
fail:
free(buf);
return NULL;
}
/* ----- expression-only parser -------------------------------------------- */
void scan_expr(const char *s);
int yyparse(void);
struct expr *expr_result;
struct expr *parse_expr(const char *s)
{
scan_expr(s);
return yyparse() ? NULL : expr_result;
}
static void vacate_op(struct expr *expr)
{
if (expr->op == &op_num || expr->op == &op_var)
return;
if (expr->op == &op_minus) {
free_expr(expr->u.op.a);
return;
}
if (expr->op == &op_add || expr->op == &op_sub ||
expr->op == &op_mult || expr->op == &op_div) {
free_expr(expr->u.op.a);
free_expr(expr->u.op.b);
return;
}
abort();
}
void free_expr(struct expr *expr)
{
vacate_op(expr);
free(expr);
}

120
expr.h Normal file
View File

@ -0,0 +1,120 @@
/*
* expr.h - Expressions and values
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef EXPR_H
#define EXPR_H
#include <math.h>
#define UNDEF HUGE_VAL
struct frame;
struct expr;
enum num_type {
nt_none,
nt_mm,
nt_mil,
};
struct num {
enum num_type type;
int exponent;
double n;
};
typedef struct num (*op_type)(const struct expr *self,
const struct frame *frame);
struct expr {
op_type op;
union {
struct num num;
const char *var;
struct {
struct expr *a;
struct expr *b;
} op;
} u;
int lineno;
};
extern struct num undef;
#define is_undef(num) ((num).type == nt_none)
#define is_dimensionless(num) (!(num).exponent)
static inline int is_distance(struct num num)
{
return (num.type == nt_mm || num.type == nt_mil) && num.exponent == 1;
}
void fail_expr(const struct expr *expr);
const char *str_unit(struct num n);
static inline struct num make_num(double n)
{
struct num res;
res.type = nt_mm;
res.exponent = 0;
res.n = n;
return res;
}
static inline struct num make_mil(double mil)
{
struct num res;
res.type = nt_mil;
res.exponent = 1;
res.n = mil;
return res;
}
int to_unit(struct num *n);
struct num op_num(const struct expr *self, const struct frame *frame);
struct num op_var(const struct expr *self, const struct frame *frame);
struct num op_minus(const struct expr *self, const struct frame *frame);
struct num op_add(const struct expr *self, const struct frame *frame);
struct num op_sub(const struct expr *self, const struct frame *frame);
struct num op_mult(const struct expr *self, const struct frame *frame);
struct num op_div(const struct expr *self, const struct frame *frame);
struct expr *new_op(op_type op);
struct expr *binary_op(op_type op, struct expr *a, struct expr *b);
struct num eval_var(const struct frame *frame, const char *name);
char *eval_str(const struct frame *frame, const struct expr *expr);
struct num eval_num(const struct expr *expr, const struct frame *frame);
/* if frame == NULL, we only check the syntax without expanding */
char *expand(const char *name, const struct frame *frame);
struct expr *parse_expr(const char *s);
void free_expr(struct expr *expr);
#endif /* !EXPR_H */

135
fpd.l Normal file
View File

@ -0,0 +1,135 @@
%{
/*
* fpd.l - FootPrint Definition language
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <stdlib.h>
#include "util.h"
#include "coord.h"
#include "expr.h"
#include "error.h"
#include "y.tab.h"
int start_token = START_FPD;
static int disable_keywords = 0;
static int is_table = 0;
void scan_empty(void)
{
yy_scan_string("");
}
void scan_expr(const char *s)
{
start_token = START_EXPR;
yy_scan_string(s);
}
%}
/* keywords are only accepted at the beginning of a line */
%s NOKEYWORD
NUM [0-9]+\.?[0-9]*
SP [\t ]*
%%
%{
/*
* Nice hack:
*
* http://www.gnu.org/software/bison/manual/bison.html#
* Multiple-start_002dsymbols
*/
if (start_token) {
int tmp = start_token;
start_token = 0;
return tmp;
}
%}
<INITIAL>"set" { BEGIN(NOKEYWORD);
return TOK_SET; }
<INITIAL>"loop" { BEGIN(NOKEYWORD);
return TOK_LOOP; }
<INITIAL>"frame" { BEGIN(NOKEYWORD);
is_table = 0;
return TOK_FRAME; }
<INITIAL>"table" { BEGIN(NOKEYWORD);
is_table = 1;
return TOK_TABLE; }
<INITIAL>"vec" { BEGIN(NOKEYWORD);
return TOK_VEC; }
<INITIAL>"pad" { BEGIN(NOKEYWORD);
return TOK_PAD; }
<INITIAL>"rect" { BEGIN(NOKEYWORD);
return TOK_RECT; }
<INITIAL>"line" { BEGIN(NOKEYWORD);
return TOK_LINE; }
<INITIAL>"circ" { BEGIN(NOKEYWORD);
return TOK_CIRC; }
<INITIAL>"arc" { BEGIN(NOKEYWORD);
return TOK_ARC; }
<INITIAL>"meas" { BEGIN(NOKEYWORD);
return TOK_MEAS; }
[a-zA-Z_][a-zA-Z_0-9]*: { *strchr(yytext, ':') = 0;
yylval.id = unique(yytext);
return LABEL; }
[a-zA-Z_][a-zA-Z_0-9]* { yylval.id = unique(yytext);
return ID; }
{NUM}{SP}mm { yylval.num.type = nt_mm;
yylval.num.exponent = 1;
sscanf(yytext, "%lf", &yylval.num.n);
return NUMBER; }
{NUM}{SP}mil { yylval.num.type = nt_mil;
yylval.num.exponent = 1;
sscanf(yytext, "%lf", &yylval.num.n);
return NUMBER; }
{NUM} { yylval.num.type = nt_mm; /* mm or mil */
yylval.num.exponent = 0;
sscanf(yytext, "%lf", &yylval.num.n);
return NUMBER; }
\"(\\[^\n\t]|[^\\"\n\t])*\" { *strrchr(yytext, '"') = 0;
yylval.str = stralloc(yytext+1);
return STRING; }
{SP} ;
\n { if (!disable_keywords)
BEGIN(INITIAL);
lineno++; }
"{" { BEGIN(NOKEYWORD);
disable_keywords = is_table;
return '{'; }
"}" { disable_keywords = 0;
return '}'; }
^#\ [0-9]+\ \"[^"]*\"(\ [0-9]+)*\n {
if (!disable_keywords)
BEGIN(INITIAL);
lineno = strtol(yytext+2, NULL, 0); }
. return *yytext;

509
fpd.y Normal file
View File

@ -0,0 +1,509 @@
%{
/*
* fpd.y - FootPrint Definition language
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <stdlib.h>
#include "util.h"
#include "error.h"
#include "expr.h"
#include "obj.h"
extern struct expr *expr_result;
static struct frame *curr_frame;
static struct table *curr_table;
static struct row *curr_row;
static struct frame *last_frame = NULL;
static struct vec *last_vec = NULL;
static struct table **next_table;
static struct loop **next_loop;
static struct vec **next_vec;
static struct obj **next_obj;
static int n_vars, n_values;
static struct frame *find_frame(const char *name)
{
struct frame *f;
for (f = frames; f; f = f->next)
if (f->name == name)
return f;
return NULL;
}
static struct vec *find_vec(const char *name)
{
struct vec *v;
for (v = curr_frame->vecs; v; v = v->next)
if (v->name == name)
return v;
return NULL;
}
static void set_frame(struct frame *frame)
{
curr_frame = frame;
next_table = &frame->tables;
next_loop = &frame->loops;
next_vec = &frame->vecs;
next_obj = &frame->objs;
last_vec = NULL;
}
static void make_var(const char *id, struct expr *expr)
{
struct table *table;
table = zalloc_type(struct table);
table->vars = zalloc_type(struct var);
table->vars->name = id;
table->vars->frame = curr_frame;
table->rows = zalloc_type(struct row);
table->rows->values = zalloc_type(struct value);
table->rows->values->expr = expr;
table->rows->values->row = table->rows;
*next_table = table;
next_table = &table->next;
}
static void make_loop(const char *id, struct expr *from, struct expr *to)
{
struct loop *loop;
loop = alloc_type(struct loop);
loop->var.name = id;
loop->var.next = NULL;
loop->var.frame = curr_frame;
loop->from.expr = from;
loop->from.row = NULL;
loop->from.next = NULL;
loop->to.expr = to;
loop->to.row = NULL;
loop->to.next = NULL;
loop->next = NULL;
loop->active = 0;
loop->initialized = 0;
*next_loop = loop;
next_loop = &loop->next;
}
static struct obj *new_obj(enum obj_type type)
{
struct obj *obj;
obj = alloc_type(struct obj);
obj->type = type;
obj->next = NULL;
return obj;
}
%}
%union {
struct num num;
char *str;
const char *id;
struct expr *expr;
struct frame *frame;
struct table *table;
struct var *var;
struct row *row;
struct value *value;
struct vec *vec;
struct obj *obj;
};
%token START_FPD START_EXPR
%token TOK_SET TOK_LOOP TOK_FRAME TOK_TABLE TOK_VEC
%token TOK_PAD TOK_RECT TOK_LINE TOK_CIRC TOK_ARC TOK_MEAS
%token <num> NUMBER
%token <str> STRING
%token <id> ID LABEL
%type <table> table
%type <var> vars var
%type <row> rows
%type <value> row value
%type <vec> vec base
%type <obj> obj
%type <expr> expr opt_expr add_expr mult_expr unary_expr primary_expr
%%
all:
START_FPD fpd
| START_EXPR expr
{
expr_result = $2;
}
;
fpd:
{
root_frame = zalloc_type(struct frame);
set_frame(root_frame);
}
frame_defs frame_items
{
root_frame->prev = last_frame;
if (last_frame)
last_frame->next = root_frame;
else
frames = root_frame;
}
;
frame_defs:
| frame_defs frame_def
;
frame_def:
TOK_FRAME ID '{'
{
if (find_frame($2)) {
yyerrorf("duplicate frame \"%s\"", $2);
YYABORT;
}
curr_frame = zalloc_type(struct frame);
curr_frame->name = $2;
set_frame(curr_frame);
curr_frame->prev = last_frame;
if (last_frame)
last_frame->next = curr_frame;
else
frames = curr_frame;
last_frame = curr_frame;
}
frame_items '}'
{
set_frame(root_frame);
}
;
frame_items:
| frame_item frame_items
;
frame_item:
table
| TOK_SET ID '=' expr
{
make_var($2, $4);
}
| TOK_LOOP ID '=' expr ',' expr
{
make_loop($2, $4, $6);
}
| vec
| LABEL vec
{
if (find_vec($1)) {
yyerrorf("duplicate vector \"%s\"", $1);
YYABORT;
}
$2->name = $1;
}
| obj
{
*next_obj = $1;
next_obj = &$1->next;
}
;
table:
TOK_TABLE
{
$<table>$ = zalloc_type(struct table);
*next_table = $<table>$;
curr_table = $<table>$;
n_vars = 0;
}
'{' vars '}' rows
{
$$ = $<table>2;
$$->vars = $4;
$$->rows = $6;
$$->active = 0;
next_table = &$$->next;
}
;
vars:
var
{
$$ = $1;
}
| vars ',' var
{
struct var **walk;
$$ = $1;
for (walk = &$$; *walk; walk = &(*walk)->next);
*walk = $3;
}
;
var:
ID
{
$$ = alloc_type(struct var);
$$->name = $1;
$$->frame = curr_frame;
$$->next = NULL;
n_vars++;
}
;
rows:
{
$$ = NULL;
}
| '{'
{
$<row>$ = alloc_type(struct row);
$<row>$->table = curr_table;
curr_row = $<row>$;;
n_values = 0;
}
row '}'
{
if (n_vars != n_values) {
yyerrorf("table has %d variables but row has "
"%d values", n_vars, n_values);
YYABORT;
}
$<row>2->values = $3;
}
rows
{
$$ = $<row>2;
$$->next = $6;
}
;
row:
value
{
$$ = $1;
}
| row ',' value
{
struct value **walk;
$$ = $1;
for (walk = &$$; *walk; walk = &(*walk)->next);
*walk = $3;
}
;
value:
expr
{
$$ = alloc_type(struct value);
$$->expr = $1;
$$->row = curr_row;
$$->next = NULL;
n_values++;
}
;
vec:
TOK_VEC base '(' expr ',' expr ')'
{
$$ = alloc_type(struct vec);
$$->name = NULL;
$$->base = $2;
if ($2)
$2->n_refs++;
$$->x = $4;
$$->y = $6;
$$->n_refs = 0;
$$->frame = curr_frame;
$$->next = NULL;
last_vec = $$;
*next_vec = $$;
next_vec = &$$->next;
}
;
base:
'@'
{
$$ = NULL;
}
| '.'
{
$$ = last_vec;
if (!$$) {
yyerrorf(". without predecessor");
YYABORT;
}
}
| ID
{
$$ = find_vec($1);
if (!$$) {
yyerrorf("unknown vector \"%s\"", $1);
YYABORT;
}
}
;
obj:
TOK_PAD STRING base base
{
$$ = new_obj(ot_pad);
$$->base = $3;
$$->u.pad.name = $2;
$$->u.pad.other = $4;
}
| TOK_RECT base base opt_expr
{
$$ = new_obj(ot_rect);
$$->base = $2;
$$->u.rect.other = $3;
$$->u.rect.width = $4;
}
| TOK_LINE base base opt_expr
{
$$ = new_obj(ot_line);
$$->base = $2;
$$->u.line.other = $3;
$$->u.line.width = $4;
}
| TOK_CIRC base base opt_expr
{
$$ = new_obj(ot_arc);
$$->base = $2;
$$->u.arc.start = $3;
$$->u.arc.end = $3;
$$->u.arc.width = $4;
}
| TOK_ARC base base base opt_expr
{
$$ = new_obj(ot_arc);
$$->base = $2;
$$->u.arc.start = $3;
$$->u.arc.end = $4;
$$->u.arc.width = $5;
}
| TOK_MEAS base base expr
{
$$ = new_obj(ot_meas);
$$->base = $2;
$$->u.meas.other = $3;
$$->u.meas.offset = $4;
}
| TOK_FRAME ID base
{
$$ = new_obj(ot_frame);
$$->base = $3;
$$->u.frame = find_frame($2);
if (!$$->u.frame) {
yyerrorf("unknown frame \"%s\"", $2);
YYABORT;
}
}
;
opt_expr:
{
$$ = NULL;
}
| expr
{
$$ = $1;
}
;
expr:
add_expr
{
$$ = $1;
}
;
add_expr:
mult_expr
{
$$ = $1;
}
| add_expr '+' mult_expr
{
$$ = binary_op(op_add, $1, $3);
}
| add_expr '-' mult_expr
{
$$ = binary_op(op_sub, $1, $3);
}
;
mult_expr:
unary_expr
{
$$ = $1;
}
| mult_expr '*' unary_expr
{
$$ = binary_op(op_mult, $1, $3);
}
| mult_expr '/' unary_expr
{
$$ = binary_op(op_div, $1, $3);
}
;
unary_expr:
primary_expr
{
$$ = $1;
}
| '-' primary_expr
{
$$ = binary_op(op_minus, $2, NULL);
}
;
primary_expr:
NUMBER
{
$$ = new_op(op_num);
$$->u.num = $1;
}
| ID
{
$$ = new_op(op_var);
$$->u.var = $1;
}
| '(' expr ')'
{
$$ = $2;
}
;

57
fped.c Normal file
View File

@ -0,0 +1,57 @@
/*
* fped.c - Footprint editor, main function
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include "cpp.h"
#include "error.h"
#include "obj.h"
#include "inst.h"
#include "gui.h"
extern void scan_empty(void);
extern int yyparse(void);
static void load_file(const char *name)
{
reporter = report_parse_error;
run_cpp_on_file(name);
(void) yyparse();
}
int main(int argc, char **argv)
{
int error;
error = gui_init(&argc, &argv);
if (error)
return error;
if (argc == 1) {
scan_empty();
(void) yyparse();
} else {
load_file(argv[1]);
argc--;
argv++;
}
reporter = report_to_stderr;
if (!instantiate())
return 1;
// inst_debug();
error = gui_main(argc, argv);
if (error)
return error;
return 0;
}

588
gui.c Normal file
View File

@ -0,0 +1,588 @@
/*
* gui.c - Editor GUI core
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gtk/gtk.h>
#include "util.h"
#include "inst.h"
#include "obj.h"
#include "unparse.h"
#include "gui_util.h"
#include "gui_style.h"
#include "gui_status.h"
#include "gui_canvas.h"
#include "gui_icons.h"
#include "gui.h"
GtkWidget *root;
static GtkWidget *frames_box;
static GtkWidget *vars_box;
/* ----- menu bar ---------------------------------------------------------- */
static void make_menu_bar(GtkWidget *vbox)
{
GtkWidget *bar;
GtkWidget *file_menu, *file, *quit;
bar = gtk_menu_bar_new();
gtk_box_pack_start(GTK_BOX(vbox), bar, FALSE, FALSE, 0);
file_menu = gtk_menu_new();
file = gtk_menu_item_new_with_label("File");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), file_menu);
gtk_menu_shell_append(GTK_MENU_SHELL(bar), file);
quit = gtk_menu_item_new_with_label("Quit");
gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit);
g_signal_connect(G_OBJECT(quit), "activate",
G_CALLBACK(gtk_main_quit), NULL);
}
/* ----- variable list ----------------------------------------------------- */
static void add_sep(GtkWidget *box, int size)
{
GtkWidget *sep;
GdkColor black = { 0, 0, 0, 0 };
sep = gtk_drawing_area_new();
gtk_box_pack_start(GTK_BOX(box), sep, FALSE, TRUE, size);
gtk_widget_modify_bg(sep, GTK_STATE_NORMAL, &black);
}
/* ----- variable name editor ---------------------------------------------- */
static int find_var_in_frame(const struct frame *frame, const char *name)
{
const struct table *table;
const struct loop *loop;
const struct var *var;
for (table = frame->tables; table; table = table->next)
for (var = table->vars; var; var = var->next)
if (!strcmp(var->name, name))
return 1;
for (loop = frame->loops; loop; loop = loop->next)
if (!strcmp(loop->var.name, name))
return 1;
return 0;
}
static int validate_var_name(const char *s, void *ctx)
{
struct var *var = ctx;
if (!is_id(s))
return 0;
return !find_var_in_frame(var->frame, s);
}
static void unselect_var(void *data)
{
struct var *var = data;
label_in_box_bg(var->widget, COLOR_VAR_PASSIVE);
}
static void edit_var(struct var *var)
{
inst_select_outside(var, unselect_var);
label_in_box_bg(var->widget, COLOR_VAR_EDITING);
status_set_name(var->name);
edit_unique(&var->name, validate_var_name, var);
}
/* ----- value editor ------------------------------------------------------ */
static void unselect_value(void *data)
{
struct value *value = data;
label_in_box_bg(value->widget,
value->row && value->row->table && value->row->table->curr_row ==
value->row ? COLOR_CHOICE_SELECTED : COLOR_EXPR_PASSIVE);
}
static void edit_value(struct value *value)
{
inst_select_outside(value, unselect_value);
label_in_box_bg(value->widget, COLOR_EXPR_EDITING);
edit_expr(&value->expr);
}
/* ----- assignments ------------------------------------------------------- */
static gboolean assignment_var_select_event(GtkWidget *widget,
GdkEventButton *event, gpointer data)
{
edit_var(data);
return TRUE;
}
static gboolean assignment_value_select_event(GtkWidget *widget,
GdkEventButton *event, gpointer data)
{
edit_value(data);
return TRUE;
}
static void build_assignment(GtkWidget *vbox, struct frame *frame,
struct table *table)
{
GtkWidget *hbox, *field;
char *expr;
if (!table->vars || table->vars->next)
return;
if (!table->rows || table->rows->next)
return;
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
field = label_in_box_new(table->vars->name);
gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
label_in_box_bg(field, COLOR_VAR_PASSIVE);
table->vars->widget = field;
g_signal_connect(G_OBJECT(box_of_label(field)),
"button_press_event",
G_CALLBACK(assignment_var_select_event), table->vars);
gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" = "),
FALSE, FALSE, 0);
expr = unparse(table->rows->values->expr);
field = label_in_box_new(expr);
free(expr);
gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
label_in_box_bg(field, COLOR_EXPR_PASSIVE);
table->rows->values->widget = field;
g_signal_connect(G_OBJECT(box_of_label(field)),
"button_press_event",
G_CALLBACK(assignment_value_select_event), table->rows->values);
}
/* ----- tables ------------------------------------------------------------ */
static gboolean table_var_select_event(GtkWidget *widget,
GdkEventButton *event, gpointer data)
{
edit_var(data);
return TRUE;
}
static gboolean table_value_select_event(GtkWidget *widget,
GdkEventButton *event, gpointer data)
{
edit_value(data);
return TRUE;
}
static void build_table(GtkWidget *vbox, struct frame *frame,
struct table *table)
{
GtkWidget *tab, *field;
GtkWidget *evbox;
struct var *var;
struct row *row;
struct value *value;
int n_vars = 0, n_rows = 0;
char *expr;
GdkColor col;
for (var = table->vars; var; var = var->next)
n_vars++;
for (row = table->rows; row; row = row->next)
n_rows++;
if (n_vars == 1 && n_rows == 1)
return;
evbox = gtk_event_box_new();
gtk_box_pack_start(GTK_BOX(vbox), evbox, FALSE, FALSE, 0);
tab = gtk_table_new(n_rows+1, n_vars, FALSE);
gtk_container_add(GTK_CONTAINER(evbox), tab);
col = get_color(COLOR_VAR_TABLE_SEP);
gtk_widget_modify_bg(GTK_WIDGET(evbox),
GTK_STATE_NORMAL, &col);
gtk_table_set_row_spacings(GTK_TABLE(tab), 1);
gtk_table_set_col_spacings(GTK_TABLE(tab), 1);
n_vars = 0;
for (var = table->vars; var; var = var->next) {
field = label_in_box_new(var->name);
gtk_table_attach_defaults(GTK_TABLE(tab), box_of_label(field),
n_vars, n_vars+1, 0, 1);
label_in_box_bg(field, COLOR_VAR_PASSIVE);
g_signal_connect(G_OBJECT(box_of_label(field)),
"button_press_event",
G_CALLBACK(table_var_select_event), var);
var->widget = field;
n_vars++;
}
n_rows = 0;
for (row = table->rows; row; row = row->next) {
n_vars = 0;
for (value = row->values; value; value = value->next) {
expr = unparse(value->expr);
field = label_in_box_new(expr);
free(expr);
gtk_table_attach_defaults(GTK_TABLE(tab),
box_of_label(field),
n_vars, n_vars+1,
n_rows+1, n_rows+2);
label_in_box_bg(field, table->active == n_rows ?
COLOR_ROW_SELECTED : COLOR_ROW_UNSELECTED);
g_signal_connect(G_OBJECT(box_of_label(field)),
"button_press_event",
G_CALLBACK(table_value_select_event), value);
value->widget = field;
n_vars++;
}
n_rows++;
}
}
/* ----- loops ------------------------------------------------------------- */
static gboolean loop_var_select_event(GtkWidget *widget,
GdkEventButton *event, gpointer data)
{
struct loop *loop = data;
edit_var(&loop->var);
return TRUE;
}
static gboolean loop_from_select_event(GtkWidget *widget,
GdkEventButton *event, gpointer data)
{
struct loop *loop = data;
edit_value(&loop->from);
return TRUE;
}
static gboolean loop_to_select_event(GtkWidget *widget,
GdkEventButton *event, gpointer data)
{
struct loop *loop = data;
edit_value(&loop->to);
return TRUE;
}
static void build_loop(GtkWidget *vbox, struct frame *frame,
struct loop *loop)
{
GtkWidget *hbox, *field;
char *expr;
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
field = label_in_box_new(loop->var.name);
gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
label_in_box_bg(field, COLOR_VAR_PASSIVE);
g_signal_connect(G_OBJECT(box_of_label(field)),
"button_press_event",
G_CALLBACK(loop_var_select_event), loop);
loop->var.widget = field;
gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" = "),
FALSE, FALSE, 0);
expr = unparse(loop->from.expr);
field = label_in_box_new(expr);
free(expr);
gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
label_in_box_bg(field, COLOR_EXPR_PASSIVE);
g_signal_connect(G_OBJECT(box_of_label(field)),
"button_press_event",
G_CALLBACK(loop_from_select_event), loop);
loop->from.widget = field;
gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" ... "),
FALSE, FALSE, 0);
expr = unparse(loop->to.expr);
field = label_in_box_new(expr);
free(expr);
gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
label_in_box_bg(field, COLOR_EXPR_PASSIVE);
g_signal_connect(G_OBJECT(box_of_label(field)),
"button_press_event",
G_CALLBACK(loop_to_select_event), loop);
loop->to.widget = field;
}
/* ----- the list of variables, tables, and loops -------------------------- */
static void build_vars(GtkWidget *vbox, struct frame *frame)
{
struct table *table;
struct loop *loop;
destroy_all_children(GTK_CONTAINER(vbox));
for (table = frame->tables; table; table = table->next) {
add_sep(vbox, 3);
build_assignment(vbox, frame, table);
build_table(vbox, frame, table);
}
for (loop = frame->loops; loop; loop = loop->next) {
add_sep(vbox, 3);
build_loop(vbox, frame, loop);
}
gtk_widget_show_all(vbox);
}
/* ----- frame list -------------------------------------------------------- */
static int validate_frame_name(const char *s, void *ctx)
{
struct frame *f;
if (!is_id(s))
return 0;
for (f = frames; f; f = f->next)
if (f->name && !strcmp(f->name, s))
return 0;
return 1;
}
static void unselect_frame(void *data)
{
struct frame *frame= data;
/*
* "unselect" means in this context that the selection has moved
* elsewhere. However, this does not necessarily change the frame.
* (And, in fact, since we rebuild the frame list anyway, the color
* change here doesn't matter if selecting a different frame.)
* So we revert from "editing" to "selected".
*/
label_in_box_bg(frame->label, COLOR_FRAME_SELECTED);
}
static void edit_frame(struct frame *frame)
{
inst_select_outside(frame, unselect_frame);
label_in_box_bg(frame->label, COLOR_FRAME_EDITING);
status_set_name(frame->name);
edit_unique(&frame->name, validate_frame_name, frame);
}
static void select_frame(struct frame *frame)
{
if (active_frame) {
label_in_box_bg(active_frame->label, COLOR_FRAME_UNSELECTED);
inst_deselect();
}
active_frame = frame;
change_world();
}
static gboolean frame_select_event(GtkWidget *widget, GdkEventButton *event,
gpointer data)
{
if (active_frame != data)
select_frame(data);
else {
if (active_frame->name)
edit_frame(data);
}
return TRUE;
}
static void build_frames(GtkWidget *vbox)
{
struct frame *f;
GtkWidget *label;
destroy_all_children(GTK_CONTAINER(vbox));
for (f = root_frame; f; f = f->prev) {
label = label_in_box_new(f->name ? f->name : "(root)");
gtk_box_pack_start(GTK_BOX(vbox), box_of_label(label),
FALSE, TRUE, 0);
gtk_misc_set_padding(GTK_MISC(label), 2, 2);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
label_in_box_bg(label, active_frame == f ?
COLOR_FRAME_SELECTED : COLOR_FRAME_UNSELECTED);
g_signal_connect(G_OBJECT(box_of_label(label)),
"button_press_event", G_CALLBACK(frame_select_event), f);
f->label = label;
}
gtk_widget_show_all(vbox);
}
/* ----- central screen area ----------------------------------------------- */
static void make_center_area(GtkWidget *vbox)
{
GtkWidget *hbox, *vars, *paned;
GtkWidget *frame_list, *icons;
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
/* Frame list */
frame_list = gtk_scrolled_window_new(NULL, NULL);
gtk_box_pack_start(GTK_BOX(hbox), frame_list, FALSE, TRUE, 0);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(frame_list),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
frames_box = gtk_vbox_new(FALSE, 0);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(frame_list),
frames_box);
build_frames(frames_box);
add_sep(hbox, 2);
paned = gtk_hpaned_new();
gtk_box_pack_start(GTK_BOX(hbox), paned, TRUE, TRUE, 0);
/* Variables */
vars = gtk_scrolled_window_new(NULL, NULL);
gtk_paned_add1(GTK_PANED(paned), vars);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vars),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_size_request(vars, 150, 100);
vars_box = gtk_vbox_new(FALSE, 0);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(vars),
vars_box);
/* Canvas */
gtk_paned_add2(GTK_PANED(paned), make_canvas());
/* Icon bar */
icons = gui_setup_icons(root->window);
gtk_box_pack_end(GTK_BOX(hbox), icons, FALSE, FALSE, 0);
}
/* ----- GUI construction -------------------------------------------------- */
void change_world(void)
{
status_begin_reporting();
instantiate();
label_in_box_bg(active_frame->label, COLOR_FRAME_SELECTED);
build_frames(frames_box);
build_vars(vars_box, active_frame);
redraw();
}
static void make_screen(GtkWidget *window)
{
GtkWidget *vbox;
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
make_menu_bar(vbox);
make_center_area(vbox);
make_status_area(vbox);
}
int gui_init(int *argc, char ***argv)
{
gtk_init(argc, argv);
return 0;
}
int gui_main(int argc, char **argv)
{
root = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(root), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(root), 600, 400);
gtk_window_set_title(GTK_WINDOW(root), "fped");
/* get root->window */
gtk_widget_show_all(root);
g_signal_connect_swapped(G_OBJECT(root), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
make_screen(root);
build_vars(vars_box, root_frame);
gtk_widget_show_all(root);
gui_setup_style(root->window);
init_canvas();
edit_nothing();
select_frame(root_frame);
gtk_main();
return 0;
}

29
gui.h Normal file
View File

@ -0,0 +1,29 @@
/*
* gui.h - Editor GUI core
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef GUI_H
#define GUI_H
#include <gtk/gtk.h>
extern GtkWidget *root;
/* update everything after a model change */
void change_world(void);
int gui_init(int *argc, char ***argv);
int gui_main(int argc, char **argv);
#endif /* !GUI_H */

327
gui_canvas.c Normal file
View File

@ -0,0 +1,327 @@
/*
* gui_canvas.c - GUI, canvas
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <math.h>
#include <gtk/gtk.h>
#include "obj.h"
#include "inst.h"
#include "gui_inst.h"
#include "gui_style.h"
#include "gui_status.h"
#include "gui.h"
#include "gui_canvas.h"
static struct draw_ctx ctx;
static struct coord curr_pos;
static struct coord user_origin = { 0, 0 };
/* ----- status display ---------------------------------------------------- */
static void update_zoom(void)
{
status_set_zoom("x%d", ctx.scale);
}
static void update_pos(struct coord pos)
{
status_set_sys_pos("X %5.2lf Y %5.2lf mm",
units_to_mm(pos.x), units_to_mm(pos.y));
status_set_user_pos("x %5.2lf y %5.2lf mm",
units_to_mm(pos.x-user_origin.x), units_to_mm(pos.y-user_origin.y));
}
/* ----- coordinate system ------------------------------------------------- */
static void center(void)
{
struct bbox bbox;
bbox = inst_get_bbox();
ctx.center.x = (bbox.min.x+bbox.max.x)/2;
ctx.center.y = (bbox.min.y+bbox.max.y)/2;
}
static void auto_scale(void)
{
struct bbox bbox;
unit_type h, w;
int sx, sy;
float aw, ah;
bbox = inst_get_bbox();
aw = ctx.widget->allocation.width;
ah = ctx.widget->allocation.height;
h = bbox.max.x-bbox.min.x;
w = bbox.max.y-bbox.min.y;
aw -= 2*CANVAS_CLEARANCE;
ah -= 2*CANVAS_CLEARANCE;
if (aw < 1)
aw = 1;
if (ah < 1)
ah = 1;
sx = ceil(h/aw);
sy = ceil(w/ah);
ctx.scale = sx > sy ? sx : sy > 0 ? sy : 1;
update_zoom();
}
/* ----- drawing ----------------------------------------------------------- */
void redraw(void)
{
float aw, ah;
aw = ctx.widget->allocation.width;
ah = ctx.widget->allocation.height;
gdk_draw_rectangle(ctx.widget->window, gc_bg, TRUE, 0, 0, aw, ah);
inst_draw(&ctx);
}
/* ----- drag -------------------------------------------------------------- */
static void drag_left(struct coord pos)
{
}
static void drag_middle(struct coord pos)
{
}
static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event,
gpointer data)
{
struct coord pos = canvas_to_coord(&ctx, event->x, event->y);
curr_pos.x = event->x;
curr_pos.y = event->y;
if (event->state & GDK_BUTTON1_MASK)
drag_left(pos);
if (event->state & GDK_BUTTON2_MASK)
drag_middle(pos);
update_pos(pos);
return TRUE;
}
/* ----- button press and release ------------------------------------------ */
static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
gpointer data)
{
struct coord pos = canvas_to_coord(&ctx, event->x, event->y);
const struct inst *prev;
switch (event->button) {
case 1:
prev = selected_inst;
inst_deselect();
inst_select(&ctx, pos);
if (prev != selected_inst)
redraw();
break;
case 2:
ctx.center = pos;
redraw();
break;
}
return TRUE;
}
static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
gpointer data)
{
return TRUE;
}
/* ----- zoom control ------------------------------------------------------ */
static void zoom_in(struct coord pos)
{
if (ctx.scale < 2)
return;
ctx.scale /= 2;
ctx.center.x = (ctx.center.x+pos.x)/2;
ctx.center.y = (ctx.center.y+pos.y)/2;
update_zoom();
redraw();
}
static void zoom_out(struct coord pos)
{
struct bbox bbox;
bbox = inst_get_bbox();
bbox.min = translate(&ctx, bbox.min);
bbox.max = translate(&ctx, bbox.max);
if (bbox.min.x >= 0 && bbox.max.y >= 0 &&
bbox.max.x < ctx.widget->allocation.width &&
bbox.min.y < ctx.widget->allocation.height)
return;
ctx.scale *= 2;
ctx.center.x = 2*ctx.center.x-pos.x;
ctx.center.y = 2*ctx.center.y-pos.y;
update_zoom();
redraw();
}
static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event,
gpointer data)
{
struct coord pos = canvas_to_coord(&ctx, event->x, event->y);
switch (event->direction) {
case GDK_SCROLL_UP:
zoom_in(pos);
break;
case GDK_SCROLL_DOWN:
zoom_out(pos);
break;
default:
/* ignore */;
}
return TRUE;
}
/* ----- keys -------------------------------------------------------------- */
static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event,
gpointer data)
{
struct coord pos = canvas_to_coord(&ctx, curr_pos.x, curr_pos.y);
switch (event->keyval) {
case ' ':
user_origin = pos;
update_pos(pos);
break;
}
return TRUE;
}
/* ----- expose event ------------------------------------------------------ */
static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event,
gpointer data)
{
static int first = 1;
if (first) {
init_canvas();
first = 0;
}
redraw();
return TRUE;
}
/* ----- enter/leave ------------------------------------------------------- */
static gboolean enter_notify_event(GtkWidget *widget, GdkEventCrossing *event,
gpointer data)
{
gtk_widget_grab_focus(widget);
return TRUE;
}
static gboolean leave_notify_event(GtkWidget *widget, GdkEventCrossing *event,
gpointer data)
{
return TRUE;
}
/* ----- canvas setup ------------------------------------------------------ */
/*
* Note that we call init_canvas twice: first to make sure we'll make it safely
* through select_frame, and the second time to set the geometry for the actual
* screen.
*/
void init_canvas(void)
{
center();
auto_scale();
}
GtkWidget *make_canvas(void)
{
GtkWidget *canvas;
GdkColor black = { 0, 0, 0, 0 };
/* Canvas */
canvas = gtk_drawing_area_new();
gtk_widget_modify_bg(canvas, GTK_STATE_NORMAL, &black);
g_signal_connect(G_OBJECT(canvas), "motion_notify_event",
G_CALLBACK(motion_notify_event), NULL);
g_signal_connect(G_OBJECT(canvas), "button_press_event",
G_CALLBACK(button_press_event), NULL);
g_signal_connect(G_OBJECT(canvas), "button_release_event",
G_CALLBACK(button_release_event), NULL);
g_signal_connect(G_OBJECT(canvas), "scroll_event",
G_CALLBACK(scroll_event), NULL);
GTK_WIDGET_SET_FLAGS(canvas, GTK_CAN_FOCUS);
g_signal_connect(G_OBJECT(canvas), "key_press_event",
G_CALLBACK(key_press_event), NULL);
g_signal_connect(G_OBJECT(canvas), "expose_event",
G_CALLBACK(expose_event), NULL);
g_signal_connect(G_OBJECT(canvas), "enter_notify_event",
G_CALLBACK(enter_notify_event), NULL);
g_signal_connect(G_OBJECT(canvas), "leave_notify_event",
G_CALLBACK(leave_notify_event), NULL);
gtk_widget_set_events(canvas,
GDK_EXPOSE | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
GDK_KEY_PRESS_MASK |
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
GDK_SCROLL |
GDK_POINTER_MOTION_MASK);
ctx.widget = canvas;
return canvas;
}

25
gui_canvas.h Normal file
View File

@ -0,0 +1,25 @@
/*
* gui_canvas.h - GUI, canvas
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef GUI_CANVAS_H
#define GUI_CANVAS_H
#include <gtk/gtk.h>
void redraw(void);
GtkWidget *make_canvas(void);
void init_canvas(void);
#endif /* !GUI_CANVAS_H */

87
gui_icons.c Normal file
View File

@ -0,0 +1,87 @@
/*
* gui_icons.c - GUI, icon bar
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <gtk/gtk.h>
#include "gui_util.h"
#include "gui_icons.h"
#include "icons/arc.xpm"
#include "icons/circ.xpm"
#include "icons/frame.xpm"
#include "icons/line.xpm"
#include "icons/meas.xpm"
#include "icons/pad.xpm"
#include "icons/point.xpm"
#include "icons/rect.xpm"
#include "icons/vec.xpm"
static GtkToolItem *icon_button(GtkWidget *bar, GdkDrawable *drawable,
char **xpm, GtkToolItem *last)
{
GdkPixmap *pixmap;
GtkWidget *image;
GtkToolItem *item;
pixmap = gdk_pixmap_create_from_xpm_d(drawable, NULL, NULL, xpm);
image = gtk_image_new_from_pixmap(pixmap, NULL);
/*
* gtk_radio_tool_button_new_from_widget is *huge*. we try to do things in a
* more compact way.
*/
#if 0
if (last)
item = gtk_radio_tool_button_new_from_widget(
GTK_RADIO_TOOL_BUTTON(last));
else
item = gtk_radio_tool_button_new(NULL);
gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(item), image);
#else
item = gtk_tool_item_new();
gtk_container_add(GTK_CONTAINER(item), image);
gtk_container_set_border_width(GTK_CONTAINER(item), 1);
#endif
gtk_toolbar_insert(GTK_TOOLBAR(bar), item, -1);
return item;
}
GtkWidget *gui_setup_icons(GdkDrawable *drawable)
{
GtkWidget *bar;
GtkToolItem *last;
bar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_ICONS);
gtk_toolbar_set_orientation(GTK_TOOLBAR(bar),
GTK_ORIENTATION_VERTICAL);
//gtk_container_set_border_width(GTK_CONTAINER(bar), 5);
last = icon_button(bar, drawable, xpm_point, NULL);
last = icon_button(bar, drawable, xpm_vec, last);
last = icon_button(bar, drawable, xpm_frame, last);
last = icon_button(bar, drawable, xpm_pad, last);
last = icon_button(bar, drawable, xpm_line, last);
last = icon_button(bar, drawable, xpm_rect, last);
last = icon_button(bar, drawable, xpm_circ, last);
last = icon_button(bar, drawable, xpm_arc, last);
last = icon_button(bar, drawable, xpm_meas, last);
return bar;
}

22
gui_icons.h Normal file
View File

@ -0,0 +1,22 @@
/*
* gui_icons.h - GUI, icon bar
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef GUI_ICONS_H
#define GUI_ICONS_H
#include <gtk/gtk.h>
GtkWidget *gui_setup_icons(GdkDrawable *drawable);
#endif /* !GUI_ICONS_H */

421
gui_inst.c Normal file
View File

@ -0,0 +1,421 @@
/*
* gui_inst.c - GUI, instance functions
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <stdlib.h>
#include <math.h>
#include <gtk/gtk.h>
#include "util.h"
#include "inst.h"
#include "gui.h"
#include "gui_util.h"
#include "gui_style.h"
#include "gui_inst.h"
#define DA GDK_DRAWABLE(ctx->widget->window)
/* ----- coordinate translation -------------------------------------------- */
struct coord translate(const struct draw_ctx *ctx, struct coord pos)
{
pos.x -= ctx->center.x;
pos.y -= ctx->center.y;
pos.x /= ctx->scale;
pos.y /= ctx->scale;
pos.y = -pos.y;
pos.x += ctx->widget->allocation.width/2;
pos.y += ctx->widget->allocation.height/2;
//fprintf(stderr, "%d %d\n", (int) pos.x, (int) pos.y);
return pos;
}
struct coord canvas_to_coord(const struct draw_ctx *ctx, int x, int y)
{
struct coord pos;
x -= ctx->widget->allocation.width/2;
y -= ctx->widget->allocation.height/2;
y = -y;
pos.x = x*ctx->scale+ctx->center.x;
pos.y = y*ctx->scale+ctx->center.y;
return pos;
}
/* ----- drawing primitives ------------------------------------------------ */
static void draw_arc(struct draw_ctx *ctx, GdkGC *gc, int fill,
int x, int y, int r, double a1, double a2)
{
if (a1 == a2)
a2 = a1+360;
gdk_draw_arc(DA, gc, fill, x-r, y-r, 2*r, 2*r, a1*64, (a2-a1)*64);
}
static void draw_circle(struct draw_ctx *ctx, GdkGC *gc, int fill,
int x, int y, int r)
{
draw_arc(ctx, gc, fill, x, y, r, 0, 360);
}
static void draw_eye(struct draw_ctx *ctx, GdkGC *gc, struct coord center,
int r1, int r2)
{
draw_circle(ctx, gc, TRUE, center.x, center.y, r1);
draw_circle(ctx, gc, FALSE, center.x, center.y, r2);
}
#define MAX_POINTS 10
static void draw_poly(struct draw_ctx *ctx, GdkGC *gc, int fill,
const struct coord *points, int n_points)
{
GdkPoint gp[MAX_POINTS];
int i;
if (n_points > MAX_POINTS)
abort();
for (i = 0; i != n_points; i++) {
gp[i].x = points[i].x;
gp[i].y = points[i].y;
}
if (fill)
gdk_draw_polygon(DA, gc, fill, gp, n_points);
else {
gdk_draw_line(DA, gc, gp[0].x, gp[0].y, gp[1].x, gp[1].y);
gdk_draw_line(DA, gc, gp[1].x, gp[1].y, gp[2].x, gp[2].y);
}
}
static void draw_arrow(struct draw_ctx *ctx, GdkGC *gc, int fill,
struct coord from, struct coord to, int len, double angle)
{
struct coord p[3];
struct coord side;
side = normalize(sub_vec(to, from), len);
p[0] = add_vec(to, rotate(side, 180-angle));
p[1] = to;
p[2] = add_vec(to, rotate(side, 180+angle));
draw_poly(ctx, gc, fill, p, 3);
}
static enum mode get_mode(struct inst *self)
{
if (selected_inst == self)
return mode_selected;
if (self->active)
return self->in_path ? mode_active_in_path : mode_active;
return self->in_path ? mode_inactive_in_path : mode_inactive;
}
/* ----- vec --------------------------------------------------------------- */
unit_type gui_dist_vec(struct inst *self, struct coord pos, unit_type scale)
{
unit_type d;
d = dist_point(pos, self->u.rect.end)/scale;
return d > VEC_EYE_R ? -1 : d;
}
/*
* The circles at a vector's tip enjoy the highest selection priority. However,
* users will probably also expected a click on a nicely exposed stem to work.
* So we give it second look after having exhausted all other options.
*/
unit_type gui_dist_vec_fallback(struct inst *self, struct coord pos,
unit_type scale)
{
unit_type d;
d = dist_line(pos, self->base, self->u.rect.end)/scale;
return d > SELECT_R ? -1 : d;
}
void gui_draw_vec(struct inst *self, struct draw_ctx *ctx)
{
struct coord from = translate(ctx, self->base);
struct coord to = translate(ctx, self->u.rect.end);
GdkGC *gc;
gc = gc_vec[get_mode(self)];
draw_arrow(ctx, gc, TRUE, from, to, VEC_ARROW_LEN, VEC_ARROW_ANGLE);
gdk_draw_line(DA, gc, from.x, from.y, to.x, to.y);
draw_circle(ctx, gc, FALSE, to.x, to.y, VEC_EYE_R);
}
/* ----- line -------------------------------------------------------------- */
unit_type gui_dist_line(struct inst *self, struct coord pos, unit_type scale)
{
unit_type r, d;
r = self->u.rect.width/scale/2;
if (r < SELECT_R)
r = SELECT_R;
d = dist_line(pos, self->bbox.min, self->bbox.max)/scale;
return d > r ? -1 : d;
}
void gui_draw_line(struct inst *self, struct draw_ctx *ctx)
{
struct coord min = translate(ctx, self->base);
struct coord max = translate(ctx, self->u.rect.end);
GdkGC *gc;
gc = gc_obj[get_mode(self)];
set_width(gc, self->u.rect.width/ctx->scale);
gdk_draw_line(DA, gc, min.x, min.y, max.x, max.y);
}
/* ----- rect -------------------------------------------------------------- */
unit_type gui_dist_rect(struct inst *self, struct coord pos, unit_type scale)
{
unit_type r, d;
r = self->u.rect.width/scale/2;
if (r < SELECT_R)
r = SELECT_R;
d = dist_rect(pos, self->bbox.min, self->bbox.max)/scale;
return d > r ? -1 : d;
}
void gui_draw_rect(struct inst *self, struct draw_ctx *ctx)
{
struct coord min = translate(ctx, self->bbox.min);
struct coord max = translate(ctx, self->bbox.max);
GdkGC *gc;
gc = gc_obj[get_mode(self)];
set_width(gc, self->u.rect.width/ctx->scale);
gdk_draw_rectangle(DA, gc, FALSE,
min.x, max.y, max.x-min.x, min.y-max.y);
}
/* ----- pad --------------------------------------------------------------- */
unit_type gui_dist_pad(struct inst *self, struct coord pos, unit_type scale)
{
unit_type d;
if (inside_rect(pos, self->bbox.min, self->bbox.max))
return SELECT_R;
d = dist_rect(pos, self->bbox.min, self->bbox.max)/scale;
return d > SELECT_R ? -1 : d;
}
void gui_draw_pad(struct inst *self, struct draw_ctx *ctx)
{
struct coord min = translate(ctx, self->bbox.min);
struct coord max = translate(ctx, self->bbox.max);
GdkGC *gc;
struct coord c;
unit_type h, w;
gc = gc_pad[get_mode(self)];
gdk_draw_rectangle(DA, gc, TRUE,
min.x, max.y, max.x-min.x, min.y-max.y);
gc = gc_ptext[get_mode(self)];
c = add_vec(min, max);
h = min.y-max.y;
w = max.x-min.x;
render_text(DA, gc, c.x/2, c.y/2, w <= h ? 0 : 90, self->u.name,
PAD_FONT, 0.5, 0.5,
w-2*PAD_BORDER, h-2*PAD_BORDER);
}
/* ----- arc --------------------------------------------------------------- */
static struct coord rotate_r(struct coord center, unit_type r, double angle)
{
struct coord res;
angle = angle/180.0*M_PI;
res.x = center.x+r*cos(angle);
res.y = center.y+r*sin(angle);
return res;
}
unit_type gui_dist_arc(struct inst *self, struct coord pos, unit_type scale)
{
struct coord c = self->base;
struct coord p;
unit_type r, d_min, d;
double angle, a2;
r = self->u.arc.width/scale/2;
if (r < SELECT_R)
r = SELECT_R;
/* check endpoints */
p = rotate_r(c, self->u.arc.r, self->u.arc.a1);
d_min = hypot(pos.x-p.x, pos.y-p.y);
p = rotate_r(c, self->u.arc.r, self->u.arc.a2);
d = hypot(pos.x-p.x, pos.y-p.y);
if (d < d_min)
d_min = d;
if (d_min/scale <= r)
return d;
/* distance from the circle containing the arc */
d = dist_circle(pos, c, self->u.arc.r)/scale;
if (d > r)
return -1;
if (self->u.arc.a1 == self->u.arc.a2)
return d;
/* see if we're close to the part that's actually drawn */
angle = atan2(pos.y-c.y, pos.x-c.x)/M_PI*180.0;
if (angle < 0)
angle += 180;
a2 = self->u.arc.a2;
if (a2 < self->u.arc.a1)
a2 += 180;
return angle >= self->u.arc.a1 && angle <= a2 ? d : -1;
}
void gui_draw_arc(struct inst *self, struct draw_ctx *ctx)
{
struct coord center = translate(ctx, self->base);
GdkGC *gc;
gc = gc_obj[get_mode(self)];
set_width(gc, self->u.arc.width/ctx->scale);
draw_arc(ctx, gc, FALSE, center.x, center.y,
self->u.arc.r/ctx->scale, self->u.arc.a1, self->u.arc.a2);
}
/* ----- meas -------------------------------------------------------------- */
static struct coord offset_vec(const struct inst *self)
{
struct coord a, b, res;
double f;
a = self->base;
b = self->u.meas.end;
res.x = a.y-b.y;
res.y = b.x-a.x;
if (res.x == 0 && res.y == 0)
return res;
f = self->u.meas.offset/hypot(res.x, res.y);
res.x *= f;
res.y *= f;
return res;
}
unit_type gui_dist_meas(struct inst *self, struct coord pos, unit_type scale)
{
struct coord a, b, off;
unit_type d;
off = offset_vec(self);
a = add_vec(self->base, off);
b = add_vec(self->u.meas.end, off);
d = dist_line(pos, a, b)/scale;
return d > SELECT_R ? -1 : d;
}
void gui_draw_meas(struct inst *self, struct draw_ctx *ctx)
{
struct coord a0, b0, a1, b1, off, c, d;
GdkGC *gc;
char *s;
off = offset_vec(self);
a0 = translate(ctx, self->base);
b0 = translate(ctx, self->u.meas.end);
a1 = translate(ctx, add_vec(self->base, off));
b1 = translate(ctx, add_vec(self->u.meas.end, off));
gc = gc_meas[get_mode(self)];
gdk_draw_line(DA, gc, a0.x, a0.y, a1.x, a1.y);
gdk_draw_line(DA, gc, b0.x, b0.y, b1.x, b1.y);
gdk_draw_line(DA, gc, a1.x, a1.y, b1.x, b1.y);
draw_arrow(ctx, gc, FALSE, a1, b1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE);
draw_arrow(ctx, gc, FALSE, b1, a1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE);
c = add_vec(a1, b1);
d = sub_vec(b0, a0);
s = stralloc_printf("%lgmm",
units_to_mm(dist_point(self->base, self->u.meas.end)));
render_text(DA, gc, c.x/2, c.y/2, -atan2(d.y, d.x)/M_PI*180, s,
MEAS_FONT, 0.5, -MEAS_BASELINE_OFFSET,
dist_point(a1, b1)-1.5*MEAS_ARROW_LEN, 0);
free(s);
}
/* ----- frame ------------------------------------------------------------- */
void gui_draw_frame(struct inst *self, struct draw_ctx *ctx)
{
struct coord center = translate(ctx, self->base);
struct coord corner = { self->bbox.min.x, self->bbox.max.y };
GdkGC *gc;
gc = gc_frame[get_mode(self)];
draw_eye(ctx, gc, center, FRAME_EYE_R1, FRAME_EYE_R2);
if (!self->u.frame.ref->name)
return;
corner = translate(ctx, corner);
corner.x -= FRAME_CLEARANCE;
corner.y -= FRAME_CLEARANCE;
gdk_draw_line(DA, gc, corner.x, corner.y,
corner.x+FRAME_SHORT_X, corner.y);
gdk_draw_line(DA, gc, corner.x, corner.y,
corner.x, corner.y+FRAME_SHORT_Y);
render_text(DA, gc, corner.x, corner.y, 0, self->u.frame.ref->name,
FRAME_FONT, 0, -FRAME_BASELINE_OFFSET, 0, 0);
}

50
gui_inst.h Normal file
View File

@ -0,0 +1,50 @@
/*
* gui_inst.h - GUI, instance functions
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef GUI_INST_H
#define GUI_INST_H
#include <gtk/gtk.h>
#include "coord.h"
#include "inst.h"
struct draw_ctx {
GtkWidget *widget;
int scale;
struct coord center;
};
struct coord translate(const struct draw_ctx *ctx, struct coord pos);
struct coord canvas_to_coord(const struct draw_ctx *ctx, int x, int y);
unit_type gui_dist_vec(struct inst *self, struct coord pos, unit_type scale);
unit_type gui_dist_vec_fallback(struct inst *self, struct coord pos,
unit_type scale);
unit_type gui_dist_line(struct inst *self, struct coord pos, unit_type scale);
unit_type gui_dist_rect(struct inst *self, struct coord pos, unit_type scale);
unit_type gui_dist_pad(struct inst *self, struct coord pos, unit_type scale);
unit_type gui_dist_arc(struct inst *self, struct coord pos, unit_type scale);
unit_type gui_dist_meas(struct inst *self, struct coord pos, unit_type scale);
void gui_draw_vec(struct inst *self, struct draw_ctx *ctx);
void gui_draw_line(struct inst *self, struct draw_ctx *ctx);
void gui_draw_rect(struct inst *self, struct draw_ctx *ctx);
void gui_draw_pad(struct inst *self, struct draw_ctx *ctx);
void gui_draw_arc(struct inst *self, struct draw_ctx *ctx);
void gui_draw_meas(struct inst *self, struct draw_ctx *ctx);
void gui_draw_frame(struct inst *self, struct draw_ctx *ctx);
#endif /* !GUI_INST_H */

476
gui_status.c Normal file
View File

@ -0,0 +1,476 @@
/*
* gui_status.c - GUI, status area
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
#include "util.h"
#include "coord.h"
#include "error.h"
#include "unparse.h"
#include "gui_util.h"
#include "gui_style.h"
#include "gui.h"
#include "gui_status.h"
struct edit_ops {
int (*changed)(GtkWidget *widget, const char *s, void *ctx);
int (*activate)(GtkWidget *widget, const char *s, void *ctx);
};
static struct edit_ops *edit_ops = NULL;
static void *edit_ctx;
/* ----- setter functions -------------------------------------------------- */
static GtkWidget *status_name, *status_entry;
static GtkWidget *status_x, *status_y;
static GtkWidget *status_r, *status_angle;
static GtkWidget *status_sys_pos, *status_user_pos;
static GtkWidget *status_zoom, *status_grid;
static GtkWidget *status_msg;
static void set_label(GtkWidget *label, const char *fmt, va_list ap)
{
char *s;
s = stralloc_vprintf(fmt, ap);
gtk_label_set_text(GTK_LABEL(label), s);
free(s);
}
#define SETTER(name) \
void status_set_##name(const char *fmt, ...) \
{ \
va_list ap; \
\
va_start(ap, fmt); \
set_label(status_##name, fmt, ap); \
va_end(ap); \
}
SETTER(name)
SETTER(x)
SETTER(y)
SETTER(r)
SETTER(angle)
SETTER(sys_pos)
SETTER(user_pos)
SETTER(zoom)
SETTER(grid)
/* ----- complex status updates -------------------------------------------- */
void status_set_xy(struct coord coord)
{
status_set_x("x = %5.2f mm", units_to_mm(coord.x));
status_set_y("y = %5.2f mm", units_to_mm(coord.y));
}
static void entry_color(const char *color)
{
GdkColor col;
col = get_color(color);
gtk_widget_modify_base(GTK_WIDGET(status_entry),
GTK_STATE_NORMAL, &col);
}
/* ----- identifier fields ------------------------------------------------- */
struct edit_unique_ctx {
const char **s;
int (*validate)(const char *s, void *ctx);
void *ctx;
};
static int unique_changed(GtkWidget *widget, const char *s, void *ctx)
{
const struct edit_unique_ctx *unique_ctx = ctx;
int ok;
if (!strcmp(s, *unique_ctx->s)) {
entry_color(COLOR_EDIT_ASIS);
return 1;
}
ok = !unique_ctx->validate || unique_ctx->validate(s, unique_ctx->ctx);
entry_color(ok ? COLOR_EDIT_GOOD : COLOR_EDIT_BAD);
return ok;
}
static int unique_activate(GtkWidget *widget, const char *s, void *ctx)
{
const struct edit_unique_ctx *unique_ctx = ctx;
if (strcmp(s, *unique_ctx->s) &&
unique_ctx->validate && !unique_ctx->validate(s, unique_ctx->ctx))
return 0;
*unique_ctx->s = unique(s);
entry_color(COLOR_EDIT_ASIS);
return 1;
}
static struct edit_ops edit_ops_unique = {
.changed = unique_changed,
.activate = unique_activate,
};
void edit_unique(const char **s, int (*validate)(const char *s, void *ctx),
void *ctx)
{
static struct edit_unique_ctx unique_ctx;
unique_ctx.s = s;
unique_ctx.validate = validate;
unique_ctx.ctx = ctx;
edit_ops = &edit_ops_unique;
edit_ctx = &unique_ctx;
gtk_entry_set_text(GTK_ENTRY(status_entry), *s);
entry_color(COLOR_EDIT_ASIS);
gtk_widget_show(status_entry);
}
/* ----- identifier fields with NULL --------------------------------------- */
static int unique_null_changed(GtkWidget *widget, const char *s, void *ctx)
{
const struct edit_unique_ctx *unique_ctx = ctx;
int ok;
if (!strcmp(s, *unique_ctx->s ? *unique_ctx->s : "")) {
entry_color(COLOR_EDIT_ASIS);
return 1;
}
ok = !*s;
if (!ok)
ok = !unique_ctx->validate ||
unique_ctx->validate(s, unique_ctx->ctx);
entry_color(ok ? COLOR_EDIT_GOOD : COLOR_EDIT_BAD);
return ok;
}
static int unique_null_activate(GtkWidget *widget, const char *s, void *ctx)
{
const struct edit_unique_ctx *unique_ctx = ctx;
if (!*s)
*unique_ctx->s = NULL;
else {
if (unique_ctx->validate &&
!unique_ctx->validate(s, unique_ctx->ctx))
return 0;
*unique_ctx->s = unique(s);
}
entry_color(COLOR_EDIT_ASIS);
return 1;
}
static struct edit_ops edit_ops_null_unique = {
.changed = unique_null_changed,
.activate = unique_null_activate,
};
void edit_unique_null(const char **s,
int (*validate)(const char *s, void *ctx), void *ctx)
{
static struct edit_unique_ctx unique_ctx;
unique_ctx.s = s;
unique_ctx.validate = validate;
unique_ctx.ctx = ctx;
edit_ops = &edit_ops_null_unique;
edit_ctx = &unique_ctx;
gtk_entry_set_text(GTK_ENTRY(status_entry), *s ? *s : "");
entry_color(COLOR_EDIT_ASIS);
gtk_widget_show(status_entry);
}
/* ----- string fields ----------------------------------------------------- */
struct edit_name_ctx {
char **s;
int (*validate)(const char *s, void *ctx);
void *ctx;
};
static int name_changed(GtkWidget *widget, const char *s, void *ctx)
{
const struct edit_name_ctx *name_ctx = ctx;
int ok;
if (!strcmp(s, *name_ctx->s)) {
entry_color(COLOR_EDIT_ASIS);
return 1;
}
ok = !name_ctx->validate || name_ctx->validate(s, name_ctx->ctx);
entry_color(ok ? COLOR_EDIT_GOOD : COLOR_EDIT_BAD);
return ok;
}
static int name_activate(GtkWidget *widget, const char *s, void *ctx)
{
const struct edit_name_ctx *name_ctx = ctx;
if (name_ctx->validate && !name_ctx->validate(s, name_ctx->ctx))
return 0;
free(*name_ctx->s);
*name_ctx->s = stralloc(s);
entry_color(COLOR_EDIT_ASIS);
return 1;
}
static struct edit_ops edit_ops_name = {
.changed = name_changed,
.activate = name_activate,
};
void edit_name(char **s, int (*validate)(const char *s, void *ctx), void *ctx)
{
static struct edit_name_ctx name_ctx;
name_ctx.s = s;
name_ctx.validate = validate;
name_ctx.ctx = ctx;
edit_ops = &edit_ops_name;
edit_ctx = &name_ctx;
gtk_entry_set_text(GTK_ENTRY(status_entry), *s);
entry_color(COLOR_EDIT_ASIS);
gtk_widget_show(status_entry);
}
/* ----- expression fields ------------------------------------------------- */
static struct expr *try_parse_expr(const char *s)
{
status_begin_reporting();
return parse_expr(s);
}
static int expr_changed(GtkWidget *widget, const char *s, void *ctx)
{
struct expr *expr;
expr = try_parse_expr(s);
if (!expr) {
entry_color(COLOR_EDIT_BAD);
return 0;
}
entry_color(COLOR_EDIT_GOOD);
free_expr(expr);
return 1;
}
static int expr_activate(GtkWidget *widget, const char *s, void *ctx)
{
struct expr **anchor = ctx;
struct expr *expr;
expr = try_parse_expr(s);
if (!expr)
return 0;
free_expr(*anchor);
*anchor = expr;
entry_color(COLOR_EDIT_ASIS);
return 1;
}
static struct edit_ops edit_ops_expr = {
.changed = expr_changed,
.activate = expr_activate,
};
void edit_expr(struct expr **expr)
{
char *s;
edit_ops = &edit_ops_expr;
edit_ctx = expr;
s = unparse(*expr);
gtk_entry_set_text(GTK_ENTRY(status_entry), s);
free(s);
entry_color(COLOR_EDIT_ASIS);
gtk_widget_show(status_entry);
}
/* ----- text entry -------------------------------------------------------- */
static gboolean changed(GtkWidget *widget, GdkEventMotion *event,
gpointer data)
{
if (edit_ops && edit_ops->changed)
edit_ops->changed(widget,
gtk_entry_get_text(GTK_ENTRY(widget)), edit_ctx);
return TRUE;
}
static gboolean activate(GtkWidget *widget, GdkEventMotion *event,
gpointer data)
{
if (edit_ops && edit_ops->activate)
if (edit_ops->activate(widget,
gtk_entry_get_text(GTK_ENTRY(widget)), edit_ctx)) {
inst_deselect();
change_world();
}
return TRUE;
}
void edit_nothing(void)
{
edit_ops = NULL;
gtk_widget_hide(status_entry);
}
/* ----- status reports ---------------------------------------------------- */
static gint context_id;
static int have_msg = 0;
static void clear_status_msg(void)
{
if (have_msg) {
gtk_statusbar_pop(GTK_STATUSBAR(status_msg), context_id);
have_msg = 0;
}
}
static void report_to_gui(const char *s)
{
if (!have_msg)
gtk_statusbar_push(GTK_STATUSBAR(status_msg), context_id, s);
have_msg = 1;
}
void status_begin_reporting(void)
{
clear_status_msg();
reporter = report_to_gui;
}
/* ----- setup ------------------------------------------------------------- */
static GtkWidget *add_label(GtkWidget *tab, int col, int row)
{
GtkWidget *label;
label = label_in_box_new(NULL);
gtk_table_attach_defaults(GTK_TABLE(tab), box_of_label(label),
col, col+1, row, row+1);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_label_set_selectable(GTK_LABEL(label), TRUE);
return label;
}
void make_status_area(GtkWidget *vbox)
{
GtkWidget *tab, *hbox;
tab = gtk_table_new(5, 2, FALSE);
gtk_box_pack_start(GTK_BOX(vbox), tab, FALSE, TRUE, 0);
/* name and input */
status_name = add_label(tab, 0, 0);
/*
* @@@ this should make the entry consume all available space. alas, it
* doesn't work like that :-(
*/
hbox = gtk_hbox_new(TRUE, 0);
gtk_table_attach_defaults(GTK_TABLE(tab), hbox,
0, 1, 1, 2);
status_entry = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(hbox), status_entry, TRUE, TRUE, 0);
gtk_entry_set_has_frame(GTK_ENTRY(status_entry), FALSE);
g_signal_connect(G_OBJECT(status_entry), "changed",
G_CALLBACK(changed), status_entry);
g_signal_connect(G_OBJECT(status_entry), "activate",
G_CALLBACK(activate), status_entry);
/* x / y */
status_x = add_label(tab, 1, 0);
status_y = add_label(tab, 1, 1);
/* r / angle */
status_r = add_label(tab, 2, 0);
status_angle = add_label(tab, 2, 1);
/* sys / user pos */
status_sys_pos = add_label(tab, 3, 0);
status_user_pos = add_label(tab, 3, 1);
/* zoom / grid */
status_zoom = add_label(tab, 4, 0);
status_grid = add_label(tab, 4, 1);
/* message bar */
status_msg = gtk_statusbar_new();
gtk_box_pack_start(GTK_BOX(vbox), status_msg, FALSE, FALSE, 0);
context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(status_msg),
"messages");
}

47
gui_status.h Normal file
View File

@ -0,0 +1,47 @@
/*
* gui_status.h - GUI, status area
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef GUI_STATUS_H
#define GUI_STATUS_H
#include <gtk/gtk.h>
#include "coord.h"
#include "expr.h"
void edit_unique(const char **s, int (*validate)(const char *s, void *ctx),
void *ctx);
void edit_unique_null(const char **s, int (*validate)(const char *s, void *ctx),
void *ctx);
void edit_name(char **s, int (*validate)(const char *s, void *ctx), void *ctx);
void edit_expr(struct expr **expr);
void edit_nothing(void);
void status_set_name(const char *fmt, ...);
void status_set_x(const char *fmt, ...);
void status_set_y(const char *fmt, ...);
void status_set_r(const char *fmt, ...);
void status_set_angle(const char *fmt, ...);
void status_set_sys_pos(const char *fmt, ...);
void status_set_user_pos(const char *fmt, ...);
void status_set_zoom(const char *fmt, ...);
void status_set_grid(const char *fmt, ...);
void status_set_xy(struct coord coord);
void status_begin_reporting(void);
void make_status_area(GtkWidget *vbox) ;
#endif /* !GUI_STATUS_H */

69
gui_style.c Normal file
View File

@ -0,0 +1,69 @@
/*
* gui_style.c - GUI, style definitions
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <gtk/gtk.h>
#include "inst.h"
#include "gui_util.h"
#include "gui.h"
#include "gui_style.h"
#define INVALID "#00ffff"
GdkGC *gc_bg;
GdkGC *gc_vec[mode_n];
GdkGC *gc_obj[mode_n];
GdkGC *gc_pad[mode_n];
GdkGC *gc_ptext[mode_n];
GdkGC *gc_meas[mode_n];
GdkGC *gc_frame[mode_n];
static GdkGC *gc(const char *spec, int width)
{
GdkGCValues gc_values = {
.background = get_color("black"),
.foreground = get_color(spec),
.line_width = width,
};
return gdk_gc_new_with_values(root->window, &gc_values,
GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_LINE_WIDTH);
}
static void style(GdkGC *gcs[mode_n],
const char *in, const char *in_path, const char *act, const char *act_path,
const char *sel)
{
gcs[mode_inactive] = gc(in, 1);
gcs[mode_inactive_in_path] = gc(in_path, 1);
gcs[mode_active] = gc(act, 1);
gcs[mode_active_in_path] = gc(act_path, 1);
gcs[mode_selected] = gc(sel, 2);
}
void gui_setup_style(GdkDrawable *drawable)
{
gc_bg = gc("#000000", 0);
/* inactive in+path active act+path selected */
style(gc_vec, "#202000", "#404020", "#909040", "#c0c080", "#ffff80");
style(gc_obj, "#006060", INVALID, "#00ffff", INVALID, "#ffff80");
style(gc_pad, "#400000", INVALID, "#ff0000", INVALID, "#ffff80");
style(gc_ptext, "#404040", INVALID, "#ffffff", INVALID, "#ffffff");
style(gc_meas, "#280040", INVALID, "#ff00ff", INVALID, "#ffff80");
style(gc_frame, "#004000", "#205020", "#00ff00", INVALID, INVALID);
}

91
gui_style.h Normal file
View File

@ -0,0 +1,91 @@
/*
* gui_style.h - GUI, style definitions
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef GUI_STYLE_H
#define GUI_STYLE_H
#include <gtk/gtk.h>
#include "inst.h"
/* ----- screen distances, etc. -------------------------------------------- */
#define CANVAS_CLEARANCE 10
#define VEC_ARROW_LEN 10
#define VEC_ARROW_ANGLE 20
#define VEC_EYE_R 5
#define PAD_FONT "Sans Bold 24"
#define PAD_BORDER 2
#define MEAS_FONT "Sans 8"
#define MEAS_BASELINE_OFFSET 0.1
#define MEAS_ARROW_LEN 9
#define MEAS_ARROW_ANGLE 30
#define FRAME_FONT "Sans 8"
#define FRAME_BASELINE_OFFSET 0.1
#define FRAME_SHORT_X 100
#define FRAME_SHORT_Y 20
#define FRAME_CLEARANCE 5
#define FRAME_EYE_R1 3
#define FRAME_EYE_R2 5
#define SELECT_R 6 /* pixels within which we select */
#define MIN_FONT_SCALE 0.20 /* don't scale fonts below this */
/* ----- assorted colors --------------------------------------------------- */
#define COLOR_EDIT_ASIS "#ffffff"
#define COLOR_EDIT_GOOD "#a0ffa0"
#define COLOR_EDIT_BAD "#ffa0a0"
#define COLOR_EDITING "#ff00ff"
#define COLOR_FRAME_UNSELECTED "#c0c0c0"
#define COLOR_FRAME_SELECTED "#fff0a0"
#define COLOR_FRAME_EDITING COLOR_EDITING
#define COLOR_VAR_PASSIVE COLOR_FRAME_UNSELECTED
#define COLOR_VAR_EDITING COLOR_EDITING
#define COLOR_EXPR_PASSIVE "#f0f0ff"
#define COLOR_EXPR_EDITING COLOR_EDITING
#define COLOR_CHOICE_UNSELECTED COLOR_EXPR_PASSIVE
#define COLOR_CHOICE_SELECTED "#9090ff"
#define COLOR_ROW_UNSELECTED COLOR_CHOICE_UNSELECTED
#define COLOR_ROW_SELECTED COLOR_CHOICE_SELECTED
#define COLOR_VAR_TABLE_SEP "black"
/* ----- canvas drawing styles --------------------------------------------- */
extern GdkGC *gc_bg;
extern GdkGC *gc_vec[mode_n];
extern GdkGC *gc_obj[mode_n];
extern GdkGC *gc_pad[mode_n];
extern GdkGC *gc_ptext[mode_n];
extern GdkGC *gc_meas[mode_n];
extern GdkGC *gc_frame[mode_n];
void gui_setup_style(GdkDrawable *drawable);
#endif /* !GUI_STYLE_H */

167
gui_util.c Normal file
View File

@ -0,0 +1,167 @@
/*
* gui_util.c - GUI helper functions
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <stdlib.h>
#include <math.h>
#include <gtk/gtk.h>
#include "gui_style.h"
#include "gui.h"
#include "gui_util.h"
/* ----- look up a color --------------------------------------------------- */
GdkColor get_color(const char *spec)
{
GdkColormap *cmap;
GdkColor color;
cmap = gdk_drawable_get_colormap(root->window);
if (!gdk_color_parse(spec, &color))
abort();
if (!gdk_colormap_alloc_color(cmap, &color, FALSE, TRUE))
abort();
return color;
}
/* ----- lines with a width ------------------------------------------------ */
void set_width(GdkGC *gc, int width)
{
gdk_gc_set_line_attributes(gc, width < 1 ? 1 : width,
GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
}
/* ----- labels in a box --------------------------------------------------- */
GtkWidget *label_in_box_new(const char *s)
{
GtkWidget *evbox, *label;
evbox = gtk_event_box_new();
label = gtk_label_new(s);
gtk_misc_set_padding(GTK_MISC(label), 1, 1);
gtk_container_add(GTK_CONTAINER(evbox), label);
return label;
}
GtkWidget *box_of_label(GtkWidget *label)
{
return gtk_widget_get_ancestor(label, GTK_TYPE_EVENT_BOX);
}
void label_in_box_bg(GtkWidget *label, const char *color)
{
GtkWidget *box;
GdkColor col = get_color(color);
box = box_of_label(label);
gtk_widget_modify_bg(box, GTK_STATE_NORMAL, &col);
}
/* ----- render a text string ---------------------------------------------- */
void render_text(GdkDrawable *da, GdkGC *gc, int x, int y, double angle,
const char *s, const char *font, double xalign, double yalign,
int xmax, int ymax)
{
GdkScreen *screen;
PangoRenderer *renderer;
PangoContext *context;
PangoLayout *layout;
PangoFontDescription *desc;
int width, height;
PangoMatrix m = PANGO_MATRIX_INIT;
double f_min, f;
/* set up the renderer */
screen = gdk_drawable_get_screen(da);
renderer = gdk_pango_renderer_get_default(screen);
gdk_pango_renderer_set_drawable(GDK_PANGO_RENDERER(renderer), da);
gdk_pango_renderer_set_gc(GDK_PANGO_RENDERER(renderer), gc);
/* start preparing the layout */
context = gdk_pango_context_get_for_screen(screen);
layout = pango_layout_new(context);
pango_layout_set_text(layout, s, -1);
/* apply the font */
desc = pango_font_description_from_string(font);
pango_layout_set_font_description(layout, desc);
pango_font_description_free(desc);
/* align and position the text */
pango_layout_get_size(layout, &width, &height);
f_min = 1.0;
if (xmax) {
f = xmax/((double) width/PANGO_SCALE);
if (f < f_min)
f_min = f;
}
if (ymax) {
f = ymax/((double) height/PANGO_SCALE);
if (f < f_min)
f_min = f;
}
if (f_min < MIN_FONT_SCALE)
f_min = MIN_FONT_SCALE;
pango_matrix_translate(&m, x, y);
pango_matrix_rotate(&m, angle);
pango_matrix_translate(&m,
-xalign*f_min*width/PANGO_SCALE,
(yalign-1)*f_min*height/PANGO_SCALE);
pango_matrix_scale(&m, f_min, f_min);
pango_context_set_matrix(context, &m);
pango_layout_context_changed(layout);
pango_renderer_draw_layout(renderer, layout, 0, 0);
/* clean up renderer */
gdk_pango_renderer_set_drawable(GDK_PANGO_RENDERER(renderer), NULL);
gdk_pango_renderer_set_gc(GDK_PANGO_RENDERER(renderer), NULL);
/* free objects */
g_object_unref(layout);
g_object_unref(context);
}
/* ----- kill the content of a container ----------------------------------- */
static void destroy_callback(GtkWidget *widget, gpointer data)
{
gtk_widget_destroy(widget);
}
void destroy_all_children(GtkContainer *container)
{
gtk_container_foreach(container, destroy_callback, NULL);
}

34
gui_util.h Normal file
View File

@ -0,0 +1,34 @@
/*
* gui_util.h - GUI helper functions
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef GUI_UTIL_H
#define GUI_UTIL_H
#include <gtk/gtk.h>
GdkColor get_color(const char *spec);
void set_width(GdkGC *gc, int width);
GtkWidget *label_in_box_new(const char *s);
GtkWidget *box_of_label(GtkWidget *label);
void label_in_box_bg(GtkWidget *box, const char *color);
void render_text(GdkDrawable *da, GdkGC *gc, int x, int y, double angle,
const char *s, const char *font, double xalign, double yalign,
int xmax, int ymax);
void destroy_all_children(GtkContainer *container);
#endif /* !GUI_UTIL_H */

13
icons/arc.fig Normal file
View File

@ -0,0 +1,13 @@
#FIG 3.2 Produced by xfig version 3.2.5a
Landscape
Center
Inches
A4
100.00
Single
-2
1200 2
5 1 0 10 3 7 50 -1 -1 0.000 0 1 1 0 4005.000 4395.000 5550 4500 5100 3300 3900 2850
0 0 10.00 450.00 600.00
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400

12
icons/circ.fig Normal file
View File

@ -0,0 +1,12 @@
#FIG 3.2 Produced by xfig version 3.2.5a
Landscape
Center
Inches
A4
100.00
Single
-2
1200 2
1 3 0 10 3 7 50 -1 -1 0.000 1 0.0000 4800 3600 900 900 4800 3600 5700 3600
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400

14
icons/frame.fig Normal file
View File

@ -0,0 +1,14 @@
#FIG 3.2 Produced by xfig version 3.2.5a
Landscape
Center
Inches
A4
100.00
Single
-2
1200 2
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
2 1 0 10 12 7 50 -1 -1 0.000 0 0 -1 0 0 3
3900 4275 3900 3675 5700 3675
4 0 12 50 -1 22 42 0.0000 4 135 450 3750 3525 FRAME\001

13
icons/line.fig Normal file
View File

@ -0,0 +1,13 @@
#FIG 3.2 Produced by xfig version 3.2.5a
Landscape
Center
Inches
A4
100.00
Single
-2
1200 2
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
2 1 0 10 3 7 50 -1 -1 0.000 0 0 -1 0 0 2
3900 4200 5700 3000

19
icons/meas.fig Normal file
View File

@ -0,0 +1,19 @@
#FIG 3.2 Produced by xfig version 3.2.5a
Landscape
Center
Inches
A4
100.00
Single
-2
1200 2
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
2 1 0 10 21 7 50 -1 -1 0.000 0 0 -1 0 0 2
3900 3300 3900 3900
2 1 0 10 21 7 50 -1 -1 0.000 0 0 -1 0 0 2
5700 3300 5700 3900
2 1 0 10 21 7 50 -1 -1 0.000 0 0 -1 1 1 2
0 0 10.00 450.00 450.00
0 0 10.00 450.00 450.00
3900 3600 5700 3600

15
icons/pad.fig Normal file
View File

@ -0,0 +1,15 @@
#FIG 3.2 Produced by xfig version 3.2.5a
Landscape
Center
Inches
A4
100.00
Single
-2
1200 2
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
2 2 0 0 0 4 50 -1 20 0.000 0 0 -1 0 0 5
4200 2700 5400 2700 5400 4500 4200 4500 4200 2700
2 1 0 15 7 7 45 -1 -1 0.000 1 1 -1 0 0 3
4875 4050 4875 3150 4650 3375

14
icons/point.fig Normal file
View File

@ -0,0 +1,14 @@
#FIG 3.2 Produced by xfig version 3.2.5a
Landscape
Center
Inches
A4
100.00
Single
-2
1200 2
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
2 3 0 0 0 7 50 -1 10 0.000 0 0 -1 0 0 8
3900 4350 4050 4500 5250 3300 5325 3675 5700 2700 4725 3075
5100 3150 3900 4350

13
icons/rect.fig Normal file
View File

@ -0,0 +1,13 @@
#FIG 3.2 Produced by xfig version 3.2.5a
Landscape
Center
Inches
A4
100.00
Single
-2
1200 2
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
2 2 0 10 3 7 50 -1 -1 0.000 0 0 -1 0 0 5
3900 3000 5700 3000 5700 4200 3900 4200 3900 3000

11
icons/template.fig Normal file
View File

@ -0,0 +1,11 @@
#FIG 3.2 Produced by xfig version 3.2.5a
Landscape
Center
Inches
A4
100.00
Single
-2
1200 2
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400

17
icons/vec.fig Normal file
View File

@ -0,0 +1,17 @@
#FIG 3.2 Produced by xfig version 3.2.5a
Landscape
Center
Inches
A4
100.00
Single
-2
1200 2
0 32 #c0c000
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
3600 2400 6000 2400 6000 4800 3600 4800 3600 2400
2 1 0 10 32 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 10.00 300.00 300.00
3900 4275 5700 3075
2 1 0 10 32 7 50 -1 -1 0.000 1 1 -1 0 0 3
4050 3150 4575 3525 4425 2925

665
inst.c Normal file
View File

@ -0,0 +1,665 @@
/*
* inst.c - Instance structures
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "util.h"
#include "coord.h"
#include "expr.h"
#include "obj.h"
#include "gui_status.h"
#include "gui_inst.h"
#include "inst.h"
struct inst_ops {
void (*debug)(struct inst *self);
void (*save)(FILE *file, struct inst *self);
void (*draw)(struct inst *self, struct draw_ctx *ctx);
unit_type (*distance)(struct inst *self, struct coord pos,
unit_type scale);
void (*select)(struct inst *self);
};
enum inst_prio {
ip_frame, /* frames have their own selection */
ip_pad, /* pads also accept clicks inside */
ip_circ, /* circles don't overlap easily */
ip_arc, /* arc are like circles, just shorter */
ip_rect, /* rectangles have plenty of sides */
ip_meas, /* mesurements are like lines but set a bit apart */
ip_line, /* lines are easly overlapped by other things */
ip_vec, /* vectors only have the end point */
ip_n, /* number of priorities */
};
#define FOR_INST_PRIOS_UP(prio) \
for (prio = 0; prio != ip_n; prio++)
#define FOR_INST_PRIOS_DOWN(prio) \
for (prio = ip_n-1; prio != (enum inst_prio) -1; prio--)
#define FOR_INSTS_UP(prio, inst) \
FOR_INST_PRIOS_UP(prio) \
for (inst = insts[prio]; inst; inst = inst->next)
#define FOR_INSTS_DOWN(prio, inst) \
FOR_INST_PRIOS_DOWN(prio) \
for (inst = insts[prio]; inst; inst = inst->next)
struct inst *curr_frame = NULL;
struct inst *selected_inst = NULL;
static struct inst *insts[ip_n], **next_inst[ip_n];
static struct inst *prev_insts[ip_n];
static unsigned long active_set = 0;
#define IS_ACTIVE ((active_set & 1))
/* ----- selection of items not on the canvas ------------------------------ */
static void *selected_outside = NULL;
static void (*outside_deselect)(void *item);
static void deselect_outside(void)
{
if (selected_outside && outside_deselect)
outside_deselect(selected_outside);
selected_outside = NULL;
}
void inst_select_outside(void *item, void (*deselect)(void *item))
{
if (item == selected_outside)
return;
deselect_outside();
inst_deselect();
selected_outside = item;
outside_deselect = deselect;
}
/* ----- selection --------------------------------------------------------- */
static struct inst_ops vec_ops;
static struct inst_ops frame_ops;
static void set_path(int on)
{
struct inst *inst;
return;
if (inst->ops != &vec_ops && inst->ops != &frame_ops)
return;
/* @@@ wrong */
for (inst = selected_inst; inst; inst = inst->outer) {
if (inst->ops != &vec_ops && inst->ops != &frame_ops)
break;
inst->in_path = on;
}
}
int inst_select(const struct draw_ctx *ctx, struct coord pos)
{
enum inst_prio prio;
struct inst *inst;
int best_dist, dist;
deselect_outside();
edit_nothing();
selected_inst = NULL;
FOR_INST_PRIOS_DOWN(prio) {
for (inst = insts[prio]; inst; inst = inst->next) {
if (!inst->active || !inst->ops->distance)
continue;
dist = inst->ops->distance(inst, pos, ctx->scale);
if (dist >= 0 && (!selected_inst || best_dist > dist)) {
selected_inst = inst;
best_dist = dist;
}
}
if (selected_inst)
goto selected;
}
/* give vectors a second chance */
for (inst = insts[ip_vec]; inst; inst = inst->next) {
if (!inst->active)
continue;
dist = gui_dist_vec_fallback(inst, pos, ctx->scale);
if (dist >= 0 && (!selected_inst || best_dist > dist)) {
selected_inst = inst;
best_dist = dist;
}
}
if (!selected_inst)
return 0;
selected:
set_path(1);
if (selected_inst->ops->select)
selected_inst->ops->select(selected_inst);
return 1;
}
void inst_deselect(void)
{
if (selected_inst)
set_path(0);
deselect_outside();
status_set_name("");
status_set_x("");
status_set_y("");
status_set_r("");
status_set_angle("");
selected_inst = NULL;
edit_nothing();
}
static void rect_status(struct coord a, struct coord b, unit_type width)
{
struct coord d = sub_vec(b, a);
double angle;
status_set_xy(d);
if (!d.x && !d.y)
status_set_angle("a = 0 deg");
else {
angle = atan2(d.y, d.x)/M_PI*180.0;
if (angle < 0)
angle += 360;
status_set_angle("a = %3.1f deg", angle);
}
status_set_r("r = %5.2f mm", hypot(units_to_mm(d.x), units_to_mm(d.y)));
if (width != -1)
status_set_name("width = %5.2f mm", units_to_mm(width));
}
/* ----- helper functions for instance creation ---------------------------- */
static void update_bbox(struct bbox *bbox, struct coord coord)
{
if (bbox->min.x > coord.x)
bbox->min.x = coord.x;
if (bbox->max.x < coord.x)
bbox->max.x = coord.x;
if (bbox->min.y > coord.y)
bbox->min.y = coord.y;
if (bbox->max.y < coord.y)
bbox->max.y = coord.y;
}
static void propagate_bbox(const struct inst *inst)
{
update_bbox(&curr_frame->bbox, inst->bbox.min);
update_bbox(&curr_frame->bbox, inst->bbox.max);
}
static struct inst *add_inst(const struct inst_ops *ops, enum inst_prio prio,
struct coord base)
{
struct inst *inst;
inst = alloc_type(struct inst);
inst->ops = ops;
inst->base = inst->bbox.min = inst->bbox.max = base;
inst->outer = curr_frame;
inst->active = IS_ACTIVE;
inst->in_path = 0;
inst->next = NULL;
*next_inst[prio] = inst;
next_inst[prio] = &inst->next;
return inst;
}
/* ----- vec --------------------------------------------------------------- */
static void vec_op_debug(struct inst *self)
{
printf("vec %lg, %lg -> %lg, %lg\n",
units_to_mm(self->base.x), units_to_mm(self->base.y),
units_to_mm(self->u.rect.end.x), units_to_mm(self->u.rect.end.y));
}
static int validate_vec_name(const char *s, void *ctx)
{
struct vec *vec = ctx;
const struct vec *walk;
if (!is_id(s))
return 0;
for (walk = vec->frame->vecs; walk; walk = walk->next)
if (walk->name && !strcmp(walk->name, s))
return 0;
return 1;
}
static void vec_op_select(struct inst *self)
{
status_set_name(self->vec->name ? self->vec->name : "");
rect_status(self->base, self->u.rect.end, -1);
edit_unique_null(&self->vec->name, validate_vec_name, self->vec);
}
static struct inst_ops vec_ops = {
.debug = vec_op_debug,
.draw = gui_draw_vec,
.distance = gui_dist_vec,
.select = vec_op_select,
};
int inst_vec(struct vec *vec, struct coord base)
{
struct inst *inst;
inst = add_inst(&vec_ops, ip_vec, base);
inst->vec = vec;
inst->u.rect.end = vec->pos;
update_bbox(&inst->bbox, vec->pos);
propagate_bbox(inst);
return 1;
}
/* ----- line -------------------------------------------------------------- */
static void line_op_debug(struct inst *self)
{
printf("line %lg, %lg / %lg, %lg\n",
units_to_mm(self->base.x), units_to_mm(self->base.y),
units_to_mm(self->u.rect.end.x), units_to_mm(self->u.rect.end.y));
}
static void line_op_select(struct inst *self)
{
rect_status(self->bbox.min, self->bbox.max, self->u.rect.width);
edit_expr(&self->obj->u.line.width);
}
static struct inst_ops line_ops = {
.debug = line_op_debug,
.draw = gui_draw_line,
.distance = gui_dist_line,
.select = line_op_select,
};
int inst_line(struct obj *obj, struct coord a, struct coord b, unit_type width)
{
struct inst *inst;
inst = add_inst(&line_ops, ip_line, a);
inst->obj = obj;
inst->u.rect.end = b;
inst->u.rect.width = width;
update_bbox(&inst->bbox, b);
propagate_bbox(inst);
return 1;
}
/* ----- rect -------------------------------------------------------------- */
static void rect_op_debug(struct inst *self)
{
printf("rect %lg, %lg / %lg, %lg\n",
units_to_mm(self->base.x), units_to_mm(self->base.y),
units_to_mm(self->u.rect.end.x), units_to_mm(self->u.rect.end.y));
}
static void rect_op_select(struct inst *self)
{
rect_status(self->bbox.min, self->bbox.max, self->u.rect.width);
edit_expr(&self->obj->u.rect.width);
}
static struct inst_ops rect_ops = {
.debug = rect_op_debug,
.draw = gui_draw_rect,
.distance = gui_dist_rect,
.select = rect_op_select,
};
int inst_rect(struct obj *obj, struct coord a, struct coord b, unit_type width)
{
struct inst *inst;
inst = add_inst(&rect_ops, ip_rect, a);
inst->obj = obj;
inst->u.rect.end = b;
inst->u.rect.width = width;
update_bbox(&inst->bbox, b);
propagate_bbox(inst);
return 1;
}
/* ----- pad --------------------------------------------------------------- */
static void pad_op_debug(struct inst *self)
{
printf("pad \"%s\" %lg, %lg / %lg, %lg\n", self->u.name,
units_to_mm(self->bbox.min.x), units_to_mm(self->bbox.min.y),
units_to_mm(self->bbox.max.x), units_to_mm(self->bbox.max.y));
}
static int validate_pad_name(const char *s, void *ctx)
{
char *tmp;
tmp = expand(s, NULL);
if (!tmp)
return 0;
free(tmp);
return 1;
}
static void pad_op_select(struct inst *self)
{
status_set_name(self->u.name);
rect_status(self->bbox.min, self->bbox.max, -1);
edit_name(&self->obj->u.pad.name, validate_pad_name, NULL);
}
static struct inst_ops pad_ops = {
.debug = pad_op_debug,
.draw = gui_draw_pad,
.distance = gui_dist_pad,
.select = pad_op_select,
};
int inst_pad(struct obj *obj, const char *name, struct coord a, struct coord b)
{
struct inst *inst;
inst = add_inst(&pad_ops, ip_pad, a);
inst->obj = obj;
inst->u.name = stralloc(name);
update_bbox(&inst->bbox, b);
propagate_bbox(inst);
return 1;
}
/* ----- arc --------------------------------------------------------------- */
static void arc_op_debug(struct inst *self)
{
printf("arc %lg, %lg radius %lg %lg ... %lg\n",
units_to_mm(self->base.x), units_to_mm(self->base.y),
units_to_mm(self->u.arc.r), self->u.arc.a1, self->u.arc.a2);
}
static void arc_op_select(struct inst *self)
{
status_set_xy(self->base);
status_set_angle("a = %3.1f deg",
self->u.arc.a1 == self->u.arc.a2 ? 360 :
self->u.arc.a2-self->u.arc.a1);
status_set_r("r = %5.2f mm", units_to_mm(self->u.arc.r));
status_set_name("width = %5.2f mm", units_to_mm(self->u.arc.width));
edit_expr(&self->obj->u.arc.width);
}
static struct inst_ops arc_ops = {
.debug = arc_op_debug,
.draw = gui_draw_arc,
.distance = gui_dist_arc,
.select = arc_op_select,
};
int inst_arc(struct obj *obj, struct coord center, struct coord start,
struct coord end, unit_type width)
{
struct inst *inst;
double r, a1, a2;
inst = add_inst(&arc_ops, ip_arc, center);
inst->obj = obj;
r = hypot(start.x-center.x, start.y-center.y);
a1 = atan2(start.y-center.y, start.x-center.x)/M_PI*180.0;
a2 = atan2(end.y-center.y, end.x-center.x)/M_PI*180.0;
if (a1 < 0)
a1 += 360.0;
if (a2 < 0)
a2 += 360.0;
inst->u.arc.r = r;
inst->u.arc.a1 = a1;
inst->u.arc.a2 = a2;
inst->u.arc.width = width;
inst->bbox.min.x = center.x-r;
inst->bbox.max.x = center.x+r;
inst->bbox.min.y = center.x-r;
inst->bbox.max.y = center.x+r;
propagate_bbox(inst);
return 1;
}
/* ----- measurement ------------------------------------------------------- */
static void meas_op_debug(struct inst *self)
{
printf("meas %lg, %lg / %lg, %lg offset %lg\n",
units_to_mm(self->base.x), units_to_mm(self->base.y),
units_to_mm(self->u.meas.end.x), units_to_mm(self->u.meas.end.y),
units_to_mm(self->u.meas.offset));
}
static void meas_op_select(struct inst *self)
{
rect_status(self->bbox.min, self->bbox.max, -1);
status_set_name("offset = %5.2f mm", units_to_mm(self->u.meas.offset));
edit_expr(&self->obj->u.meas.offset);
}
static struct inst_ops meas_ops = {
.debug = meas_op_debug,
.draw = gui_draw_meas,
.distance = gui_dist_meas,
.select = meas_op_select,
};
int inst_meas(struct obj *obj, struct coord from, struct coord to,
unit_type offset)
{
struct inst *inst;
inst = add_inst(&meas_ops, ip_meas, from);
inst->obj = obj;
inst->u.meas.end = to;
inst->u.meas.offset = offset;
/* @@@ our bbox is actually a bit more complex than this */
update_bbox(&inst->bbox, to);
propagate_bbox(inst);
return 1;
}
/* ----- active instance --------------------------------------------------- */
void inst_begin_active(int active)
{
active_set = (active_set << 1) | active;
}
void inst_end_active(void)
{
active_set >>= 1;
}
/* ----- frame ------------------------------------------------------------- */
static void frame_op_debug(struct inst *self)
{
printf("frame %s @ %lg, %lg\n",
self->u.frame.ref->name ? self->u.frame.ref->name : "(root)",
units_to_mm(self->base.x), units_to_mm(self->base.y));
}
static struct inst_ops frame_ops = {
.debug = frame_op_debug,
.draw = gui_draw_frame,
};
void inst_begin_frame(const struct frame *frame, struct coord base, int active)
{
struct inst *inst;
inst = add_inst(&frame_ops, ip_frame, base);
inst->u.frame.ref = frame;
inst->active = active;
curr_frame = inst;
}
void inst_end_frame(const struct frame *frame)
{
struct inst *inst = curr_frame;
curr_frame = curr_frame->outer;
if (curr_frame)
propagate_bbox(inst);
}
/* ----- misc. ------------------------------------------------------------- */
struct bbox inst_get_bbox(void)
{
return insts[ip_frame]->bbox;
}
static void inst_free(struct inst *list[ip_n])
{
enum inst_prio prio;
struct inst *next;
FOR_INST_PRIOS_UP(prio)
while (list[prio]) {
next = list[prio]->next;
free(list[prio]);
list[prio] = next;
}
}
void inst_start(void)
{
enum inst_prio prio;
FOR_INST_PRIOS_UP(prio) {
prev_insts[prio] = insts[prio];
insts[prio] = NULL;
next_inst[prio] = &insts[prio];
}
}
void inst_commit(void)
{
inst_free(prev_insts);
}
void inst_revert(void)
{
enum inst_prio prio;
inst_free(insts);
FOR_INST_PRIOS_UP(prio)
insts[prio] = prev_insts[prio];
}
void inst_draw(struct draw_ctx *ctx)
{
enum inst_prio prio;
struct inst *inst;
FOR_INSTS_UP(prio, inst)
if (!inst->active && inst->ops->draw)
inst->ops->draw(inst, ctx);
FOR_INSTS_UP(prio, inst)
if (prio != ip_frame && inst->active &&
inst != selected_inst && inst->ops->draw)
inst->ops->draw(inst, ctx);
for (inst = insts[ip_frame]; inst; inst = inst->next)
if (inst->active && inst != selected_inst && inst->ops->draw)
inst->ops->draw(inst, ctx);
if (selected_inst && selected_inst->ops->draw)
selected_inst->ops->draw(selected_inst, ctx);
}
void inst_debug(void)
{
enum inst_prio prio;
struct inst *inst;
FOR_INSTS_UP(prio, inst)
inst->ops->debug(inst);
}

104
inst.h Normal file
View File

@ -0,0 +1,104 @@
/*
* inst.h - Instance structures
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef INST_H
#define INST_H
#include <stdio.h>
#include "coord.h"
#include "obj.h"
enum mode {
mode_inactive, /* on inactive frame */
mode_inactive_in_path, /* inactive but is in path to selected */
mode_active, /* on active frame */
mode_active_in_path, /* active and is in path to selected */
mode_selected, /* item is selected */
mode_n /* number of modes */
};
struct bbox {
struct coord min;
struct coord max;
};
struct inst_ops;
struct draw_ctx;
struct inst {
const struct inst_ops *ops;
struct coord base;
// struct inst *base_inst; /* frame or vector leading to this item */
struct bbox bbox;
struct vec *vec; /* undefined if not vector */
struct obj *obj; /* undefined if not object */
struct inst *outer; /* frame containing this item */
int active;
int in_path;
union {
struct {
const struct frame *ref;
} frame;
const char *name;
struct {
struct coord end;
unit_type width;
} rect;
struct {
unit_type r;
double a1, a2;
unit_type width;
} arc;
struct {
struct coord end;
double offset;
} meas;
} u;
struct inst *next;
};
extern struct inst *selected_inst;
void inst_select_outside(void *item, void (*deselect)(void *item));
int inst_select(const struct draw_ctx *ctx, struct coord pos);
void inst_deselect(void);
int inst_vec(struct vec *vec, struct coord base);
int inst_line(struct obj *obj, struct coord a, struct coord b, unit_type width);
int inst_rect(struct obj *obj, struct coord a, struct coord b, unit_type width);
int inst_pad(struct obj *obj, const char *name, struct coord a, struct coord b);
int inst_arc(struct obj *obj, struct coord center, struct coord start,
struct coord stop, unit_type width);
int inst_meas(struct obj *obj, struct coord from, struct coord to,
unit_type offset);
void inst_begin_active(int active);
void inst_end_active(void);
void inst_begin_frame(const struct frame *frame, struct coord base, int active);
void inst_end_frame(const struct frame *frame);
struct bbox inst_get_bbox(void);
void inst_start(void);
void inst_commit(void);
void inst_revert(void);
void inst_draw(struct draw_ctx *ctx);
void inst_debug(void);
#endif /* !INST_H */

270
obj.c Normal file
View File

@ -0,0 +1,270 @@
/*
* obj.c - Object definition model
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "error.h"
#include "expr.h"
#include "inst.h"
#include "obj.h"
#define DEFAULT_SILK_WIDTH make_mil(15) /* @@@ */
#define MAX_ITERATIONS 1000 /* abort "loop"s at this limit */
struct frame *frames = NULL;
struct frame *root_frame = NULL;
struct frame *active_frame = NULL;
static int generate_frame(struct frame *frame, struct coord base,
const struct frame *parent, int active);
static struct num eval_unit(const struct expr *expr, const struct frame *frame)
{
struct num d;
d = eval_num(expr, frame);
if (!is_undef(d) && to_unit(&d))
return d;
fail_expr(expr);
return undef;
}
static struct num eval_unit_default(const struct expr *expr,
const struct frame *frame, struct num def)
{
if (expr)
return eval_unit(expr, frame);
to_unit(&def);
return def;
}
static int generate_vecs(struct frame *frame, struct coord base)
{
struct coord vec_base;
struct vec *vec;
struct num x, y;
for (vec = frame->vecs; vec; vec = vec->next) {
x = eval_unit(vec->x, frame);
if (is_undef(x))
return 0;
y = eval_unit(vec->y, frame);
if (is_undef(y))
return 0;
vec_base = vec->base ? vec->base->pos : base;
vec->pos = vec_base;
vec->pos.x += x.n;
vec->pos.y += y.n;
if (!inst_vec(vec, vec_base))
return 0;
}
return 1;
}
static int generate_objs(struct frame *frame, struct coord base, int active)
{
struct obj *obj;
char *name;
int ok;
struct num width, offset;
for (obj = frame->objs; obj; obj = obj->next)
switch (obj->type) {
case ot_frame:
if (!generate_frame(obj->u.frame,
obj->base ? obj->base->pos : base, frame, active))
return 0;
break;
case ot_line:
width = eval_unit_default(obj->u.line.width, frame,
DEFAULT_SILK_WIDTH);
if (is_undef(width))
return 0;
if (!inst_line(obj, obj->base ? obj->base->pos : base,
obj->u.line.other ? obj->u.line.other->pos : base,
width.n))
return 0;
break;
case ot_rect:
width = eval_unit_default(obj->u.rect.width, frame,
DEFAULT_SILK_WIDTH);
if (is_undef(width))
return 0;
if (!inst_rect(obj, obj->base ? obj->base->pos : base,
obj->u.rect.other ? obj->u.rect.other->pos : base,
width.n))
return 0;
break;
case ot_pad:
name = expand(obj->u.pad.name, frame);
if (!name)
return 0;
ok = inst_pad(obj, name,
obj->base ? obj->base->pos : base,
obj->u.pad.other ? obj->u.pad.other->pos : base);
free(name);
if (!ok)
return 0;
break;
case ot_arc:
width = eval_unit_default(obj->u.arc.width, frame,
DEFAULT_SILK_WIDTH);
if (is_undef(width))
return 0;
if (!inst_arc(obj, obj->base ? obj->base->pos : base,
obj->u.arc.start ? obj->u.arc.start->pos : base,
obj->u.arc.end ? obj->u.arc.end->pos : base,
width.n))
return 0;
break;
case ot_meas:
offset = eval_unit(obj->u.meas.offset, frame);
if (is_undef(offset))
return 0;
if (!inst_meas(obj, obj->base ? obj->base->pos : base,
obj->u.meas.other ? obj->u.meas.other->pos : base,
offset.n))
return 0;
break;
default:
abort();
}
return 1;
}
static int generate_items(struct frame *frame, struct coord base, int active)
{
int ok;
inst_begin_active(active && frame == active_frame);
ok = generate_vecs(frame, base) && generate_objs(frame, base, active);
inst_end_active();
return ok;
}
static int run_loops(struct frame *frame, struct loop *loop,
struct coord base, int active)
{
struct num from, to;
int n;
if (!loop)
return generate_items(frame, base, active);
from = eval_num(loop->from.expr, frame);
if (is_undef(from)) {
fail_expr(loop->from.expr);
return 0;
}
if (!is_dimensionless(from)) {
fail("incompatible type for start value");
fail_expr(loop->from.expr);
return 0;
}
to = eval_num(loop->to.expr, frame);
if (is_undef(to)) {
fail_expr(loop->to.expr);
return 0;
}
if (!is_dimensionless(to)) {
fail("incompatible type for end value");
fail_expr(loop->to.expr);
return 0;
}
assert(!loop->initialized);
loop->curr_value = from.n;
loop->initialized = 1;
n = 0;
for (; loop->curr_value <= to.n; loop->curr_value += 1) {
if (n >= MAX_ITERATIONS) {
fail("%s: too many iterations (%d)", loop->var.name,
MAX_ITERATIONS);
goto fail;
}
if (!run_loops(frame, loop->next, base,
active && loop->active == n))
goto fail;
n++;
}
loop->initialized = 0;
return 1;
fail:
loop->initialized = 0;
return 0;
}
static int iterate_tables(struct frame *frame, struct table *table,
struct coord base, int active)
{
int n;
if (!table)
return run_loops(frame, frame->loops, base, active);
n = 0;
for (table->curr_row = table->rows; table->curr_row;
table->curr_row = table->curr_row->next) {
if (!iterate_tables(frame, table->next, base,
active && table->active == n))
return 0;
n++;
}
return 1;
}
static int generate_frame(struct frame *frame, struct coord base,
const struct frame *parent, int active)
{
int ok;
/*
* We ensure during construction that frames can never recurse.
*/
inst_begin_frame(frame, base, active && frame == active_frame);
frame->curr_parent = parent;
ok = iterate_tables(frame, frame->tables, base, active);
inst_end_frame(frame);
return ok;
}
int instantiate(void)
{
struct coord zero = { 0, 0 };
int ok;
inst_start();
ok = generate_frame(root_frame, zero, NULL, 1);
if (ok)
inst_commit();
else
inst_revert();
return ok;
}

167
obj.h Normal file
View File

@ -0,0 +1,167 @@
/*
* obj.h - Object definition model
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef OBJ_H
#define OBJ_H
#include <gtk/gtk.h>
#include "expr.h"
#include "coord.h"
struct var {
const char *name;
struct var *next;
/* back reference */
struct frame *frame;
/* for the GUI */
GtkWidget *widget;
/* for evaluation */
int visited;
};
struct value {
struct expr *expr;
struct value *next;
/* back reference */
struct row *row;
/* for the GUI */
GtkWidget *widget;
};
struct row {
struct value *values;
struct row *next;
/* back reference */
struct table *table;
};
struct table {
struct var *vars;
struct row *rows;
struct table *next;
/* used during generation and when editing */
struct row *curr_row;
/* GUI use */
int active; /* n-th row is active, 0 based */
};
struct loop {
struct var var;
struct value from;
struct value to;
struct loop *next;
/* used during generation */
double curr_value;
/* GUI use */
int active; /* n-th iteration is active, 0 based */
/* for evaluation */
int initialized;
};
struct vec {
const char *name; /* NULL if anonymous */
struct expr *x;
struct expr *y;
struct vec *base; /* NULL if frame */
int n_refs;
struct vec *next;
/* used during generation */
struct coord pos;
/* used when editing */
struct frame *frame;
};
struct frame {
const char *name; /* NULL if top-level */
struct table *tables;
struct loop *loops;
struct vec *vecs;
struct obj *objs;
struct frame *next;
struct frame *prev; /* for the list of frames in the GUI */
/* used during generation */
const struct frame *curr_parent;
/* for the GUI */
GtkWidget *label;
};
enum obj_type {
ot_frame,
ot_rect,
ot_pad,
ot_line,
ot_arc,
ot_meas,
};
struct rect {
struct vec *other; /* NULL if frame origin */
struct expr *width;
};
struct pad {
char *name;
struct vec *other; /* NULL if frame origin */
};
struct arc {
struct vec *start; /* NULL if frame origin */
struct vec *end; /* NULL if this is a circle */
struct expr *width;
};
struct meas {
struct vec *other; /* NULL if frame origin */
struct expr *offset;
};
struct obj {
enum obj_type type;
union {
struct frame *frame;
struct rect rect;
struct rect line;
struct pad pad;
struct arc arc;
struct meas meas;
} u;
struct vec *base;
struct obj *next;
};
extern struct frame *frames;
extern struct frame *root_frame;
extern struct frame *active_frame;
int instantiate(void);
#endif /* !OBJ_H */

70
qfn.fpd Normal file
View File

@ -0,0 +1,70 @@
/*
* Example of a QFN package (and general construction site to experiment with
* fped features during development)
*
* Everything you see here is likely to change sooner or later.
*
* http://www.nxp.com/acrobat/packages/footprint/SOT616-1_fp_reflow.pdf
*/
frame pad_up {
c: vec @(-D/2, 0mm)
vec .(0mm, C)
meas c . 0.2mm
vec c(D, C)
set pad = n+1
pad "$pad" c .
}
frame pads {
loop n = 0, N/4-1
vec @(P*(n-(N/4-1)/2), -Ay/2)
frame pad_up .
}
set N = 24
/*
* Note that this table is not a great example because it contains lots of
* fields we don't really need for iterations. But it's useful for driving
* the GUI to extremes.
*/
table
{ P, Ax, Ay, Bx, By, C, D, SLx, SLy, SPx_tot, SPy_tot, SPx, SPy, Gx, Gy, Hx, Hy }
{ 0.5mm, 5mm, 5mm, 3.2mm, 3.2mm, 0.9mm, 0.24mm, 2.1mm, 2.1mm, 1.2mm,
1.2mm, 0.45mm, 0.45mm, 4.3mm, 4.3mm, 5.25mm, 5.25mm }
h_x0y0: vec @(-Hx/2, -Hy/2)
h_x1y1: vec .(Hx, Hy)
rect h_x0y0 h_x1y1 8mil
/*
* we can't draw the package outline on the silk screen for it would print
* over the pads.
*/
#if 0
g_x0y0: vec @(-Gx/2, -Gy/2)
g_x1y1: vec .(Gx, Gy)
#endif
frame pads @
// ARC, just for testing
c: vec @(-1mm, 1mm)
r: vec c(0mm, 0.5mm)
e: vec c(-0.5mm, 0mm)
arc c r e 2mil
r2: vec c(0mm, 0.8mm)
circ c r2 5mil
/*
x1 = 1+2*3
x2 = (1+2)*3
x3 = 1-(2+3)
*/

111
unparse.c Normal file
View File

@ -0,0 +1,111 @@
/*
* unparse.c - Dump an expression tree into a string
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
/*
* This is crazily inefficient but who cares :-)
*/
#include <stdlib.h>
#include <stdio.h>
#include "util.h"
#include "expr.h"
#include "unparse.h"
enum prec {
prec_add,
prec_mult,
prec_unary,
prec_primary,
};
static int precedence(op_type op)
{
if (op == op_add || op == op_sub)
return prec_add;
if (op == op_mult || op == op_div)
return prec_mult;
if (op == op_minus)
return prec_unary;
if (op == op_num || op == op_var)
return prec_primary;
abort();
}
static char *merge3(char *a, const char *op, char *b)
{
char *buf;
buf = alloc_size(strlen(op)+strlen(a)+strlen(b)+1);
sprintf(buf, "%s%s%s", a, op, b);
free(a);
free(b);
return buf;
}
static char *merge2(const char *op, char *a)
{
char *buf;
buf = alloc_size(strlen(op)+strlen(a)+1);
sprintf(buf, "%s%s", op, a);
free(a);
return buf;
}
static char *unparse_op(const struct expr *expr, enum prec prec)
{
char tmp[100];
char *buf, *temp;
if (prec > precedence(expr->op)) {
temp = unparse_op(expr, prec_add);
buf = alloc_size(strlen(temp)+3);
sprintf(buf, "(%s)", temp);
free(temp);
return buf;
}
if (expr->op == op_num) {
snprintf(tmp, sizeof(tmp), "%lg%s",
expr->u.num.n, str_unit(expr->u.num));
return stralloc(tmp);
}
if (expr->op == op_var)
return stralloc(expr->u.var);
if (expr->op == op_minus)
return merge2("-", unparse_op(expr->u.op.a, prec_add));
if (expr->op == op_add)
return merge3(unparse_op(expr->u.op.a, prec_add), "+",
unparse_op(expr->u.op.b, prec_add));
if (expr->op == op_sub)
return merge3(unparse_op(expr->u.op.a, prec_add), "-",
unparse_op(expr->u.op.b, prec_mult));
if (expr->op == op_mult)
return merge3(unparse_op(expr->u.op.a, prec_mult), "*",
unparse_op(expr->u.op.b, prec_mult));
if (expr->op == op_div)
return merge3(unparse_op(expr->u.op.a, prec_mult), "/",
unparse_op(expr->u.op.b, prec_primary));
abort();
}
char *unparse(const struct expr *expr)
{
return expr ? unparse_op(expr, prec_add) : stralloc("");
}

22
unparse.h Normal file
View File

@ -0,0 +1,22 @@
/*
* unparse.h - Dump an expression tree into a string
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#ifndef UNPARSE_H
#define UNPARSE_H
#include "expr.h"
char *unparse(const struct expr *expr);
#endif /* !UNPARSE_H */

100
util.c Normal file
View File

@ -0,0 +1,100 @@
/*
* util.c - Common utility functions
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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.
*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "util.h"
/* ----- printf buffer allocation ------------------------------------------ */
char *stralloc_vprintf(const char *fmt, va_list ap)
{
va_list aq;
char *buf;
int n;
va_copy(aq, ap);
n = snprintf(NULL, 0, fmt, aq);
va_end(aq);
buf = alloc_size(n+1);
vsnprintf(buf, n+1, fmt, ap);
return buf;
}
char *stralloc_printf(const char *fmt, ...)
{
va_list ap;
char *s;
va_start(ap, fmt);
s = stralloc_vprintf(fmt, ap);
va_end(ap);
return s;
}
/* ----- identifier syntax check ------------------------------------------- */
int is_id_char(char c, int first)
{
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
return 1;
if (first)
return 0;
return c >= '0' && c <= '9';
}
int is_id(const char *s)
{
const char *p;
if (!*s)
return 0;
for (p = s; *p; p++)
if (!is_id_char(*p, s == p))
return 0;
return 1;
}
/* ----- unique identifiers ------------------------------------------------ */
static struct unique {
const char *s;
struct unique *next;
} *uniques = NULL;
/* @@@ consider using rb trees */
const char *unique(const char *s)
{
struct unique **walk;
for (walk = &uniques; *walk; walk = &(*walk)->next)
if (!strcmp(s, (*walk)->s))
return (*walk)->s;
*walk = alloc_type(struct unique);
(*walk)->s = stralloc(s);
(*walk)->next = NULL;
return (*walk)->s;
}

58
util.h Normal file
View File

@ -0,0 +1,58 @@
/*
* util.h - Common utility functions
*
* Written 2006, 2009 by Werner Almesberger
* Copyright 2006, 2009 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.
*/
#ifndef UTIL_H
#define UTIL_H
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#define alloc_size(s) \
({ void *alloc_size_tmp = malloc(s); \
if (!alloc_size_tmp) \
abort(); \
alloc_size_tmp; })
#define alloc_type(t) ((t *) alloc_size(sizeof(t)))
#define zalloc_type(t) \
({ t *zalloc_type_tmp = alloc_type(t); \
memset(zalloc_type_tmp, 0, sizeof(t)); \
zalloc_type_tmp; })
#define stralloc(s) \
({ char *stralloc_tmp = strdup(s); \
if (!stralloc_tmp) \
abort(); \
stralloc_tmp; })
#define strnalloc(s, n) \
({ char *strnalloc_tmp = alloc_size((n)+1); \
if (!strnalloc_tmp) \
abort(); \
strncpy(strnalloc_tmp, (s), (n)); \
strnalloc_tmp[n] = 0; \
strnalloc_tmp; })
char *stralloc_vprintf(const char *fmt, va_list ap);
char *stralloc_printf(const char *fmt, ...);
int is_id_char(char c, int first);
int is_id(const char *s);
const char *unique(const char *s);
#endif /* !UTIL_H */