Just another update

This commit is contained in:
Ingy döt Net 2015-02-20 00:35:01 -05:00
parent a25938f123
commit 00a190b0a6
6591 changed files with 94363 additions and 23227 deletions

View file

@ -0,0 +1,141 @@
import Data.Char (toUpper)
import Data.Function (on)
import Data.List (zipWith4)
import System.Exit
import System.Random
-- External libraries.
import Graphics.Gloss
import Graphics.Gloss.Data.Vector
import Graphics.Gloss.Geometry
import Graphics.Gloss.Interface.IO.Game
import System.Random.Shuffle
-- A record of a hexagon-letter.
data Hex =
Hex
{ hLetter :: Char -- The letter it holds.
, hSelected :: Bool -- The flag for if the hexagon has been selected.
, hPath :: Path -- The hexagon's vertices.
, hCenter :: Point -- The center of the hexagon.
}
-- A record of the world state.
data World =
World
{ wHexes :: [Hex] -- The hexagons to interact with.
, wString :: String -- An ordering of picked letters.
}
-- Assorted helper functions.
addV, subV :: Vector -> Vector -> Vector
addV (a, b) (x, y) = (a + x, b + y)
subV (a, b) (x, y) = (a - x, b - y)
translateP :: Vector -> Path -> Path
translateP v = map (addV v)
translateV :: Vector -> Picture -> Picture
translateV (x, y) p = translate x y p
lightblue, darkblue :: Color
lightblue = makeColor 0.5 0.5 1.0 1.0
darkblue = makeColor 0.0 0.0 0.5 1.0
-- Create vertices for an n-gon with the given radius to a vertex
ngon :: Int -> Float -> Path
ngon n radius =
let angle = 2 * pi / fromIntegral n
in map (mulSV radius . unitVectorAtAngle . (* angle) . fromIntegral)
[0..(n - 1)]
-- Determine if a point lies on or within a polygon.
inPolygon :: Point -> Path -> Bool
inPolygon point path =
all (>= 0) $ zipWith detV vas vbs
where
vas = zipWith subV (drop 1 $ cycle path) path
vbs = map (subV point) path
-- Construct all of the hexagons transformed to their screen coordinates
-- to make mouse picking easier to solve.
mkHexes :: RandomGen g => g -> Float -> World
mkHexes gen radius = World hexes ""
where
letters = take 20 $ shuffle' ['A'..'Z'] 26 gen
xs = concatMap (replicate 4) [-2..2]
ys = cycle [-2..1]
inRad = radius * (cos $ degToRad 30)
yOff x = if ((floor x) :: Int) `mod` 2 == 0 then inRad else 0
yStep = inRad * 2
xStep = radius * 1.5
centers = zipWith (\x y -> (x * xStep, yOff x + y * yStep)) xs ys
paths = map (flip translateP $ ngon 6 radius) centers
hexes = zipWith4 Hex letters (repeat False) paths centers
-- Draw a single hexagon-letter.
drawHex :: Hex -> Picture
drawHex (Hex letter selected path center) =
pictures [hex, outline, letterPic]
where
hex = color hcolor $ polygon path
outline = color blue $ lineLoop path
letterPic = color lcolor
$ translateV (addV (-10, -10) center)
$ scale 0.25 0.25
$ text [letter]
(hcolor, lcolor) = if selected
then (darkblue, white)
else (lightblue, black)
-- Draw the whole scene.
drawWorld :: World -> Picture
drawWorld (World hexes string) =
pictures [pictures $ map drawHex hexes
,pictures $ map drawHighHex hexes
,color (light lightblue) $ textPic
,scale 1.05 1.05 $ textPic]
where
drawHighHex hex = color black $ scale 1.05 1.05 $ lineLoop $ hPath hex
textPic = translateV (-130, -175) $ scale 0.15 0.15 $ text string
-- Handle keyboard and mouse events and update the hexagons
-- accordingly. This function checks the hexagon states and
-- invokes a system exit when all are marked selected.
handleInput :: Event -> World -> IO World
handleInput event world@(World hexes string) =
case event of
EventKey key Down _ point ->
case key of
SpecialKey KeyEsc -> exitSuccess
Char char -> hCond (\hex -> hLetter hex == toUpper char)
MouseButton _ -> hCond (\hex -> inPolygon point $ hPath hex)
_ -> return world
_ ->
return world
where
checkWorld w = if all hSelected $ wHexes w then exitSuccess else return w
hCond cond = checkWorld $ World newHexes newString
where
newHexes = flip map hexes
(\hex -> if cond hex
then hex {hSelected = True}
else hex)
diff = map fst
$ filter (uncurry ((/=) `on` hSelected))
$ zip hexes newHexes
newString = case diff of
[] -> string
(hex:_) -> string ++ [hLetter hex]
main :: IO ()
main = do
stdGen <- getStdGen
playIO
(InWindow "Honeycombs" (500, 500) (100, 100))
white
60
(mkHexes stdGen 30)
(return . drawWorld)
handleInput
(\_ x -> return x)

View file

@ -0,0 +1,134 @@
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Honeycombs extends JFrame {
HoneycombsPanel panel;
public static void main(String[] args) {
JFrame f = new Honeycombs();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
public Honeycombs() {
Container content = getContentPane();
content.setLayout(new BorderLayout());
panel = new HoneycombsPanel();
content.add(panel, BorderLayout.CENTER);
setTitle("Honeycombs");
setResizable(false);
pack();
setLocationRelativeTo(null);
}
}
class HoneycombsPanel extends JPanel {
Hexagon[] comb;
public HoneycombsPanel() {
setPreferredSize(new Dimension(600, 500));
setBackground(Color.white);
setFocusable(true);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
for (Hexagon hex : comb)
if (hex.contains(e.getX(), e.getY())) {
hex.setSelected();
break;
}
repaint();
}
});
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
for (Hexagon hex : comb)
if (hex.letter == Character.toUpperCase(e.getKeyChar())) {
hex.setSelected();
break;
}
repaint();
}
});
char[] letters = "LRDGITPFBVOKANUYCESM".toCharArray();
comb = new Hexagon[20];
int x1 = 150, y1 = 100, x2 = 225, y2 = 143, w = 150, h = 87;
for (int i = 0; i < comb.length; i++) {
int x, y;
if (i < 12) {
x = x1 + (i % 3) * w;
y = y1 + (i / 3) * h;
} else {
x = x2 + (i % 2) * w;
y = y2 + ((i - 12) / 2) * h;
}
comb[i] = new Hexagon(x, y, w / 3, letters[i]);
}
requestFocus();
}
@Override
public void paintComponent(Graphics gg) {
super.paintComponent(gg);
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setFont(new Font("SansSerif", Font.BOLD, 30));
g.setStroke(new BasicStroke(3));
for (Hexagon hex : comb)
hex.draw(g);
}
}
class Hexagon extends Polygon {
final Color baseColor = Color.yellow;
final Color selectedColor = Color.magenta;
final char letter;
private boolean hasBeenSelected;
Hexagon(int x, int y, int halfWidth, char c) {
letter = c;
for (int i = 0; i < 6; i++)
addPoint((int) (x + halfWidth * Math.cos(i * Math.PI / 3)),
(int) (y + halfWidth * Math.sin(i * Math.PI / 3)));
getBounds();
}
void setSelected() {
hasBeenSelected = true;
}
void draw(Graphics2D g) {
g.setColor(hasBeenSelected ? selectedColor : baseColor);
g.fillPolygon(this);
g.setColor(Color.black);
g.drawPolygon(this);
g.setColor(hasBeenSelected ? Color.black : Color.red);
drawCenteredString(g, String.valueOf(letter));
}
void drawCenteredString(Graphics2D g, String s) {
FontMetrics fm = g.getFontMetrics();
int asc = fm.getAscent();
int dec = fm.getDescent();
int x = bounds.x + (bounds.width - fm.stringWidth(s)) / 2;
int y = bounds.y + (asc + (bounds.height - (asc + dec)) / 2);
g.drawString(s, x, y);
}
}

View file

@ -0,0 +1,184 @@
package graphics;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import static java.util.Arrays.parallelSetAll;
import static java.util.Arrays.stream;
import static java.util.stream.IntStream.range;
public class Honeycombs extends JFrame {
HoneycombsPanel panel;
public static void main(String[] args) {
JFrame f = new Honeycombs();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
public Honeycombs() {
Container content = getContentPane();
content.setLayout(new BorderLayout());
panel = new HoneycombsPanel();
content.add(panel, BorderLayout.CENTER);
setTitle("Honeycombs");
setResizable(false);
pack();
setLocationRelativeTo(null);
}
}
class HoneycombsPanel extends JPanel {
Hexagon[] comb;
@FunctionalInterface
private static interface MousePressed extends MouseListener {
public static MousePressed new_(MousePressed mousePressed) {
return mousePressed;
}
@Override
public default void mouseClicked(MouseEvent mouseEvent) {}
@Override
public default void mouseEntered(MouseEvent mouseEvent) {}
@Override
public default void mouseExited(MouseEvent mouseEvent) {}
@Override
public default void mouseReleased(MouseEvent mouseEvent) {}
}
@FunctionalInterface
private static interface KeyPressed extends KeyListener {
public static KeyPressed new_(KeyPressed keyPressed) {
return keyPressed;
}
@Override
public default void keyReleased(KeyEvent keyEvent) {}
@Override
public default void keyTyped(KeyEvent keyEvent) {}
}
public HoneycombsPanel() {
setPreferredSize(new Dimension(600, 500));
setBackground(Color.white);
setFocusable(true);
addMouseListener(MousePressed.new_(e -> {
stream(comb)
.filter(hex -> hex.contains(e.getX(), e.getY()))
.findFirst()
.ifPresent(hex -> hex.setSelected())
;
repaint();
}));
addKeyListener(KeyPressed.new_(e -> {
stream(comb)
.filter(hex -> hex.letter == Character.toUpperCase(e.getKeyChar()))
.findFirst()
.ifPresent(hex -> hex.setSelected())
;
repaint();
}));
char[] letters = "LRDGITPFBVOKANUYCESM".toCharArray();
comb = new Hexagon[20];
int x1 = 150, y1 = 100, x2 = 225, y2 = 143, w = 150, h = 87;
parallelSetAll(comb, i -> {
int x, y;
if (i < 12) {
x = x1 + (i % 3) * w;
y = y1 + (i / 3) * h;
} else {
x = x2 + (i % 2) * w;
y = y2 + ((i - 12) / 2) * h;
}
return new Hexagon(x, y, w / 3, letters[i]);
});
requestFocus();
}
@Override
public void paintComponent(Graphics gg) {
super.paintComponent(gg);
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
);
g.setFont(new Font("SansSerif", Font.BOLD, 30));
g.setStroke(new BasicStroke(3));
stream(comb)
.forEach(hex -> hex.draw(g))
;
}
}
class Hexagon extends Polygon {
final Color baseColor = Color.yellow;
final Color selectedColor = Color.magenta;
final char letter;
private boolean hasBeenSelected;
Hexagon(int x, int y, int halfWidth, char c) {
letter = c;
range(0, 6)
.forEach(i ->
addPoint((int) (x + halfWidth * Math.cos(i * Math.PI / 3)),
(int) (y + halfWidth * Math.sin(i * Math.PI / 3)))
)
;
getBounds();
}
void setSelected() {
hasBeenSelected = true;
}
void draw(Graphics2D g) {
g.setColor(hasBeenSelected ? selectedColor : baseColor);
g.fillPolygon(this);
g.setColor(Color.black);
g.drawPolygon(this);
g.setColor(hasBeenSelected ? Color.black : Color.red);
drawCenteredString(g, String.valueOf(letter));
}
void drawCenteredString(Graphics2D g, String s) {
FontMetrics fm = g.getFontMetrics();
int asc = fm.getAscent();
int dec = fm.getDescent();
int x = bounds.x + (bounds.width - fm.stringWidth(s)) / 2;
int y = bounds.y + (asc + (bounds.height - (asc + dec)) / 2);
g.drawString(s, x, y);
}
}

View file

@ -0,0 +1,57 @@
function Honeycombs
nRows = 4; % Number of rows
nCols = 5; % Number of columns
nHexs = nRows*nCols; % Number of hexagons
rOuter = 1; % Circumradius
startX = 0; % x-coordinate of upper left hexagon
startY = 0; % y-coordinate of upper left hexagon
delX = rOuter*1.5; % Horizontal distance between hexagons
delY = rOuter*sqrt(3); % Vertical distance between hexagons
offY = delY/2; % Vertical offset between columns
genHexX = rOuter.*cos(2.*pi.*(0:5).'./6); % x-coords of general hexagon
genHexY = rOuter.*sin(2.*pi.*(0:5).'./6); % y-coords of general hexagon
centX = zeros(1, nHexs); % x-coords of hexagon centers
centY = zeros(1, nHexs); % y-coords of hexagon centers
for c = 1:nCols
idxs = (c-1)*nRows+1:c*nRows; % Indeces of hexagons in that column
if mod(c, 2) % Odd numbered column - higher y-values
centY(idxs) = startY:-delY:startY-delY*(nRows-1);
else % Even numbered column - lower y-values
centY(idxs) = startY-offY:-delY:startY-offY-delY*(nRows-1);
end
centX(idxs) = (startX+(c-1)*delX).*ones(1, nRows);
end
[MCentX, MGenHexX] = meshgrid(centX, genHexX);
[MCentY, MGenHexY] = meshgrid(centY, genHexY);
HexX = MCentX+MGenHexX; % x-coords of hexagon vertices
HexY = MCentY+MGenHexY; % y-coords of hexagon vertices
figure
hold on
letters = char([65:90 97:122]);
randIdxs = randperm(26);
letters = [letters(randIdxs) letters(26+randIdxs)];
hexH = zeros(1, nHexs);
for k = 1:nHexs % Create patches individually
hexH(k) = patch(HexX(:, k), HexY(:, k), [1 1 0]);
textH = text(centX(k), centY(k), letters(mod(k, length(letters))), ...
'HorizontalAlignment', 'center', 'FontSize', 14, ...
'FontWeight', 'bold', 'Color', [1 0 0], 'HitTest', 'off');
set(hexH(k), 'UserData', textH) % Save to object for easy access
end
axis equal
axis off
set(gca, 'UserData', '') % List of clicked patch labels
set(hexH, 'ButtonDownFcn', @onClick)
end
function onClick(obj, event)
axesH = get(obj, 'Parent');
textH = get(obj, 'UserData');
set(obj, 'FaceColor', [1 0 1]) % Change color
set(textH, 'Color', [0 0 0]) % Change label color
set(obj, 'HitTest', 'off') % Ignore future clicks
currList = get(axesH, 'UserData'); % Hexs already clicked
newList = [currList get(textH, 'String')]; % Update list
set(axesH, 'UserData', newList)
title(newList)
end

View file

@ -1,7 +1,7 @@
Shoes.app(:height => 700, :width => 800) do
Shoes.app(title: "Honeycombs", height: 700, width: 700) do
C = Math::cos(Math::PI/3)
S = Math::sin(Math::PI/3)
Radius = 60
Radius = 60.0
letters = [
%w[L A R N D 1 2],
%w[G U I Y T 3 4],
@ -11,11 +11,11 @@ Shoes.app(:height => 700, :width => 800) do
]
def highlight(hexagon)
hexagon.style(:fill => magenta)
hexagon.style(fill: magenta)
end
def unhighlight(hexagon)
hexagon.style(:fill => yellow)
hexagon.style(fill: yellow)
end
def choose(hexagon)
@ -25,8 +25,8 @@ Shoes.app(:height => 700, :width => 800) do
if chosen.size == @hexagons.size
@chosen.text = 'Every hexagon has been chosen.'
else
@chosen.text = "Chosen: #{chosen.sort.join(',')}" +
"\nLast Chosen: #{hexagon.letter}"
@chosen.text = "Chosen: #{chosen.sort.join(',')}\n" +
"Last Chosen: #{hexagon.letter}"
end
end
@ -36,23 +36,22 @@ Shoes.app(:height => 700, :width => 800) do
letter_to_hex = {}
# create the GUI
stack(:height => height, :width => width) do
stack(height: height, width: width) do
@chosen = para("Chosen:\nLast chosen:")
# draw the hexagrams
letters.size.times do |row|
letters[0].size.times do |column|
x = 60 + column * Radius * 0.75 + (1-S)*Radius
y = 80 + row * Radius * S + (column.odd? ? S * Radius * 0.5 : 0)
letters.each_index do |row|
letters[0].each_index do |column|
x = 60 + column * Radius * 0.75 + (1-S) * Radius
y = 80 + row * S * Radius + (column.odd? ? S * Radius * 0.5 : 0)
h = shape(x-Radius, y-S*Radius) do
stroke red
strokewidth 3
move_to(x-C*Radius, y-S*Radius)
line_to(x+C*Radius, y-S*Radius)
line_to(x+Radius, y)
line_to(x+Radius, y)
line_to(x+C*Radius, y+S*Radius)
line_to(x-C*Radius, y+S*Radius)
line_to(x-Radius, y)
line_to(x-Radius, y)
line_to(x-C*Radius, y-S*Radius)
end
@ -65,26 +64,26 @@ Shoes.app(:height => 700, :width => 800) do
def choose
@state = :chosen
end
def contains?(px,py)
def contains?(px, py)
if @x-Radius < px and px <= @x-C*Radius
ratio = (px - @x + Radius).to_f/(Radius*(1-C))
return (@y - ratio*S*Radius < py and py <= @y + ratio*S*Radius)
ratio = (px - @x + Radius)/(Radius*(1-C))
@y - ratio*S*Radius < py and py <= @y + ratio*S*Radius
elsif @x-C*Radius < px and px <= @x+C*Radius
return (@y - S*Radius < py and py < @y + S*Radius)
@y - S*Radius < py and py < @y + S*Radius
elsif @x+C*Radius < px and px <= @x+Radius
ratio = (@x + Radius - px).to_f/(Radius*(1-C))
return (@y - ratio*S*Radius < py and py <= @y + ratio*S*Radius)
ratio = (@x + Radius - px)/(Radius*(1-C))
@y - ratio*S*Radius < py and py <= @y + ratio*S*Radius
else
return false
false
end
end
def inspect
'<%s,"%s",%s,%d@%d>' % [self.class, letter, chosen?, x, y]
%q(<%s,"%s",%s,%d@%d>) % [self.class, letter, chosen?, x, y]
end
end
h.x = x + x-Radius
h.y = y + y-S*Radius
h.x = x + x - Radius
h.y = y + y - S*Radius
h.letter = letters[row][column]
unhighlight h
@ -93,30 +92,29 @@ Shoes.app(:height => 700, :width => 800) do
letter_to_hex[h.letter.upcase] = h
# add the letter to the hexagon
para(h.letter) \
.style(:size => 56, :stroke => red) \
.move(h.x - C*Radius, h.y - S*Radius)
para(h.letter).style(size:56, stroke:red) \
.move(h.x - C*Radius, h.y - S*Radius)
end
end
# highlight the hexagon under the mouse
@hex_over = nil
hex_over = nil
motion do |x, y|
hex = @hexagons.find {|h| h.contains?(x,y)}
unless hex.nil? or hex.chosen?
highlight hex
end
unless @hex_over == hex or @hex_over.nil? or @hex_over.chosen?
unhighlight @hex_over
unless hex_over == hex or hex_over.nil? or hex_over.chosen?
unhighlight hex_over
end
@hex_over = hex
hex_over = hex
end
# handle mouse clicks
click do |button, x, y|
info("button #{button} clicked at (#{x}, #{y})")
hexagon = @hexagons.find {|h| h.contains?(x,y)}
unless hexagon.nil?
if hexagon
info("clicked hexagon #{hexagon}")
choose hexagon
end