290 lines
10 KiB
JavaScript
290 lines
10 KiB
JavaScript
var currently_dragging = null; // if this is null, we have nothing being dragged. if it's not null, then it will represent the glyph object (as goes in the glyphs array) which is currently being dragged
|
|
var major_radius;
|
|
const glyphs = [];
|
|
const vowels = [];
|
|
const consonants = [];
|
|
|
|
var svg;
|
|
var connections_container;
|
|
|
|
$(document).ready(function() {
|
|
svg = $('svg#arena')[0];
|
|
connections_container = $('svg#arena #connections');
|
|
major_radius = parseFloat($('circle').attr('r'));
|
|
|
|
$(document).on("mouseup", on_mouse_up);
|
|
|
|
$('td > svg').on("mousedown", function(event) {
|
|
let receptacle = $("#consonant_put_it_in_here");
|
|
let selected = $(this);
|
|
receptacle.html(selected.html());
|
|
|
|
const mouse_position = svg_from_dom({x:event.clientX, y:event.clientY}, svg);
|
|
|
|
var g = make_new_consonant();
|
|
set_location(g, mouse_position);
|
|
|
|
currently_dragging = g;
|
|
})
|
|
$('.vowel-example > svg').on("mousedown", function(event) {
|
|
let receptacle = $('#vowel_put_it_in_here');
|
|
let selected = $(this);
|
|
receptacle.html(selected.html());
|
|
|
|
var g = make_new_vowel();
|
|
|
|
const mouse_position = svg_from_dom({x:event.clientX, y:event.clientY}, svg);
|
|
set_location(g, mouse_position);
|
|
|
|
currently_dragging = g;
|
|
});
|
|
|
|
$('td > svg').each(function() { this.setAttribute('viewBox',"0 30 40 100");});
|
|
populate_consonant_grid();
|
|
|
|
populate_vowels();
|
|
});
|
|
|
|
function populate_consonant_grid() {
|
|
// this function expects that there's a #consonant-grid table, where each row corresponds to a glyph set
|
|
// the cells of the row need to be filled with the various possible configurations of that glyph
|
|
// so like, the <th> in a given row should contain a .megaglyph svg group, which contains the base glyph plus all possible radicals.
|
|
// then we use the split_megaglyph function to generate the five possible configurations out of that megaglyph
|
|
// and insert the five configured glyphs into their corresponding (td) cells of the table
|
|
|
|
$("#consonant-grid tbody tr").each(function (asdf, row) { // iterate over the rows in the table
|
|
|
|
// row will now be something like <tr><th/><td/><td/><td/><td/><td/><th/></tr>
|
|
// that is, a tr containing one th on either end plus a td for each consonant in this row
|
|
var head = $(row).find("th:first-child svg"); // this is the header for the row, which should contain the .megaglyph object
|
|
var megaglyph = head.find(".megaglyph");
|
|
var glyphs = split_megaglyph(megaglyph); // now this is a list of the configured consonant symbols
|
|
|
|
$(row).find("td").each(function(idx, cell) { // iterate over the cells in the row
|
|
// cell will now be a <td> cell which contains the IPA for that cell's consonants, plus the <svg> canvas into which to insert the configured consonant symbol
|
|
|
|
$(cell).find("svg").append(glyphs[idx]);
|
|
});
|
|
});
|
|
}
|
|
|
|
function split_megaglyph(megaglyph) {
|
|
// megaglyph is an svg object which contains all the possible radicals at once.
|
|
// this should return a list of new glyphs, cloned frmo megaglyph, each of which contains only the radicals it wants.
|
|
var one = megaglyph.clone();
|
|
one.find('.line_1,.line_2,.line_3,.dot_2,.dot_3').remove(); // only .dot_1
|
|
remove_class(one, "megaglyph");
|
|
var two = megaglyph.clone();
|
|
two.find('.line_2,.line_3,.dot_1,.dot_2,.dot_3').remove(); // line 1 only
|
|
remove_class(two, "megaglyph");
|
|
var three = megaglyph.clone();
|
|
three.find('.line_1,.line_2,.line_3,.dot_1').remove(); // only dots 2 and 3
|
|
remove_class(three, "megaglyph");
|
|
var four = megaglyph.clone();
|
|
four.find('.line_1,.dot_1,.dot_2,.dot_3').remove(); //lines 2 and 3
|
|
remove_class(four, "megaglyph");
|
|
var five = megaglyph.clone();
|
|
five.find('.line_1,.line_2,.line_3').remove(); // all three dots
|
|
remove_class(five, "megaglyph");
|
|
|
|
return [one, two, three, four, five];
|
|
}
|
|
|
|
function populate_vowels() {
|
|
megaglyph = $(".vowel.megaglyph");
|
|
$(".vowel-example svg").each((i, ex) => {
|
|
let glyphs = $(ex).text().replace(/(\s|\n)+/g,",").split(",").filter((a) => a!="")
|
|
$(ex).text("");
|
|
|
|
$.each(glyphs, (i, e) => {
|
|
let wanted_class = "." + e;
|
|
let glyph_original = megaglyph.find(wanted_class);
|
|
let new_glyph = glyph_original.clone();
|
|
$(ex).append(new_glyph);
|
|
});
|
|
});
|
|
}
|
|
|
|
function make_new_consonant(source) {
|
|
// create a new glyph object by copying the elements pointed to by source
|
|
// this will add it to the svg and to the glyphs list
|
|
var x = {};
|
|
x.angle = 0;
|
|
x.element = $("#consonant_copy_from_here").clone().attr("id","").attr("onclick", "").attr("onmousedown", "");
|
|
x.element.find("#consonant_put_it_in_here").attr("id","");
|
|
x.position = {x:0, y:0};
|
|
x.element.on("mousedown", x, (event) => { currently_dragging = event.data });
|
|
$(svg).append(x.element);
|
|
|
|
x.handles = [];
|
|
x.handles.push($(ns_elem("circle", svg_ns)))
|
|
x.handles.push($(ns_elem("circle", svg_ns)))
|
|
$.each(x.handles, function() {
|
|
$(svg).append(this)
|
|
this.attr("r", 2);
|
|
add_class(this, "handle");
|
|
});
|
|
update_handles(x);
|
|
glyphs.push(x);
|
|
x.vowel = false;
|
|
return x;
|
|
}
|
|
|
|
function make_new_vowel(source) {
|
|
// create a new glyph object by copying the elements pointed to by source
|
|
// this will add it to the svg and to the glyphs list
|
|
var x = {};
|
|
x.angle = 0;
|
|
x.element = $("#vowel_copy_from_here").clone().attr("id","").attr("onclick", "").attr("onmousedown", "");
|
|
x.element.find("#vowel_put_it_in_here").attr("id","");
|
|
x.position = {x:0, y:0};
|
|
x.element.on("mousedown", x, (event) => { currently_dragging = event.data });
|
|
$(svg).append(x.element);
|
|
|
|
x.handles = [];
|
|
x.handles.push($(ns_elem("circle", svg_ns)));
|
|
x.handles.push($(ns_elem("circle", svg_ns)));
|
|
$.each(x.handles, function() {
|
|
$(svg).append(this)
|
|
this.attr("r", 2);
|
|
add_class(this, "handle");
|
|
});
|
|
update_handles(x);
|
|
glyphs.push(x);
|
|
x.vowel = true;
|
|
return x;
|
|
}
|
|
|
|
function on_mouse_up(event) {
|
|
// this is called on the whole document when the mouse is released on it. event.data is meaningless. this should handle releasing the current dragged element, if it exists.
|
|
if (currently_dragging != null && is_in_delete_region(currently_dragging.position)) {
|
|
remove(glyphs, currently_dragging);
|
|
$.each(currently_dragging.handles, function() { $(this).remove(); });
|
|
currently_dragging.element.remove();
|
|
currently_dragging = null;
|
|
connect_vowels();
|
|
}
|
|
|
|
if (currently_dragging != null) {
|
|
set_location(currently_dragging, mul(norm(currently_dragging.position), major_radius));
|
|
}
|
|
|
|
currently_dragging = null;
|
|
}
|
|
|
|
function is_in_delete_region(p) {
|
|
r = $("#delete");
|
|
first = {x:parseFloat(r.attr("x")), y:parseFloat(r.attr("y"))};
|
|
second = {x:parseFloat(r.attr("width"))+first.x, y:parseFloat(r.attr("height"))+first.y};
|
|
|
|
if (p.x > first.x && p.x < second.x && p.y > first.y && p.y < second.y) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
$(document).mousemove(function(event) {
|
|
const mouse_position = svg_from_dom({x:event.clientX, y:event.clientY}, svg);
|
|
const mouse_move = sub(mouse_position, previous_mouse_position);
|
|
|
|
previous_mouse_position = mouse_position;
|
|
var s_position = mouse_position;
|
|
// get the position inside the svg tag for the mouse
|
|
|
|
if (currently_dragging != null) {
|
|
set_location(currently_dragging, add(currently_dragging.position, mouse_move));
|
|
update_handles(currently_dragging);
|
|
}
|
|
});
|
|
|
|
function update_handles(g) {
|
|
if (g.element.find('path.glyph,path.vowel-base').length != 0) {
|
|
set_loc(g.handles[0], get_global_point_at_length(svg, g.element.find('path.glyph,path.vowel-base')[0], 0));
|
|
set_loc(g.handles[1], get_global_point_at_length(svg, g.element.find('path.glyph,path.vowel-base')[0], 1000));
|
|
}
|
|
connect_vowels();
|
|
}
|
|
|
|
function set_location(glyph, point) {
|
|
glyph.position = point;
|
|
glyph.angle = Math.atan2(point.y, point.x);
|
|
set_transform(glyph.element, glyph.position, appropriate_angle(point));
|
|
update_handles(glyph);
|
|
}
|
|
|
|
function connect_vowels() {
|
|
let vowels = $.grep(glyphs, (v) => v.vowel);
|
|
vowels.sort((a, b) => b.angle - a.angle);
|
|
console.log($.map(vowels, (a)=>a.angle));
|
|
|
|
|
|
// clear oout the old connections
|
|
|
|
connections_container.html("");
|
|
|
|
if (vowels.length == 0) {
|
|
// if there's no vowels, we should have just a bare circle
|
|
/*} else if (vowels.length == 1) {
|
|
// make a fake point on the other side to connect
|
|
let connection = ns_elem("path", svg_ns);
|
|
add_class(connection, "intervowel");
|
|
|
|
let path = `M ${start_x} ${start_y} `;
|
|
path += `A ${}`
|
|
|
|
$(connection).attr("d", `M ${start_x} ${start_y} A ${major_radius} ${major_radius} 0 ${sweep} ${large} ${end_x} ${end_y}`);
|
|
|
|
connections_container.append(connection);*/
|
|
} else {
|
|
// otherwise iterate over them and connect one to the next
|
|
for (let i=0; i < vowels.length; i++) {
|
|
//connections_container.append($(ns_elem("circle", svg_ns)).attr("r",1).attr("cx",Math.cos(vowels[i].angle)*major_radius).attr("cy",major_radius*Math.sin(vowels[i].angle)));
|
|
let connection = ns_elem("path", svg_ns);
|
|
add_class(connection, "intervowel");
|
|
|
|
let first = vowels[i];
|
|
let second = vowels[(i+1)%vowels.length];
|
|
|
|
let start_x = parseFloat(first.handles[1].attr("cx"));
|
|
let start_y = parseFloat(first.handles[1].attr("cy"));
|
|
|
|
/* abandoned quadratic approach
|
|
let roundness = 0.2*Math.abs(first.angle - second.angle);
|
|
roundness = first.angle > second.angle ? 1 : 0;
|
|
|
|
let s_dx = start_x + (Math.sin(first.angle) * major_radius * roundness);
|
|
let s_dy = start_y - (Math.cos(first.angle) * major_radius * roundness);
|
|
*/
|
|
let end_x = parseFloat(second.handles[0].attr("cx"));
|
|
let end_y = parseFloat(second.handles[0].attr("cy"));
|
|
/*
|
|
let e_dx = end_x - (Math.sin(second.angle) * major_radius * roundness);
|
|
let e_dy = end_y + (Math.cos(second.angle) * major_radius * roundness);
|
|
|
|
// $(connection).attr("d", `M ${start_x} ${start_y} C ${s_dx} ${s_dy} ${e_dx} ${e_dy} ${end_x} ${end_y}`);
|
|
*/
|
|
|
|
let distance = -(second.angle - first.angle);
|
|
if (vowels.length == 1) {
|
|
distance = 1.5*Math.PI;
|
|
}
|
|
let midpoint;
|
|
let sweep = "0";
|
|
let large = "0";
|
|
if (distance < 0) {
|
|
distance += 2*Math.PI;
|
|
}
|
|
if (distance < Math.PI) {
|
|
sweep = "0"
|
|
large = "0"
|
|
} else if (distance < 2*Math.PI) {
|
|
sweep = "1"
|
|
large = "0"
|
|
}
|
|
|
|
$(connection).attr("d", `M ${start_x} ${start_y} A ${major_radius} ${major_radius} 0 ${sweep} ${large} ${end_x} ${end_y}`);
|
|
|
|
connections_container.append(connection);
|
|
}
|
|
}
|
|
} |