|
|
|
var state = {} |
|
state.replacer = function (key,value) { |
|
if (typeof(value) === 'object') return value |
|
else return Math.floor(value * 100) / 100 |
|
} |
|
|
|
state.width = 960 |
|
state.height = 500 |
|
|
|
state.x0 = 480 |
|
state.y0 = 250 |
|
state.rot0 = 0 |
|
state.segments = 360 |
|
state.size = 100000 |
|
state.side = Math.sqrt(state.size) |
|
state.rad = state.side / 2 |
|
state.refdot = Math.round(state.segments * (1 / 4)) |
|
|
|
state.legendXloc = 5 |
|
state.legendFontSize = 30 |
|
state.legendYloc = state.height - state.legendFontSize |
|
|
|
state.noticeXloc = 5 |
|
state.noticeFontSize = 12 |
|
state.noticeYloc = state.height - state.noticeFontSize |
|
|
|
var types = { |
|
asterisk: {m: 12, n1: .3, n2: 0, n3: 10, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
bean: {m: 2, n1: 1, n2: 4, n3: 8, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
butterfly: {m: 3, n1: 1, n2: 6, n3: 2, a: .6, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
circle: {m: 4, n1: 2, n2: 2, n3: 2, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
clover: {m: 6, n1: .3, n2: 0, n3: 10, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
cloverFour: {m: 8, n1: 10, n2: -1, n3: -8, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
cross: {m: 8, n1: 1.3, n2: .01, n3: 8, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
diamond: {m: 4, n1: 1, n2: 1, n3: 1, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
drop: {m: 1, n1: .5, n2: .5, n3: .5, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
ellipse: {m: 4, n1: 2, n2: 2, n3: 2, a: 9, b: 6, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
gear: {m: 19, n1: 100, n2: 50, n3: 50, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
heart: {m: 1, n1: .8, n2: 1, n3: -8, a: 1, b: .18, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
heptagon: {m: 7, n1: 1000, n2: 400, n3: 400, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
hexagon: {m: 6, n1: 1000, n2: 400, n3: 400, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
malteseCross: {m: 8, n1: .9, n2: .1, n3: 100, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
pentagon: {m: 5, n1: 1000, n2: 600, n3: 600, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
rectangle: {m: 4, n1: 100, n2: 100, n3: 100, a: 2, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
roundedStar: {m: 5, n1: 2, n2: 7, n3: 7, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
square: {m: 4, n1: 100, n2: 100, n3: 100, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
star: {m: 5, n1: 30, n2: 100, n3: 100, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
triangle: {m: 3, n1: 100, n2: 200, n3: 200, a: 1, b: 1, tx: state.x0, ty: state.y0, rot: state.rot0}, |
|
} |
|
|
|
var format = d3.format(".4n"); |
|
|
|
var scale = d3.scaleLinear() |
|
.domain([-10, 20, 1000]) |
|
.range([0, 800, 1000]) |
|
|
|
var svg = d3.select("body") |
|
.append("svg") |
|
.attr("width", state.width) |
|
.attr("height", state.height) |
|
.style("border", "1px solid lightgray") |
|
|
|
var formShape = d3.superformula() // form |
|
.types(types) |
|
.type("asterisk") |
|
.size(state.size) |
|
.segments(state.segments) |
|
|
|
var formPath = svg.append("path") |
|
.attr("class", "sample") |
|
.attr("d", formShape); |
|
|
|
var refAttrKeyVal = function (shape) { // refs |
|
let pts = shape.points() |
|
let r = {} |
|
|
|
r.cx = pts[state.refdot][0] |
|
r.cy = pts[state.refdot][1] |
|
|
|
return r |
|
} |
|
|
|
var points = formShape.points() |
|
var refPoint = points[Math.round(points.length * (1 / 4))] |
|
var jsonCircle = |
|
{ "x": refPoint[0], "y": refPoint[1], "rad": 4, "fill" : "black" } |
|
|
|
|
|
var refElem = svg.append("circle") |
|
.datum(jsonCircle) |
|
.attr("class", "refcircle") |
|
.attr("cx", d => d.x) |
|
.attr("cy", d => d.y) |
|
.attr("r", d => d.rad) |
|
.attr("fill", d => d.fill) |
|
.attr("stroke", d => d.stroke) |
|
|
|
|
|
|
|
var circleAttrKeyVal = function (key, val) { // circle |
|
let r = {key, val} |
|
if (key === 'tx') {r.key = 'cx'; r.val = val } |
|
if (key === 'ty') {r.key = 'cy'; r.val = val } |
|
return r |
|
} |
|
|
|
var circleShape = |
|
{ "x": state.x0, "y": state.y0, "rad": state.rad, "fill" : "transparent", "stroke" : "green" } |
|
|
|
var circleElem = svg.append("circle") |
|
.datum(circleShape) |
|
.attr("class", "circle") |
|
.attr("cx", d => d.x) |
|
.attr("cy", d => d.y) |
|
.attr("r", d => d.rad) |
|
.attr("fill", d => d.fill) |
|
.attr("stroke", d => d.stroke) |
|
|
|
|
|
var legendAttrKeyVal = function (key, val) { // legend |
|
let r = '' |
|
if (key === 'm') { r = 'Arity of rotational symmetry. ' } |
|
if (key === 'n1') { r = 'Large n1 and equals n2, n3 mark polygonal shapes' } |
|
if (key === 'n2') { r = 'n2 and n3 provide axial freedom ' } |
|
if (key === 'n3') { r = 'n2 = n3 represents axial symmetry ' } |
|
if (key === 'a') { r = ' ' } |
|
if (key === 'b') { r = ' ' } |
|
return r |
|
} |
|
|
|
var legendElem = svg.append("text") |
|
.data(['mathematical beauty of natural forms']) |
|
.classed("info", true) |
|
.style("font-family", 'sans-serif') |
|
.attr("x", d => state.legendXloc) |
|
.attr("y", d => state.legendYloc) |
|
.style("font-size", d => state.legendFontSize) |
|
.text(d => d) |
|
.style("fill-opacity", 1) |
|
|
|
|
|
var noticeElem = svg.append("text") // notice |
|
.data(['']) |
|
.classed("notice", true) |
|
.style("font-family", 'sans-serif') |
|
.attr("x", d => state.noticeXloc) |
|
.attr("y", d => state.noticeYloc) |
|
.style("font-size", d => state.noticeFontSize) |
|
.text(d => d) |
|
.style("fill-opacity", 1) |
|
|
|
|
|
var rectInAttrKeyVal = function (key, val) { // extent |
|
let r = {key, val} |
|
if (key === 'tx') {r.key = 'x'; r.val = val - state.side / 2} |
|
if (key === 'ty') {r.key = 'y'; r.val = val - state.side / 2 } |
|
return r |
|
} |
|
|
|
var rectInShape = |
|
{ "x": state.x0 - state.side / 2, "y": state.y0 - state.side / 2, "width": state.side, "height": state.side, "fill" : "transparent", "stroke" : "red" } |
|
|
|
var rectInElem = svg.append("rect") |
|
.datum(rectInShape) |
|
.attr("class", "extent") |
|
.attr("x", d => d.x) |
|
.attr("y", d => d.y ) |
|
.attr("width", d => d.width) |
|
.attr("height", d => d.height) |
|
.attr("fill", d => d.fill) |
|
.attr("stroke", d => d.stroke) |
|
|
|
|
|
var control = d3.select("#controls") // control |
|
.selectAll("div") |
|
.data(d3.entries(types.asterisk)) |
|
.enter().append("div") |
|
.attr("id", function(d) { return d.key; }); |
|
|
|
control.append("label") |
|
.text(function(d) { return d.key; }); |
|
|
|
control.append("input") |
|
.attr("type", "range") |
|
.attr("max", 1000) |
|
.attr("min", 0) |
|
.property("value", function(d) { return scale(d.value); }) |
|
.on("change", changed) |
|
.on("input", changed); |
|
|
|
control.append("span") |
|
.text(function(d) { return format(d.value); }); |
|
|
|
d3.select("#controls") |
|
.append("div") |
|
.selectAll("button") |
|
.data(d3.entries(types)) |
|
.enter().append("button") |
|
.text(function(d) { return d.key; }) |
|
.on("click", function(d) { |
|
for (var param in d.value) { |
|
var control = d3.select("#" + param); |
|
control.select("input").property("value", scale(d.value[param])); |
|
control.select("span").text(format(d.value[param])); |
|
formShape.param(param, d.value[param]); |
|
} |
|
formPath.attr("d", formShape); |
|
refElem.attr("cx", refAttrKeyVal(formShape).cx) |
|
refElem.attr("cy", refAttrKeyVal(formShape).cy) |
|
}); |
|
|
|
|
|
|
|
function changed(d) { // board |
|
var v = scale.invert(this.value); |
|
formShape.formParam(d.key, v) |
|
|
|
formPath.attr("d", formShape) |
|
circleElem.attr(circleAttrKeyVal(d.key, v).key, circleAttrKeyVal(d.key, v).val) |
|
|
|
rectInElem.attr(rectInAttrKeyVal(d.key, v).key, rectInAttrKeyVal(d.key, v).val) |
|
|
|
legendElem.text(legendAttrKeyVal(d.key, v)) |
|
noticeElem.text(JSON.stringify(formShape.defparams(), state.replacer) ) |
|
|
|
refElem.attr("cx", refAttrKeyVal(formShape).cx) |
|
refElem.attr("cy", refAttrKeyVal(formShape).cy) |
|
|
|
|
|
d3.select(this.nextSibling).text(format(v)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|