This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright (c) 2011 Martin Prout | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
* License as published by the Free Software Foundation; either | |
* version 2.1 of the License, or (at your option) any later version. | |
* | |
* http://creativecommons.org/licenses/LGPL/2.1/ | |
* | |
* This library is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public | |
* License along with this library; if not, write to the Free Software | |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
*/ | |
package lut; | |
/** | |
* A very restricted lookup table for fast sine & cosine computations. The table | |
* currently has a fixed precision of 1.0 degrees. Thus should be as accurate as | |
* Math.sin when using integer input. However with a float input, values are | |
* cast to integer, and there will be errors. Note the reduced lookup up table, | |
* is restricted to the first quadrant of sine. Conditional rules are used to | |
* map to other quadrants and cos. Based on ideas from:- | |
* http://en.wikipedia.org/wiki/Lookup_table | |
* One annoyance of java is the behaviour of % wrt negative values cf python for | |
* example. A kludge is required to return the complement of 360, which would | |
* not otherwise be required. Compile this and jar library as lut.jar | |
*/ | |
public class LUT { | |
/** | |
* Lookup table for degree cosine/sine, has a fixed precision 1.0 degrees | |
* @author Martin Prout <martin_p@lineone.net> | |
*/ | |
public static float[] sinLUT = new float[91]; | |
/** | |
* Message to display on console processing ide | |
*/ | |
public static final String message = "Sine/Cosine lookup tables initialized" | |
+ " with a fixed\nprecision of 1.0 degrees. NB: degree input. Use\n" | |
+ "LUT2 for greater precision (of ca. 0.25 degrees)\n"; | |
/** | |
* Initialise sin table with values (first quadrant only) | |
*/ | |
public static void initialize() { | |
for (int i = 0; i <= 90; i++) { | |
sinLUT[i] = (float) Math.sin(Math.toRadians(i)); | |
} | |
System.out.print(message); | |
} | |
/** | |
* Look up sine for the passed angle in degrees. | |
* | |
* @param thet degree int | |
* @return sine value for theta | |
*/ | |
public static float sin(int thet) { | |
while (thet < 0) { | |
thet += 360; // Needed because negative modulus plays badly in java | |
} | |
int theta = thet % 360; | |
int y = theta % 90; | |
float result = (theta < 90) ? sinLUT[y] : (theta < 180) | |
? sinLUT[90 - y] : (theta < 270) | |
? -sinLUT[y] : -sinLUT[90 - y]; | |
return result; | |
} | |
/** | |
* Look up sin for the passed angle in degrees. NB lacks precision unless | |
* float is round number (needed to work with pen and turtle interface) | |
* Casting to int rather than rounding is deliberate, use LUT2 instead for | |
* greater precision with a decimal float input | |
* @param thet degree float | |
* @return sin value for theta | |
*/ | |
public static float sin(float thet) { | |
return LUT.sin((int) thet); | |
} | |
/** | |
* Look up cos for the passed angle in degrees. | |
* | |
* @param thet degree int | |
* @return sine value for theta | |
*/ | |
public static float cos(int thet) { | |
while (thet < 0) { | |
thet += 360; // Needed because negative modulus plays badly in java | |
} | |
int theta = thet % 360; | |
int y = theta % 90; | |
float result = (theta < 90) ? sinLUT[90 - y] : (theta < 180) | |
? -sinLUT[y] : (theta < 270) | |
? -sinLUT[90 - y] : sinLUT[y]; | |
return result; | |
} | |
/** | |
* Look up cos for the passed angle in degrees. NB lacks precision unless | |
* float is round number (needed to work with pen and turtle interface) | |
* Casting to int rather than rounding is deliberate, use LUT2 instead for | |
* greater precision with a decimal float input | |
* @param thet degree float | |
* @return sine value for theta | |
*/ | |
public static float cos(float thet) { | |
return LUT.cos((int) thet); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
############################################################# | |
# library/grammar/grammar.rb | |
# Non-stochastic grammar | |
# with unique premise/rules | |
############################################################ | |
class Grammar | |
attr_accessor :axiom, :rules | |
def initialize axiom | |
@axiom = axiom | |
@rules = Hash.new | |
end | |
def add_rule premise, rule | |
rules.store(premise, rule) | |
end | |
########################################## | |
# replace each pre char with a unique rule | |
########################################## | |
def new_production production | |
production.gsub!(/./) { |c| (r = @rules[c]) ? r : c } | |
end | |
########################################## | |
# control the number of iterations | |
# default 0, returns the axiom | |
########################################## | |
def generate repeat = 0 | |
prod = axiom | |
repeat.times do | |
prod = new_production prod | |
end | |
return prod | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Rod_Hilbert < Processing::App | |
#full_screen # NB: All distances are relative to screen height | |
load_libraries 'grammar', 'lut' | |
import 'lut' | |
attr_reader :grammar, :production, :distance, :depth, :centre_adjust | |
# some lsystem constants | |
CENTER_ADJUST = [0, 0.5, 1.5, 3.5, 7.5] | |
XPOS = 0 | |
YPOS = 1 | |
ANGLE = 2 | |
BEN = Math::PI/720 # use BEN to create a bent Hilbert | |
THETA = Math::PI/2 # + BEN | |
PHI = Math::PI/2 #- BEN | |
AXIOM = "A" | |
def setup() | |
size 600, 600, P3D | |
#render_mode P3D | |
LUT.initialize | |
@grammar = Grammar.new(AXIOM) | |
@grammar.add_rule "A", "B>F<CFC<F>D+F-D>F<1+CFC<F<B1^" | |
@grammar.add_rule "B", "A+F-CFB-F-D1->F>D-1>F-B1>FC-F-A1^" | |
@grammar.add_rule "C", "1>D-1>F-B>F<C-F-A1+FA+F-C<F<B-F-D1^" | |
@grammar.add_rule "D", "1>CFB>F<B1>FA+F-A1+FB>F<B1>FC1^" | |
@depth = 2 | |
reset(depth) | |
camera(width/2.0, height/2.0, 600, 0, 0, 0, 0, -1, 0) | |
noStroke() | |
end | |
def reset(depth) | |
@distance = 380 | |
create_grammar(depth) | |
end | |
def create_grammar(gen) | |
if (gen > 0) | |
@distance *= 1.0/((2**gen) - 1.0) | |
@production = grammar.generate gen | |
end | |
end | |
def render(production) | |
""" | |
Render evaluates the production string and calls box primitive | |
uses processing affine transforms (translate/rotate) | |
""" | |
lightSpecular(30, 30, 30) | |
ambient(192, 192, 192) | |
ambientLight(80, 80, 80) | |
directionalLight(0, 0, 0, 80, 80, 80) | |
specular(40, 40, 40) | |
shininess(0.3) | |
fill(192, 192, 192) | |
sphere_detail(10) | |
sphere(distance/7) # first sphere end cap | |
repeat = 1 | |
production.scan(/./) do |ch| | |
case(ch) | |
when "F" | |
draw_rod(distance) # NB: translation done in draw_rod | |
when '+' | |
rotate_x(THETA * repeat) | |
repeat = 1 | |
when '-' | |
rotate_x(-THETA * repeat) | |
repeat = 1 | |
when '>' | |
rotate_y(THETA * repeat) | |
repeat = 1 | |
when '<' | |
rotate_y(-THETA * repeat) | |
when '^' | |
rotateZ(PHI * repeat) | |
repeat = 1 | |
when '1' | |
repeat = 2 | |
when 'A', 'B', 'C', 'D' | |
else | |
puts("character '#{ch}' not in grammar") | |
end | |
end | |
end | |
def draw_rod(distance) | |
sides = 10 | |
radius = distance/7 | |
angle = 0 | |
angle_increment = 360 / sides | |
translate(0, 0, -distance / 2.0) | |
begin_shape(QUAD_STRIP) | |
for i in 0...sides+1 | |
normal(LUT.cos(angle), LUT.sin(angle), 0) | |
vertex(radius*LUT.cos(angle), radius*LUT.sin(angle), -distance/2) | |
vertex(radius*LUT.cos(angle), radius*LUT.sin(angle), distance/2) | |
angle += angle_increment | |
end | |
end_shape() | |
translate(0, 0, -distance/2) | |
sphere(radius) | |
end | |
def draw() | |
background(10, 10, 200) | |
lights() | |
push_matrix() | |
rotate_x(LUT.sin(frame_count)) | |
rotate_y(LUT.cos(frame_count)) | |
push_matrix() | |
translate( distance * CENTER_ADJUST[depth], -distance * CENTER_ADJUST[depth], distance * CENTER_ADJUST[depth]) | |
render(production) | |
pop_matrix() | |
pop_matrix() | |
end | |
end | |
No comments:
Post a Comment