Last active
March 12, 2019 02:21
-
-
Save kjlubick/d25768f65c5f800a5a54dd16366021d9 to your computer and use it in GitHub Desktop.
Family Tree Map
This file contains hidden or 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
canvas.width = 3000; | |
canvas.height = 2200; | |
canvas.style.width = "900px"; | |
const surface = CanvasKit.MakeCanvasSurface(canvas); | |
if (!surface) { | |
throw 'Could not make surface'; | |
} | |
const skcanvas = surface.getCanvas(); | |
const paint = new CanvasKit.SkPaint(); | |
const font = new CanvasKit.SkFont(null, 20); | |
// FIXME: this is for zooming in and seeing the result better | |
//skcanvas.translate(-900, -800); | |
skcanvas.drawText("hello", 40, 40, paint, font); | |
paint.setStrokeWidth(2); | |
paint.setStyle(CanvasKit.PaintStyle.Stroke); | |
const OUTER_RADIUS = 1500; | |
const INNER_RADIUS = 150; | |
const MARGIN = 10; | |
const UNDER_ANGLE = 38; | |
// Midpoint line, just for reference | |
let path = new CanvasKit.SkPath(); | |
path.moveTo(OUTER_RADIUS + MARGIN, MARGIN); | |
path.lineTo(OUTER_RADIUS + MARGIN, OUTER_RADIUS + MARGIN - INNER_RADIUS); | |
skcanvas.drawPath(path, paint); | |
path.delete(); | |
/* | |
drawRing(OUTER_RADIUS, skcanvas, paint); | |
drawRing(INNER_RADIUS + 1000, skcanvas, paint); | |
drawRing(INNER_RADIUS + 700, skcanvas, paint); | |
//drawRing(INNER_RADIUS + 475, skcanvas, paint); | |
drawRing(INNER_RADIUS + 300, skcanvas, paint); | |
drawRing(INNER_RADIUS + 150, skcanvas, paint); | |
drawRing(INNER_RADIUS, skcanvas, paint); | |
// Grandparent gen 3 | |
for (let i = 1; i < 4; i++) { | |
drawRay(-UNDER_ANGLE + i * (UNDER_ANGLE + 90) / 2, INNER_RADIUS + 150, skcanvas, paint); | |
} | |
// Great grandparent gen 4 | |
for (let i = 1; i < 8; i++) { | |
// drawRay(-UNDER_ANGLE + i * (UNDER_ANGLE + 90) / 4, INNER_RADIUS + 300, skcanvas, paint); | |
} | |
// 2xGreat grandparent gen 5 | |
for (let i = 1; i < 16; i++) { | |
drawRay(-UNDER_ANGLE + i * (UNDER_ANGLE + 90) / 8, INNER_RADIUS + 475, skcanvas, paint); | |
} | |
// 3xGreat gen 6 | |
for (let i = 1; i < 32; i++) { | |
drawRay(-UNDER_ANGLE + i * (UNDER_ANGLE + 90) / 16, INNER_RADIUS + 700, skcanvas, paint); | |
} | |
// 4xGreat gen 7 | |
for (let i = 1; i < 64; i++) { | |
drawRay(-UNDER_ANGLE + i * (UNDER_ANGLE + 90) / 32, INNER_RADIUS + 1000, skcanvas, paint); | |
} | |
*/ | |
// TODO try drawing a branch at the grandparents level | |
let person = { | |
given_names: ['Joseph', 'Jordan', 'Fredrich'], | |
given_names_order: [1, 2, 0], | |
surnames: ['Henderson'], | |
birth_date: Date.UTC(1866, 3, 28), | |
birth_location: ['Trieste, Austria', 'Austria', 'AUT'], | |
death_date: Date.UTC(1904, 11, 30), | |
death_location: ['Chicago, Illinois', 'Chicago', 'IL'], | |
}; | |
const ANGLE_4TH_LEVEL = 32; | |
rotateToCenter(skcanvas, 16 - 4*32, INNER_RADIUS + 475); | |
//draw4thGen(person); | |
skcanvas.restore(); | |
rotateToCenter(skcanvas, 16 - 3*32, INNER_RADIUS + 475); | |
//draw4thGen(person); | |
skcanvas.restore(); | |
rotateToCenter(skcanvas, 16 - 2*32, INNER_RADIUS + 475); | |
//draw4thGen(person); | |
skcanvas.restore(); | |
rotateToCenter(skcanvas, 16 - 1*32, INNER_RADIUS + 475); | |
draw4thGen(person); | |
skcanvas.restore(); | |
surface.flush(); | |
function drawRing(r, canvas, paint) { | |
const cx = OUTER_RADIUS + MARGIN; | |
const cy = OUTER_RADIUS + MARGIN; | |
const arc = new CanvasKit.SkPath(); | |
arc.moveTo(cx + INNER_RADIUS * Math.cos(toRad(UNDER_ANGLE)), | |
cy + INNER_RADIUS * Math.sin(toRad(UNDER_ANGLE))); | |
arc.arcTo(CanvasKit.LTRBRect(cx-r, cy-r, cx+r, cy+r), UNDER_ANGLE, | |
-180 - 2 * UNDER_ANGLE, false); | |
arc.lineTo(cx - INNER_RADIUS * Math.cos(toRad(UNDER_ANGLE)), | |
cy + INNER_RADIUS * Math.sin(toRad(UNDER_ANGLE))); | |
arc.close(); | |
canvas.drawPath(arc, paint); | |
arc.delete(); | |
} | |
function drawRay(angle, innerR, canvas, paint) { | |
const ray = new CanvasKit.SkPath(); | |
const cx = OUTER_RADIUS + MARGIN; | |
const cy = OUTER_RADIUS + MARGIN; | |
ray.moveTo(cx - innerR * Math.cos(toRad(angle)), | |
cy - innerR * Math.sin(toRad(angle))); | |
ray.lineTo(cx - OUTER_RADIUS * Math.cos(toRad(angle)), | |
cy - OUTER_RADIUS * Math.sin(toRad(angle))); | |
canvas.drawPath(ray, paint); | |
ray.delete(); | |
} | |
function toRad(degrees) { | |
return degrees * (Math.PI / 180); | |
} | |
function rotateToCenter(canvas, angleOfCenter, outerRadius) { | |
canvas.save(); | |
const cx = OUTER_RADIUS + MARGIN; | |
const cy = OUTER_RADIUS + MARGIN; | |
canvas.translate(cx, cy); | |
canvas.rotate(angleOfCenter, 0, 0); | |
canvas.translate(0, -outerRadius); | |
} | |
function fitFirstName(person, font, maxWidth) { | |
const str = "J Jordan Freidrich"; | |
return [str, font.measureText(str), 30]; | |
} | |
function fitSurname(person, font, maxWidth) { | |
const str = "Henderson"; | |
return [str, font.measureText(str), 32]; | |
} | |
function fitLifetime(person, font, maxWidth) { | |
const str = "Apr 1866 - Dec 1904"; | |
return [str, font.measureText(str), 24]; | |
} | |
function fitLocation(person, font, maxWidth) { | |
const str = "Austria Chicago"; | |
return [str, font.measureText(str), 24]; | |
} | |
function fillArcSegment(widthDegrees, outerRadius, innerRadius, fillColor) { | |
// TODO this is a mess | |
const cx = outerRadius + MARGIN; | |
const cy = outerRadius + MARGIN; | |
const arc = new CanvasKit.SkPath(); | |
const fillPaint = new CanvasKit.SkPaint(); | |
// bottom left corner | |
arc.moveTo(cx + innerRadius * Math.cos(toRad(-widthDegrees/2 - 90)), | |
cy + innerRadius * Math.sin(toRad(-widthDegrees/2 - 90))); | |
arc.lineTo(cx + outerRadius * Math.cos(toRad(-widthDegrees/2 - 90)), | |
cy + outerRadius * Math.sin(toRad(-widthDegrees/2 - 90))); | |
//arc.arcTo(CanvasKit.LTRBRect(cx-outerRadius, cy-outerRadius, | |
// cx+outerRadius, cy+outerRadius), | |
// 0, widthDegrees, false); | |
arc.lineTo(cx + outerRadius * Math.cos(toRad(widthDegrees/2 - 90)), | |
cy + outerRadius * Math.sin(toRad(widthDegrees/2 - 90))); | |
arc.lineTo(cx + innerRadius * Math.cos(toRad(widthDegrees/2 - 90)), | |
cy + innerRadius * Math.sin(toRad(widthDegrees/2 - 90))); | |
arc.close(); | |
skcanvas.drawPath(arc, paint); | |
arc.delete(); | |
fillPaint.delete(); | |
} | |
function draw4thGen(person) { | |
// draw background | |
const testPath = new CanvasKit.SkPath(); | |
testPath.moveTo(10, 10); | |
testPath.lineTo(40, 10); | |
skcanvas.drawPath(testPath, paint); | |
fillArcSegment(32, INNER_RADIUS + 300, INNER_RADIUS+150, CanvasKit.BLACK); | |
// draw text | |
const textPaint = new CanvasKit.SkPaint(); | |
textPaint.setStyle(CanvasKit.PaintStyle.Fill); | |
const namesFont = new CanvasKit.SkFont(null, 32); | |
const bottomFont = new CanvasKit.SkFont(null, 24); | |
const [gn, width1, size1] = fitFirstName(person, namesFont, "??"); | |
const [sn, width2, size2] = fitSurname(person, namesFont, "??"); | |
const [lt, width3, size3] = fitLifetime(person, bottomFont, "??"); | |
const [loc, width4, size4] = fitLocation(person, bottomFont, "??"); | |
namesFont.setSize(size1); | |
skcanvas.drawText(gn, -width1/2, 35, textPaint, namesFont); | |
namesFont.setSize(size2); | |
skcanvas.drawText(sn, -width2/2, 70, textPaint, namesFont); | |
bottomFont.setSize(size3); | |
skcanvas.drawText(lt, -width3/2, 100, textPaint, bottomFont); | |
bottomFont.setSize(size4); | |
skcanvas.drawText(loc, -width4/2, 125, textPaint, bottomFont); | |
textPaint.delete(); | |
namesFont.delete(); | |
bottomFont.delete(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment