#!/usr/bin/ruby
###############################################################################
# class Node
class Node
attr_reader :x, :y, :edges
def initialize(x,y)
@x=x
@y=y
@nb_edges=0
@edges=Array.new #list of edges connected to this node. In principle
#there would just need to be a set, since we don't
#need indexes (as long as we can iterate)
end
def to_svg
return "\n"
end
def to_s
return "------------\nNode: (#{@x},#{@y}). Edges:\n" + @edges.to_s + "-------------"
end
def add_edge(e)
@nb_edges=@nb_edges+1
@edges[@nb_edges-1]=e
end
end
###############################################################################
# class Edge
class Edge
attr_reader :node1, :node2, :sign, :angle1, :angle2
def initialize(n1,n2,sign)
@node1=n1
@node2=n2
@sign=sign
@angle1=Math.atan2(@node2.y-@node1.y, @node2.x-@node1.x)
@angle2=Math.atan2(@node1.y-@node2.y, @node1.x-@node2.x)
end
def to_svg
return "
"
end
def to_s
return "Edge: (#{@node1.x},#{@node1.y}), (#{@node2.x},#{@node2.y}). Angles: #{@angle1}, #{@angle2}\n"
end
def angle(n)
#returns the angle of the edge at Node n
if n==@node1 then @angle1 elsif n==@node2 then @angle2 else "error!" end
end
end
###############################################################################
# class Graph
class Graph
attr_reader :nb_nodes, :nb_edges, :nodes, :edges
def initialize()
@nb_nodes=0
@nb_edges=0
@nodes=Array.new
@edges=Array.new
end
def add_node(n)
@nodes[@nb_nodes]=n
@nb_nodes += 1
end
def add_edge(e)
@edges[@nb_edges]=e
@nb_edges+=1
#for each node n of e, add to n a pointer to e
e.node1.add_edge(e)
e.node2.add_edge(e)
end
def next_edge_around(n, e, direction)
#return the next edge after e around node n clockwise
if direction=="clockwise" then minmax=10 else minmax=-10 end
# as long as it's greater than Pi (clockwise) or less than -Pi
# (anticlockwise), then we're ok
next_edge = e
# we must iterate through all the edges stemming from n
n.edges.each do |edge|
if edge != e then
angle=edge.angle(n)
if (direction=="clockwise" and angle < minmax) or
(direction=="anticlockwise" and angle > minmax) then
next_edge=edge
minmax=angle
end
end
end
return next_edge
end
def to_svg()
s="";
@nodes.each { |n| s+=n.to_svg }
@edges.each { |e| s+=e.to_svg }
return s
end
def min_x()
min=1e200
@nodes.each { |n| if n.x < min then min=n.x end }
return min
end
def max_x()
max=-1e200
@nodes.each { |n| if n.x > max then max=n.x end }
return max
end
def min_y()
min=1e200
@nodes.each { |n| if n.x < min then min=n.x end }
return min
end
def max_y()
max=-1e200
@nodes.each { |n| if n.x > max then max=n.x end }
return max
end
end
###############################################################################
# class CelticPattern
class CelticPattern
BoundingBoxMargin=3
def initialize(graph)
@graph = graph
end
def draw_spline(node, edge1, edge2)
# P1 (x1,y1) is the middle point of edge1
x1=(edge1.node1.x+edge1.node2.x)/2.0
y1=(edge1.node1.y+edge1.node2.y)/2.0
# P2 (x2,y2) is the middle point of edge1
x4=(edge2.node1.x+edge2.node2.x)/2.0
y4=(edge2.node1.y+edge2.node2.y)/2.0
alpha=beta=0.9
# I1 sticks out from edge1 to the right (coming from node)
# i.e. P1I1 = P1N /\ -alphaZ
i1x = -alpha*(node.y-y1)+x1
i1y = alpha*(node.x-x1)+y1
# I2 sticks out from edge2 to the left (coming from node)
# i.e. P4I2 = P1N /\ alphaZ
i2x = alpha*(node.y-y4)+x4
i2y = -alpha*(node.x-x4)+y4
# P2 is drawn from I1 coming back toward N opposite NP1 and parallel to it
# i.e. I1P2 = I1P1 /\ -betaZ
x2 = -beta*(y1-i1y) + i1x
y2 = beta*(x1-i1x) + i1y
# P3 is drawn from I2 coming back toward N opposite NP4 and parallel to it
# i.e. I2P3 = I2P4 /\ betaZ
x3 = beta*(y4-i2y) + i2x
y3 = -beta*(x4-i2x) + i2y
return "\n"
end
def draw_spline_direction(node, edge1, edge2, direction)
# P1 (x1,y1) is the middle point of edge1
x1=(edge1.node1.x+edge1.node2.x)/2.0
y1=(edge1.node1.y+edge1.node2.y)/2.0
# P2 (x2,y2) is the middle point of edge1
x4=(edge2.node1.x+edge2.node2.x)/2.0
y4=(edge2.node1.y+edge2.node2.y)/2.0
if direction == "clockwise" and (x1-node.x)*(y4-node.y)-(y1-node.y)*(x4-node.x) < 0 then
draw_spline(node, edge2, edge1)
else
draw_spline(node, edge1, edge2)
end
then
def to_svg_by_node
s= "\n"
@graph.nodes.each { |n|
if n.edges
current_edge=first_edge=n.edges[0]
next_edge=@graph.next_edge_around(n,first_edge,"anticlockwise")
while next_edge != first_edge do
s+= draw_spline(n,current_edge,next_edge);
current_edge=next_edge
next_edge=@graph.next_edge_around(n,next_edge,"anticlockwise")
end
s+= draw_spline(n,current_edge,next_edge);
end
}
return s+="\n"
end
# draw_spline(n,e1,e2,direction)
def to_svg_by_curve
first_node = node = @graph.nodes[0]
first_edge = edge = @graph.nodes[0].edges[0]
first_side = side = "left"
turn=""
begin
end until node==first_side and edge==first_edge and side==first_side
end
def to_svg
s="\n"
return s
end
end
g = Graph.new
n1 = Node.new(0.0,0.0)
n2 = Node.new(10.0,0.0)
n3 = Node.new(0.0,10.0)
g.add_node(n1)
g.add_node(n2)
g.add_node(n3)
e1 = Edge.new(n1,n2,"+")
e2 = Edge.new(n2,n3,"+")
e3 = Edge.new(n1,n3,"+")
g.add_edge(e1)
g.add_edge(e2)
g.add_edge(e3)
c = CelticPattern.new(g)
puts c.to_svg