Skip to content

Instantly share code, notes, and snippets.

@zeffii
Last active December 22, 2015 07:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeffii/6441431 to your computer and use it in GitHub Desktop.
Save zeffii/6441431 to your computer and use it in GitHub Desktop.
coffee_sol_lewitt_rules4c

[ Launch: coffee_sol_lewitt_rules4b ] 6441431 by zeffii
[ Launch: coffee_sol_lewitt_rules4 ] 6440274 by zeffii
[ Launch: coffee_sol_lewitt_rules3 ] 6436229 by zeffii
[ Launch: coffee_sol_lewitt_rules2 ] 6435259 by zeffii
[ Launch: coffee_sol_lewitt_rules ] 6422996 by zeffii
[ Launch: coffee_template ] 6420246 by zeffii
[ Launch: boomstick_motion_wcolor_coffee ] 6399870 by zeffii
[ Launch: boomstick_motion_wcolor_coffee ] 6382272 by zeffii
[ Launch: boomstick_motion_wcolor_coffee ] 6382237 by zeffii
[ Launch: boomstick_motion_wcolor_coffee ] 6379220 by zeffii
[ Launch: boomstick_motion_wcolor ] 6376715 by zeffii
[ Launch: boomstick_motion2 ] 6365156 by zeffii
[ Launch: boomstick_motion ] 6364686 by zeffii
[ Launch: boomstick ] 6364584 by zeffii
[ Launch: zeffii default ] 6364028 by zeffii
[ Launch: zeffii default ] 5033869 by zeffii

german version found, but I highly recommend not looking at the drawing until
after you have spent time trying to figure out how to interpret the instructions.
at:
" the placing of a quad "
full page (german)
http://www.walkerart.org/collections/artworks/die-plazierung-eines-vierecks-the-location-of-a-quadrangle
just text (german)
image: http://i.imgur.com/ZpOWUi2.png
© The LeWitt Estate / Artists Rights Society (ARS), New York
Translation is my own: (it was bugging me, but it is no improvement)
The first line starts **from** a point halfway between a point halfway between
the centre of the page (wall) and the bottom right corner and the middle of
the left edge and the top right corner **to** a point halfway between
the middle of the bottom edge and top right corner.
The second line starts **from** a point halfway between the starting-point of
the 1st line and a point halfway between the middle of the bottom edge and
the top left corner **to** a point halfway between a point halfway between
the center of the page (wall) and the bottom left corner and the middle of
the bottom edge.
The third line starts **from** a point halfway between a point halfway between
the starting-point of the 1st line and the end-point of the 2nd line and
a point halfway between the middle of the left edge and the bottom left corner
**to** a point 'auf einer Achse von der' bottom left corner to a point
halfway between the middle of the right edge and the top right corner,
where a line from the center of the page to a point halfway between
the middle of the right edge and the bottom right corner would intersect the axis.
The fourth line starts **from** a point the same distance between the end of
the 3rd line, the endpoint of the 2nd line, and from a point halfway between
a point halfway between the center of the page and the middle of
the bottom edge and a point halfway between the middle of the bottom edge and
the bottom right corner **to** a point halfway between the starting point of
the 2nd line and a point, where a line would intersect the 1st line, if it ran
from the middle of the right edge to a point halfway between the middle of
the top edge and the top left corner.
SL 1974.
My thoughts:
Though Written in a procedural way, these instructions are at times somewhat
obfuscated and definitely inefficient without symbols.
- The 1st line turns out to be unparsable without adding an extra 'halfway between'
statement.
- The 3rd line doesn't make sense to me introducing the term axis
- a cool exercise is to rewrite the instructions with as few characters
as possible, person who can get most people to recreate the quad accurately
wins.
{"description":"coffee_sol_lewitt_rules4c","endpoint":"","display":"svg","public":true,"require":[],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"style.css":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"data2.csv":{"default":true,"vim":false,"emacs":false,"fontSize":12},"util.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"injet.coffee":{"default":true,"vim":false,"emacs":false,"fontSize":12},"inlet.coffee":{"default":true,"vim":false,"emacs":false,"fontSize":12},"readme.txt":{"default":true,"vim":false,"emacs":false,"fontSize":12},"util.coffee":{"default":true,"vim":false,"emacs":false,"fontSize":12},"guides.coffee":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"fullscreen":false,"play":false,"loop":false,"restart":false,"autoinit":true,"pause":true,"loop_type":"period","bv":false,"nclones":15,"clone_opacity":0.4,"duration":3000,"ease":"linear","dt":0.01,"thumbnail":"http://i.imgur.com/Y3K7fb0.gif","ajax-caching":false}
tributary.make_guides = (A,B,C,D,MID,AB,BC,CD,AD,
L_support_1,L_support_1b,L_support_2,
L1, L2, L3, L4) ->
# construction lines for completeness...this isn't supposed to be comprehensive.
L1_guide_1 = {p1: MID, p2: A}
L1_guide_2 = {p1: L1_guide_1, p2: AD}
L1_guide_3 = {p1: L1_guide_2, p2: A}
L1_guide_4 = {p1: AB, p2: B}
L2_guide_1 = {p1: AB, p2: A}
L2_guide_2 = {p1: L1.p1, p2: L2_guide_1}
L2_guide_3 = {p1: MID, p2: D}
#L2_guide_4 = {p1: L2_guide_3, p2: CD}
L2_guide_5 = {p1: tributary.between(MID, D), p2: CD}
L3_guide_1 = {p1: BC, p2: B}
L3_guide_2 = {p1: BC, p2: C}
L3_guide_3 = {p1: L1.p1, p2: L2.p2}
L3_guide_4 = {p1: AD, p2: D}
L3_guide_5 = {p1: L3_guide_3, p2: L3_guide_4}
L4_guide_1 = {p1: MID, p2: CD}
L4_guide_2 = {p1: CD, p2: C}
L4_guide_3 = {p1: L4_guide_1, p2: L4_guide_2}
L4_guide_4 = {p1: L2.p1, p2: tributary.intersect(L_support_2, L1)}
AMID_AD = {p1: tributary.between(A, MID), p2: AD}
guides = [
L1_guide_1, L1_guide_2, L1_guide_3, L1_guide_4,
L2_guide_1, L2_guide_2, L2_guide_3, #L2_guide_4,
L2_guide_5,
L3_guide_1, L3_guide_2, L3_guide_3, L3_guide_4, L3_guide_5,
L4_guide_1, L4_guide_2, L4_guide_3, L4_guide_4,
AMID_AD
]
###
Info in the readme.txt tab (links to original german text)
from3points uses the fact that 2 bisectors of 3 points will
intersect at the center of the circle circumscribing those points
no more external code dependancies
###
# utils, importing from util.coffee in the tab above.
between = tributary.between
equid = tributary.from3points
intersect = tributary.intersect
tolist = tributary.tolist
button_function = tributary.button_function
make_guides = tributary.make_guides
# constants and setup
d3.select("body").style "background-color", d3.rgb(225, 225, 225)
svg = d3.select("svg")
scalar = 0.6
X = width = 966 * scalar
Y = height = 979 * scalar
thin = 0.3
thick = 1.2
A = {x: 0, y: 0} # top left
B = {x: X, y: 0} # top right
C = {x: X, y: Y} # bottom right
D = {x: 0, y: Y} # bottom left
MID = between(A, C) # center
AB = between(A, B) # mid top
BC = between(B, C) # mid right
CD = between(C, D) # mid bottom
AD = between(A, D) # mid left
paper_position = "translate(" + [200,100] + ")"
group0 = svg.append("g").classed("group0", true)
.attr
'transform': paper_position
group2 = svg.append("g").classed("group2", true)
.attr
'transform': paper_position
'opacity': 1.0
group1 = svg.append("g").classed("group1", true)
.attr
'transform': paper_position
'opacity': 1.0
.style
'fill': "none"
'stroke': "#3F3F3F"
### drawing instructions deconstructed ###
L1 =
p1: between( between( between(MID, A), AD), A)
p2: between(AB, B)
L2 =
p1: between(L1.p1, between(AB, A))
p2: between( between(MID, D), CD)
L_support_1 = {p1: D, p2: between(BC, B)}
L_support_1b = {p1: MID, p2: between(BC, C)}
L3 =
p1: between( between(L1.p1, L2.p2), between(AD, D)),
p2: intersect(L_support_1, L_support_1b)
eq =
p1: L3.p2
p2: L2.p2
p3: between(between(MID, CD), between(CD, C))
L_support_2 = {p1: BC, p2: between(AB, A)}
L4 =
p1: equid([eq.p1, eq.p2, eq.p3])
p2: between(L2.p1, intersect(L_support_2, L1))
### populate svg ###
# lines only
line_data = [
{line: {p1: A, p2: B}, line_thickness: thin},
{line: {p1: B, p2: C}, line_thickness: thin},
{line: {p1: C, p2: D}, line_thickness: thin},
{line: {p1: A, p2: D}, line_thickness: thin},
{line: L1, line_thickness: thick},
{line: L2, line_thickness: thick},
{line: L_support_1, line_thickness: thin},
{line: L_support_1b, line_thickness: thin},
{line: L_support_2, line_thickness: thin},
{line: L3, line_thickness: thick},
{line: L4, line_thickness: thick},
]
guides = make_guides(A,B,C,D,MID,AB,BC,CD,AD,
L_support_1,L_support_1b,L_support_2,
L1,L2,L3,L4)
for guide in guides
line_data.push line: guide, line_thickness: thin
lines = group1.selectAll('line').data(line_data).enter()
.append('line')
.attr
'x1': (d) -> d.line.p1.x
'y1': (d) -> d.line.p1.y
'x2': (d) -> d.line.p2.x
'y2': (d) -> d.line.p2.y
.style
'stroke-width': (d) -> d.line_thickness
# verts only
vert_data = [A, B, C, D, MID, AB, BC, CD, AD]
verts = group1.selectAll('circle').data(vert_data).enter()
.append('circle')
.attr
'r': 2.3
'cx': (d) -> d.x
'cy': (d) -> d.y
.style
'stroke': "none"
'fill': "#789BA2"
# labels_only
pu = 20
label_data = [
{n: 'A', pos: A, tr: [-pu,-pu]},
{n: 'B', pos: B, tr: [pu,-pu]},
{n: 'C', pos: C, tr: [pu,pu]},
{n: 'D', pos: D, tr: [-pu,pu]},
{n: 'MID', pos: MID, tr: [0,-0.5*pu]},
{n: 'AB', pos: AB, tr: [0,-pu]},
{n: 'BC', pos: BC, tr: [1.5*pu,0]},
{n: 'CD', pos: CD, tr: [0,1.5*pu]},
{n: 'AD', pos: AD, tr: [-1.5*pu,0]}]
labels = group1.append('g').classed('labels', true)
.style
'stroke': "none"
'fill': "#615B5B"
label = labels.selectAll('text').data(label_data).enter()
.append('text')
.attr
'x': (d) -> d.pos.x
'y': (d) -> d.pos.y
"text-anchor": "middle"
.text( (d) -> d.n)
.attr
'transform': (d) -> 'translate('+ d.tr + ')'
.style
"font-size": 14
# light bg only
group0.append("path")
.style
'fill': '#F3F1F1'
'stroke': 'none'
.attr
'd': (d) -> 'M' + tolist(A,B,C,D) + 'z'
# quad only
group1.append("path")
.style
'fill': '#B4B4B4'
'fill-opacity': 0.2
'stroke': '#D690AF'
'stroke-width': 2.16
.attr
'd': (d) ->
p1 = intersect(L1, L2)
p2 = intersect(L2, L3)
p3 = intersect(L3, L4)
p4 = intersect(L4, L1)
'M' + tolist(p1,p2,p3,p4) + 'z'
# tri only
group1.append("path")
.style
'fill': '#DAF6FF'
'fill-opacity': 0.136752
'stroke': '#D690AF'
'stroke-width': 1.16
'stroke-dasharray': "5,5"
.attr
'd': (d) -> 'M' + tolist(eq.p1, eq.p2, eq.p3) + 'z'
# image only
img_url = "http://i.imgur.com/akyto53.png";
group2.insert("image")
.attr
"xlink:href": img_url
"width": width
"height": height
# buttons
group3 = svg.append("g").classed("group3", true)
bY = Y + 136
bX = 200
show_overlay = true
show_original = true
button_function(group3, 'button1', [bX, bY], 'hide/show overlay', '.group1', show_overlay)
button_function(group3, 'button2', [bX+200, bY], 'hide/show original', '.group2', show_original)
# EOF
.cm-s-elegant.CodeMirror { background: #1e2426; color: #696969; }
.cm-s-elegant div.CodeMirror-selected {background: #064968 !important;} /* 33322B*/
.cm-s-elegant span.cm-variable { color:#22EFFF; }
.cm-s-elegant span.cm-variable-2 { color: #FFCCB4; }
.cm-s-elegant span.cm-variable-3 { color: white; }
.cm-s-elegant span.cm-string { color: Chartreuse; }
.cm-s-elegant span.cm-string-2 {color: Chartreuse;}
.cm-s-elegant span.cm-def {color: #FFCCB4; opacity: 1.0}
.cm-s-elegant span.cm-bracket { color: #EBEFE7; }
.cm-s-elegant pre { color:#FFF; }
.cm-s-elegant span.cm-qualifier { color:#C0C0C0; }
.cm-s-elegant span.cm-comment { color: #AFB4B4;}
.cm-s-elegant span.cm-property {color: #FDA676;}
.cm-s-elegant span.cm-number { color: #FF92EE;}
.cm-s-elegant span.cm-keyword { color: #FFFF18; }
.cm-s-elegant .CodeMirror-cursor { border-left: 1px solid white !important; }
.cm-s-elegant .CodeMirror-gutters {background: #505050;}
.cm-s-elegant .CodeMirror-linenumber {color: #D3D3D3;}
tributary.between = (p1, p2) ->
x: (p1.x + p2.x) * 0.5
y: (p1.y + p2.y) * 0.5
tributary.circle_circle_bisector = (p0, p1) ->
# literal translation from C to .coffee of
# http://paulbourke.net/geometry/circlesphere/tvoght.c
r0 = r1 = tributary.distance(p0, p1) * 0.65
[x0, y0] = [p0.x, p0.y]
[x1, y1] = [p1.x, p1.y]
[dx, dy] = [x1 - x0, y1 - y0]
d = Math.sqrt((dy*dy) + (dx*dx));
return None if (d > (r0 + r1)) or (d < Math.abs(r0 - r1))
a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ;
x2 = x0 + (dx * a/d);
y2 = y0 + (dy * a/d);
h = Math.sqrt((r0*r0) - (a*a));
rx = -dy * (h/d);
ry = dx * (h/d);
p1: {x: x2 + rx, y: y2 + ry}
p2: {x: x2 - rx, y: y2 - ry}
tributary.from3points = (points) ->
[a, b, c] = [points...]
line1 = tributary.circle_circle_bisector(a, b)
line2 = tributary.circle_circle_bisector(b, c)
p_center = tributary.intersect(line1, line2)
#p: p_center
#line1: line1
#line2: line2
tributary.intersect = (line1, line2) ->
[x1,y1,x2,y2] = [line1.p1.x, line1.p1.y, line1.p2.x, line1.p2.y]
[x3,y3,x4,y4] = [line2.p1.x, line2.p1.y, line2.p2.x, line2.p2.y]
[bx, by_, dx, dy] = [x2 - x1, y2 - y1, x4 - x3, y4 - y3]
b_dot_d_perp = (bx * dy) - (by_ * dx)
return None if b_dot_d_perp is 0
[cx, cy] = [x3 - x1, y3 - y1]
t = (cx * dy - cy * dx) / b_dot_d_perp
x: x1 + t * bx
y: y1 + t * by_
tributary.tolist = (points...) ->
plist = []
for p in points
for c in [p.x, p.y]
plist.push c
plist
tributary.distance = (pos1, pos2) ->
a = Math.max(pos1.x, pos2.x) - Math.min(pos1.x, pos2.x)
b = Math.max(pos1.y, pos2.y) - Math.min(pos1.y, pos2.y)
Math.sqrt((a * a) + (b * b))
tributary.button_function = (group, classname, pos, text, destination, switcher) ->
button = group.append('g').classed(classname, true)
.attr
transform: 'translate(' + pos + ')'
button
.append('rect')
.attr
height: 20, width: 33, ry: 5
.style fill: '#BDBDBD', stroke: '#707070', 'stroke-width': 0.7
button
.append('text')
.text(text)
.attr
transform: 'translate(' + [40, 16] + ')'
.style
fill: '#606177'
"font-size": 14
button
.on('click', (d) ->
g = d3.select(destination)
.transition()
.duration(1400)
.attr
'opacity': if switcher then 0.001 else .99
switcher = !switcher
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment