mirror of
git://projects.qi-hardware.com/fped.git
synced 2025-01-03 17:30:15 +02:00
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:
commit
e455b37abb
113
Makefile
Normal file
113
Makefile
Normal 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
339
README
Normal 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
54
TODO
Normal 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
164
coord.c
Normal 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
78
coord.h
Normal 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
217
cpp.c
Normal 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
26
cpp.h
Normal 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
76
error.c
Normal 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
30
error.h
Normal 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
423
expr.c
Normal 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
120
expr.h
Normal 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
135
fpd.l
Normal 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
509
fpd.y
Normal 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
57
fped.c
Normal 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
588
gui.c
Normal 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
29
gui.h
Normal 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
327
gui_canvas.c
Normal 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
25
gui_canvas.h
Normal 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
87
gui_icons.c
Normal 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
22
gui_icons.h
Normal 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
421
gui_inst.c
Normal 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
50
gui_inst.h
Normal 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
476
gui_status.c
Normal 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
47
gui_status.h
Normal 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
69
gui_style.c
Normal 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
91
gui_style.h
Normal 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
167
gui_util.c
Normal 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
34
gui_util.h
Normal 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
13
icons/arc.fig
Normal 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
12
icons/circ.fig
Normal 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
14
icons/frame.fig
Normal 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
13
icons/line.fig
Normal 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
19
icons/meas.fig
Normal 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
15
icons/pad.fig
Normal 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
14
icons/point.fig
Normal 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
13
icons/rect.fig
Normal 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
11
icons/template.fig
Normal 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
17
icons/vec.fig
Normal 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
665
inst.c
Normal 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
104
inst.h
Normal 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
270
obj.c
Normal 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
167
obj.h
Normal 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
70
qfn.fpd
Normal 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
111
unparse.c
Normal 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
22
unparse.h
Normal 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
100
util.c
Normal 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
58
util.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user