english
version "1.0"
identify "xyz"

#: Copyright (c) 2000, 2003 by Wayne C. Gramlich.
#, All rights reserved.
#,
#, Permission to use, copy, modify, distribute, and sell this software
#, for any purpose is hereby granted without fee provided that the above
#, copyright notice and this permission are retained.  The author makes
#, no representations about the suitability of this software for any purpose.
#, It is provided "as is" without express or implied warranty.

module mill

#: This module implements a Gerber photo plotter output generator.

define mill				#: The plotter data
    record
	bar unsigned			#: Magic constant
	bottom mill_surface		#: Bottom {mill_surface}
	cell_size unsigned		#: Steps per cell
	cutter_depth unsigned		#: Depth of trace cutter (in steps)
	counter_sink_depth unsigned	#: Depth of counter-sink
	drill_depth unsigned		#: Depth of drill plunge (in steps)
	drills vector[vector[mill_node]] #: Sorted drills list
	foo unsigned			#: Magic constant
	geometry geometry		#: {geometry} object
	schematic_capture logical	#: {true} => schematic capture
	shaved_size unsigned		#: Size of a shaved via in steps
	thick_diagonal unsigned		#: Thick diagonal width in steps
	thick_width unsigned		#: Width of thick line in steps
	thin_diagonal unsigned		#: Thin diagonal width in steps
	thin_width unsigned		#: Width of thin line in steps
	top mill_surface		#: Top {mill_surface}
	travel_height unsigned		#: Height at which travel occurs
	unshaved_size unsigned		#: Size of an unshaved via in steps
	x unsigned			#: X coordinate
	x_flipped logical		#: {true}=>X coordinate flipped
	y unsigned			#: Y coordinate
	zot unsigned			#: Magic constant
    generate allocate, erase, print

define mill_node			#: One node of the mill
    record
	hole_size unsigned		#: Size of hole (=0xffffffff for none)
	segments vector[mill_segment]	#: List of segments connected to node
	shave_east logical		#: Shave east side
	shave_north logical		#: Shave north side
	shave_north_east logical	#: Shave north east side
	shave_north_west logical	#: Shave nort west side
	shave_south logical		#: Shave south side
	shave_south_east logical	#: Shave south_east side
	shave_south_west logical	#: Shave south_west side
	shave_west logical		#: Shave west side
	wire unsigned			#: Wire number
	x unsigned			#: X coordinate
	x_offset integer		#: X offset
	y unsigned			#: Y coordinate
	y_offset integer		#: Y offset
    generate address_get, allocate, erase, identical, print

define mill_point			#: One end-point of a {mill_stroke}
    record
	strokes vector[mill_stroke]	#: Strokes attached to {mill_point}
	wire unsigned			#: Wire associated with point
	x unsigned			#: X coordinate (in steps)
	y unsigned			#: Y coordinate (in steps)
    generate address_get, allocate, erase, identical, print

define mill_segment
    record
	ccwx1 unsigned			#: Counter clockwise x for {node1}
	ccwx2 unsigned			#: Counter clockwise x for {node2}
	ccwy1 unsigned			#: Counter clockwise y for {node1}
	ccwy2 unsigned			#: Counter clockwise y for {node2}
	cwx1 unsigned			#: Clockwise x for {node1}
	cwx2 unsigned			#: Clockwise x for {node2}
	cwy1 unsigned			#: Clockwise y for {node1}
	cwy2 unsigned			#: Clockwise y for {node2}
	node1 mill_node			#: One end-point
	node2 mill_node			#: Other end-point
	thickness line_thickness	#: Thickness of line
    generate address_get, allocate, erase, identical, print

define mill_stroke			#: One stroke of the mill
    record
	point1 mill_point		#: First {mill_point}
	point2 mill_point		#: Second {mill_point}
	wire unsigned			#: Wire associated with stroke
    generate address_get, allocate, erase, identical, print

define mill_surface			#: One surface of milled PCB
    record
	mill mill			#: Parent mill object
	nodes vector[mill_node]		#: List of {mill_node}'s
	nodes_table set[mill_node]	#: Table of {mill_node}'s
	segments vector[mill_segment]	#: List of {mill_segments}'s
	strokes vector[mill_stroke]	#: List of {mill_stroke}'s
	points vector[mill_point]	#: List of {mill_points}'s
	points_table set[mill_point]	#: Table of {mill_points}'s
	temporary_node mill_node	#: Temporary {mill_node}
	temporary_point mill_point	#: Temporary {mill_point}
    generate address_get, allocate, erase, print



#: {mill} routines:

procedure characters_insert@mill
    takes
	mill mill
	layer layer
	x unsigned
	y unsigned
	characters_transpose logical
	characters vector[unsigned]
    returns logical

    #: This procedure will insert {characters} into {mill} at ({x}, {y})
    #, on {layer}.  If {characters_transpose} is {true}, the characters
    #, will be transposed.  If any errors occur, {true} is returned
    #, {false} otherwise.


procedure chunk_layer_insert@mill
    takes
	mill mill
	layer layer
	x unsigned
	y unsigned
	name string
    returns logical

    #: This procedure will insert a glyph into the output.  Since
    #, plotting does not support glyphs, this procedure is a no-op.


procedure create@mill
    takes
	geometry geometry
    returns mill

    #: This procedure will create and return a newly allocated {mill}
    #, object containing {geometry}.

    #, run between the pins of stand DIP package with pins on .1 inch
    #, centers.  The thick trace width is 50 mils (.050inch),  This
    #, means that there must be 25 mils of thick trace on each side
    #, the component pins.  This leaves 50 mils of space between the
    #, edges of the thick traces.  We need to run two isolation paths
    #, and the signal trace in the remaining 50 mils.  Thus,
    #, 50 = 2 * isolation_width + 1 * thin_width.

    #, out signal wire running between two pins travel in exactly the same
    #, path when the pads are being cut out.  If a pad is at grid location
    #, (X,Y) and the signal is going through (X+1,Y), we want to be sure
    #, that X*{cell_size}+{thick_width/2}+{isolation_width/2} =
    #, (X+1)*{cell_size}-{thin_width/2}-{isolation_width/2}.

    #, means that we want the cutter for two pads with no signal between the
    #, pins to have the isolation cutter travel the same path.  Thus, if
    #, there are two pads at (X,Y) and (X+2,Y) we want the pad isolation
    #, cut to be right between the two at (X+1,Y).  This means that:
    #, X*{cell_size} + {unshaved_width/2} + {isolation_width/2} =
    #, (X+2)*{cell_size} - {unshaved_width/2} - {isolation_width/2} .

    #, the X or Y axis; *not* the directly across the line:
    #, {foo}, {bar}, and {zot}:

    #, left/right of the a 45 degree line intersection the line edge meets on:
    #, {isolation_width} variable.

    #, of the trace line.

procedure connection_insert@mill
    takes
	mill mill
	x unsigned
	y unsigned
	hole_number unsigned
    returns logical

    #: This procuedure will insert a via at ({x}, {y}) with a 
    #, hole number of {hole_number} in {mill}.  {true} is returned
    #, if there are any errors and {false} otherwise.


procedure control_generate@mill
    takes
	mill mill
	control_stream out_stream
    returns_nothing

    #: This procedure will output the control commands for {mill} to
    #, {control_stream}.

    #, the origin on the flipped board.  We assume that there are
    #, only two registration holes.  Assume that the origin is
    #, near the "closest" registration hole.  After flipping the
    #, the board, the origin is next to the other hole.

procedure coordinate_helper@mill
    takes
	mill mill
	number unsigned
	out_stream out_stream
    returns_nothing

    #: This procedure will output {number} to {out_stream} using {mill}.


procedure coordinate_show@mill
    takes
	mill mill
	label string
	x unsigned
	y unsigned
	out_stream out_stream
    returns_nothing

    #: This procedure output ({x}, {y}) with {label} to {out_stream}.


procedure drill_or_counter_sink@mill
    takes
	mill mill
	drill_start unsigned
	drill_end unsigned
	depth unsigned
	message string
	x_flipped logical
	control_stream out_stream
    returns_nothing

    #: This procedure will output the commands to drill or counter_sink the
    #, holes for the drills numbered from {drill_start} through {drill_end}
    #, out to {control_stream}.  The drill/counter-sink depth is set with
    #, {depth}.  A pause message is output containing {message}.  {x_flipped}
    #, is set to {true}@{logical} to make invert the x axis.


procedure generate@mill
    takes
	pcb_file_name string
	mill_base_name string
	outline logical
	geometry geometry
	pcb_timer timer
    returns logical

    #: This procedure will read in {pcb_file_name} and generate
    #, a top and a bottom layer PCB mill file for {mill_base_name}.


procedure glyph_file_read@mill
    takes
	mill mill
	characters_mirror logical
    returns logical

    #: This procedure is used to read the `glyph file'.  Since we
    #, don't support glyphs in milled output, this procedure is a no-op.


procedure line_draw@mill
    takes
	mill mill
	layer layer
	x1 unsigned
	y1 unsigned
	x2 unsigned
	y2 unsigned
	line_thickness line_thickness
    returns logical

    #: This procedure will draw a line of thickness {line_thickness}
    #, from ({x1}, {y1}) to ({x2}, {y2}) on {layer} in {plot}.
    #, If any error occurs, {true} is returned; otherwise {false}
    #, is returned.


procedure move_to@mill
    takes
	mill mill
	x unsigned
	y unsigned
	wire unsigned
	control_stream out_stream
    returns_nothing

    #: This procedure will output a command to {control_stream}to move the
    #, mill head of {mill} to ({x}, {y}).


procedure overcut_eliminate@mill
    takes
	mill mill
    returns_nothing

    #: This procedure will remove duplicate overcuts in {mill}.


procedure pause@mill
    takes
	mill mill
	message string
	control_stream out_stream
    returns_nothing

    #: This procedure will output a pause command to {control_stream}
    #, using {message} as the message.


procedure strokes_generate@mill
    takes
	mill mill
    returns_nothing

    #: This procedure will generate a list of mill strokes for both
    #, surfaces in {mill}.


procedure via_name_insert@mill
    takes
	mill mill
	x unsigned
	y unsigned
	hole_number unsigned
	name string
    returns logical

    #: This procuedure will insert a via at ({x}, {y}) with a hole
    #, number of {hole_number} into {plot}.  The via name in {name}
    #, is ignored.  {true} is returned if there are any errors and
    #, {false} otherwise.


procedure vias_shave@mill
    takes
	mill mill
    returns_nothing

    #: This procedure will figure out which vias to shave on both
    #, surfaces in {mill}.


procedure wires_assign@mill
    takes
	mill mill
    returns_nothing

    #: This procedure will assign wire nubmers the the various electricaly
    #, connected {mill_node}'s in both surfaces of {mill}.


#: {mill_node} procedures:

procedure clockwise_locate@mill_node
    takes
	mill_node mill_node
	direction direction
    returns mill_segment

    #: This procedure will search clockwise from {direction} in {mill_node}
    #, to find the first {mill_segment} that intersects.
    #, ??@{mill_segement} is returned if no segement can found.


procedure counter_clockwise_locate@mill_node
    takes
	mill_node mill_node
	direction direction
    returns mill_segment

    #: This procedure will search counter clockwise from {direction} in
    #, {mill_node} to find the first {mill_segment} that intersects.
    #, ??@{mill_segement} is returned if no segement can found.


procedure create@mill_node
    takes
	x unsigned
	y unsigned
    returns mill_node

    #: This procedure will create and return a new {mill_node} object
    #, containing {x} and {y}.


procedure direction@mill_node
    takes
	node1 mill_node
	node2 mill_node
    returns direction

    #: This procedure will return the {direction} from {node1} to {node2}.


procedure equal@mill_node
    takes
	mill_node1 mill_node
	mill_node2 mill_node
    returns logical

    #: This procedure will return {true} if the coordinate of {mill_node1}
    #, is equal to the coordinate of {mill_node2}.


procedure hash@mill_node
    takes
	mill_node mill_node
    returns unsigned

    #: This procedure will return a hash of {mill_node}.


procedure segment_append@mill_node
    takes
	mill_node mill_node
	mill_segment mill_segment
    returns_nothing

    #: This procedure will append {mill_segment} to the segment list
    #, in {mill_node}.


procedure stroke@mill_node
    takes
	mill_node mill_node
	mill_surface mill_surface
	mill mill
    returns_nothing

    #: This procedure will identify the end-points for each {mill_segment}
    #, attached to {mill_node}.  In addition, it will generate a list of
    #, mill strokes for any via outlines needed for {mill_node} and append
    #, them to the {mill_stroke} list in {mill_surface}.

    #, acute (< 90 degrees) angles.


procedure wire_assign@mill_node
    takes
	mill_node mill_node
	wire unsigned
	indent unsigned
    returns_nothing

    #: This procedure will recursively assign {wire} to all of the
    #, {mill_node}'s that are transitively connected to {mill_node}.


#: {mill_point} procedures:

procedure create@mill_point
    takes
	x unsigned
	y unsigned
	wire unsigned
    returns mill_point

    #: This procedure will create and return a {mill_point} containing
    #, ({x}, {y}).


procedure compare@mill_point
    takes
	mill_point1 mill_point
	mill_point2 mill_point
    returns integer

    #, {mill_point1} is less than, equal to, or greater than {mill_point2}.


procedure equal@mill_point
    takes
	mill_point1 mill_point
	mill_point2 mill_point
    returns logical

    #: This procedure will return {true}@{logical} if {mill_point1} is
    #, equal to {mill_point2} and {false} otherwise.


procedure hash@mill_point
    takes
	mill_point mill_point
    returns unsigned

    #: This procedure will return a hash of {mill_point}.


procedure stroke_append@mill_point
    takes
	mill_point mill_point
	mill_stroke mill_stroke
    returns_nothing

    #: This procedure will append {mill_stroke} to the stroke list
    #, in {mill_point}.


procedure stroke_remove@mill_point
    takes
	mill_point mill_point
	mill_stroke mill_stroke
    returns_nothing

    #: This procedure will remove {mill_stroke} to the stroke list
    #, in {mill_point}.


#: {mill_segment} procedures:

procedure ccw_select@mill_segment
    takes
	mill_segment mill_segment
	mill_node mill_node
    returns unsigned, unsigned

    #: This procedure will return the counter clockwise coordinate of
    #, {mill_segement} associated {mill_node}.


procedure ccw_set@mill_segment
    takes
	mill_segment mill_segment
	mill_node mill_node
	x unsigned
	y unsigned
    returns_nothing

    #: This procedure will set the counter clockwise end point of
    #, {mill_segment} associated with {mill_node} to ({x}, {y}).


procedure create@mill_segment
    takes
	mill_node1 mill_node
	mill_node2 mill_node
	line_thickness line_thickness
    returns mill_segment

    #: This procedure will create and return a {mill_segment} object
    #, from {mill_node1} to {mill_node2} of thickness {line_thickness}.


procedure cw_select@mill_segment
    takes
	mill_segment mill_segment
	mill_node mill_node
    returns unsigned, unsigned

    #: This procedure will return the clockwise coordinate of
    #, {mill_segement} associated {mill_node}.


procedure cw_set@mill_segment
    takes
	mill_segment mill_segment
	mill_node mill_node
	x unsigned
	y unsigned
    returns_nothing

    #: This procedure will set the clockwise end point of
    #, {mill_segment} associated with {mill_node} to ({x}, {y}).


procedure compare@mill_segment
    takes
	mill_segment1 mill_segment
	mill_segment2 mill_segment
    returns integer

    #: This procedure will return -1, 0, or 1 depending upon whether
    #, {mill_segment1} is counter clockwise, equal to, or counter clockwise
    #, {mill_segment2}.  Both {mill_segment1} and {mill_segment2} must
    #, have a common {mill_node}.


procedure direction@mill_segment
    takes
	mill_segment mill_segment
	mill_node mill_node
    returns direction

    #: This procedure will return the direction of {mill_segment}
    #, in relation to {mill_node}.


procedure is_thick@mill_segment
    takes
	mill_segment mill_segment
    returns logical

    #: This procedure will return {true}@{logical} if {mill_segment}
    #, is thick and {false} otherwise.


procedure is_thin@mill_segment
    takes
	mill_segment mill_segment
    returns logical

    #: This procedure will return {true}@{logical} if {mill_segment}
    #, is thin and {false} otherwise.


procedure segment_select@mill_segment
    takes
	segment1 mill_segment
	segment2 mill_segment
    returns mill_segment

    #: This procedure will return the thicker of {segment1} and {segment2}
    #, where a segment of ??@{mill_segment} is considered to have zero
    #, thickness.  Warning messages are generated when a thick line
    #, is supressing a thin line.

    #, This is probably a mistake.

procedure show@mill_segment
    takes
	mill_segment mill_segment
	label string
	out_stream out_stream
    returns_nothing


procedure stroke@mill_segment
    takes
	mill_segment mill_segment
	mill_surface mill_surface
    returns_nothing

    #: This proceudure will add the strokes for {mill_segment} to the
    #, {mill_stroke} list in {mill_surface}.


procedure wire_assign@mill_segment
    takes
	mill_segment mill_segment
	wire unsigned
	indent unsigned
    returns_nothing

    #: This procedure will recursively assign {wire} to all {mill_node}'s
    #, that are electrically connected to {mill_segment}.


#: {mill_stroke} procedures:

procedure compare@mill_stroke
    takes
	stroke1 mill_stroke
	stroke2 mill_stroke
    returns integer

    #: This procedure will return -1, 0, or 1 depending upon whether {stroke1}
    #, is "less than", "equal to" or "greater than" {stroke2}.  {stroke1} and
    #, {stroke2} must be on the same horizontal, vertical, or diagonal line.


procedure create@mill_stroke
    takes
	point1 mill_point
	point2 mill_point
	wire unsigned
    returns mill_stroke

    #: This procedure will create and return a new {mill_stroke} object
    #, from {point1} to {point2}.


#: {mill_surface} procedures:

procedure adjoin@mill_surface
    takes
	mill_surface mill_surface
	mill_node mill_node
	direction direction
    returns logical

    #: This procedure will return {true}@{logical} if there is a pad in
    #, {direction} from {mill_node} that is electrically connected to
    #, it (and there is no intervening signal wire.


procedure control_generate@mill_surface
    takes
	mill_surface mill_surface
	mill mill
	x_flipped logical
	control_stream out_stream
    returns_nothing

    #: This procedure will output the control commands for {mill_surface} to
    #, {control_stream}.  {x_flipped} is set to true to invert the
    #, x direction.


procedure create@mill_surface
    takes
	mill mill
    returns mill_surface

    #: This procedure will create and return a new {surface} object.


procedure fetch2@mill_surface
    takes
	mill_surface mill_surface
	x unsigned
	y unsigned
    returns mill_node

    #: This procedure will fetch the {mill_node} from {mill_surface} at
    #, ({x}:{y}).  If there is no such {mill_node} at ({x}:{y}),
    #, ??@{mill_node} is returned.


procedure node_insert@mill_surface
    takes
	mill_surface mill_surface
	x unsigned
	y unsigned
    returns mill_node

    #: This procedure return the node at ({x}, {y}) in {mill_surface}.
    #, If such a node does not already exist, it will be created.


procedure notch@mill_surface
    takes
	mill_surface mill_surface
	mill_node mill_node
	direction direction
    returns logical

    #: This procedure will return {true}@{logical} if the {direction} corner
    #, of {mill_node} should be notched.  This only occurs when an signal
    #, wire makes a right angle bend at the {direction} corner of {mill_node}.


procedure overcut_eliminate@mill_surface
    takes
	mill_surface mill_surface
    returns_nothing

    #: This procedure will go through all of the strokes in {mill_surface},
    #, detect overcuts, and eliminate them.


procedure overcut_eliminate_scan@mill_surface
    takes
	mill_surface mill_surface
	table table[integer, vector[mill_stroke]]
	keys vector[integer]
    returns_nothing

    #: This procedure scan through each key in {keys} and cull out any
    #, duplicate over cuts.  The remaining strokes are appended to
    #, {mill_surface}.{strokes}.


procedure overcut_eliminate_group@mill_surface
    takes
	mill_surface mill_surface
	strokes vector[mill_stroke]
	set set[mill_point]
	key integer
    returns_nothing

    #: ...


procedure overcut_eliminate_join@mill_surface
    takes
	mill_surface mill_surface
	points vector[mill_point]
	set set[mill_point]
    returns_nothing

    #: ....

    #put@("\n\", debug_stream)



procedure overcut_eliminate_insert@mill_surface
    takes
	table table[integer, vector[mill_stroke]]
	keys vector[integer]
	key integer
	stroke mill_stroke
    returns_nothing

    #: This procedure will insert {stroke} in {table} and {keys} as necessary.


procedure point_create@mill_surface
    takes
	mill_surface mill_surface
	x unsigned
	y unsigned
	wire unsigned
    returns mill_point

    #: This procedure will return the {mill_point} assoicated with
    #, ({x}, {y}) in {mill_surface}.


procedure segment_insert@mill_surface
    takes
	mill_surface mill_surface
	x1 unsigned
	y1 unsigned
	x2 unsigned
	y2 unsigned
	line_thickness line_thickness
    returns_nothing

    #: This procedure will insert a segment from ({x1}, {y1}) to ({x2}, {y2})
    #, of thickness {line_thickness} into {mill_surface}.


procedure stroke_append@mill_surface
    takes
	mill_surface mill_surface
	x1 unsigned
	y1 unsigned
	x2 unsigned
	y2 unsigned
	wire unsigned
	flags unsigned
    returns_nothing

    #: This procedure will create a {mill_stroke} object containing
    #, {x1}, {y1}, {x2}, and {y2} and append it to the strokes list
    #, in {mill_surface}.


procedure strokes_generate@mill_surface
    takes
	mill_surface mill_surface
	mill mill
    returns_nothing

    #: This procedure will generate the {mill_stroke}'s list for {mill_surface}.


procedure via_shave@mill_surface
    routine_types
	procedure shave_set
	    takes mill_node, logical
	    returns_nothing
    takes
	mill_surface mill_surface
	x unsigned
	y unsigned
	direction direction
	shave_set shave_set
    returns_nothing

    #: This procedure will cause any via at ({x}, {y}) in {mill_surface}
    #, to be shaved using the {shave_set} if there is no segment in
    #, {direction}.


procedure wires_assign@mill_surface
    takes
	mill_surface mill_surface
    returns_nothing

    #: This procedure will figure out which nodes are electrically
    #, connected to one another.  This is used for isolation path
    #, selection.  We try to keep the cutter on one trace at a time.


procedure vias_shave@mill_surface
    takes
	mill_surface mill_surface
    returns_nothing

    #: This procedure will figure out which vias to shave on the nodes
    #, for {mill_surface}.