How to make bitmap editor (plot a family tree)

… to show as dots ancestors found in family tree, that is a 2D rendering of the entire tree using a dot for each ancestor. Black dots on white canvas.

1 Like

Do you have a real family tree that you want to visualize or do you
want just the image of a tree?

If you have a family tree, in which form is the data?

I want to enter each dot manually on a grid - with scrolling and printing features…/Lennart

1 Like

Do you want to save on hard drive or just have it once and when you close the Sketch the data is lost?

here is an example to get you started (processing, not p5.js)


// input family tree

// point data in a list
ArrayList<PVector> listPVectors = new ArrayList();
boolean showRect=true;

// -------------------------------------------------------------------------------

void setup() {
  size(1900, 1000);
  rectMode(CENTER);
}//func

void draw() {
  background(255); // white

  // show help text
  fill(0); // black
  text("Click mouse to set an ancestor.\nBackspace to remove last. Space Bar - dump. Return - toggle view (rects or only points).", 17, 17);

  // show list
  for (PVector pv : listPVectors) {
    stroke(0); // black
    noFill();  // no filling

    // rect and/or point
    if (showRect)
      rect(pv.x, pv.y, 4, 4);
    point(pv.x, pv.y);
  }//for
}//func

// ------------------------------------------------------------------------
// Inputs

void mousePressed() {
  // store point
  listPVectors.add (new PVector(mouseX, mouseY));
}//func

void keyPressed() {
  if (key==' ') {
    // space bar
    // dump
    println("--- " + listPVectors.size() + " entries. --- ");
    for (PVector pv : listPVectors) {
      println(pv.x+","+pv.y);
    }//for
  } else if (key==BACKSPACE) {
    // delete last
    if (listPVectors.size()>0) {
      listPVectors.remove(listPVectors.size()-1);
    }
  } else if (key==ESC) {
    // kill ESC
    key=0; // kill ESC
  } else if (key==RETURN||key==ENTER) {
    // toggle
    showRect=
      ! showRect; //
  }
}//func
//

1 Like

A second variation uses a Rect class and toggles fill color of selected rectangle, shouldn’t be too difficult to port to p5.js:

Rect[] r;

final int _numCols = 70;
final int _numRows = 60;

final int _wndW = 700;
final int _wndH = 600;

class Rect {
 int x,y,w,h;
 boolean isSelected = false;
 
 // Constructor
 Rect(int xpos, int ypos, int wide, int ht) {
   x = xpos;
   y = ypos;
   w = wide;
   h = ht;
 }
  
 void display() {
  rect(x,y,w,h);
 }
}

 void rectGrid(int left, int top, int w, int h, int vg, int hg) {
   int id = 0;
  for(int k = 0; k < _numRows; k++) {
   for(int j = 0; j < _numCols; j++){
     int x = left + j*(w+vg);
     int y = top + k*(h+hg);
     r[id] = new Rect(x,y,w,h);
     id++;
   }
  }
 }
 
void setup() {
 size(_wndW,_wndH);
 r = new Rect[_numRows*_numCols];
 rectGrid(0, 0, 10, 10, 0, 0);
}

void draw() {
 for (int i = 0; i < r.length; i++) {
   if(r[i].isSelected){
     fill(0);
   } else {
     fill(255);
   }
   r[i].display(); // Display each object  
 }
}

void mousePressed(){
 for (int i = 0; i < r.length; i++) {
  if ((mouseX >= r[i].x) && (mouseX <= r[i].x +r[i].w) && (mouseY >= r[i].y) && (mouseY <= r[i].y + r[i].h)) {
    println("id =",i);
    if (r[i].isSelected){
      r[i].isSelected = false;
    } else {
      r[i].isSelected = true;
    }
  }
 }
}
2 Likes

not clear whether you want to have a bitmap editor or a family tree…

2 Likes

@lennartd welcome to the forum, if you want members to provide relevant answers then your question should be made very clear.

What you have provided represents a minimum information problem . I certainly can’t see how the title ‘How to make Bitmap editor’ is relevant to family trees. You need to provide must more detail. :thinking:

2 Likes

I want to see which ancestors I have identified and which ones are still missing in a 2D format. By transferring information person-by-person from my tree (which is in pages format). Thus building “strings” of black dots on a white background - one “string” for each branch. It could be a manual procedure or smart by searching he document at hand. The ancestors are named using Kekule´s system: generation (level) 1 denoted by “2” and “3” where “2” is father and “3” is mother - “4” is father´s father, “5” is father´s mother, “6” is mother´s father, “7” is mother´s mother, etcetera back through the generations. The 10th generation will have 2^10 = 1024 possible dots. So I want one dot for each of these numbers, built up level by level from the bottom up. The size of the dots must be customizable for readability. I have an A3 paper size printer which I will use for output.
Lennart

1 Like

I can find no reference to this system :frowning_face: the closest I found is the Ahnentafel Numbering System.

Consider your parents, they may well have a common ancestor so not all 1024 dots have to represent a unique person. How many generations do you want to go back? You will soon reach the legible limit of the printer resolution.

1 Like

I just explained what Kekule´s system means. So you do not need to look it up. If this is unclear we leave the subject.
The resolution of the printer is a non-issue here. We start with a screen solution.
Lennart

Do you mean a structure like this:

Clipboard02sss

can you post the data you have?

Because then we can have a look as how to load and parse the data to a bit map.

Thank you!

Chrisir

I looked it up because I wanted more information about this system so I could help, it was not meant as a criticism.

seems to be the same as Sosa-Stradonitz-System / Sosa-Number

3 Likes

Thanks @Chrisir for the information - most interesting.

Each of my family trees start with a summary of the ancestors as is shown below. Otherwise the whole document has to be searched which is probably overkill. Thus different numbering from different trees are to be converged into one chart of dots - one tree at a time. These trees (“Ahnentafel”) are updated when a new generation is discovered.

Hello @lennartd,

I was able to code this in steps:

  • nested for() loops for nodes
  • drawing lines between nodes (can be skipped)
  • counter in the for() loop to number the nodes
  • extracting data (numbers) from your summary (picture)
  • if() else() to test for number to color the nodes as required

Can you provide a simple text file of the data on that page?
You may be able to export this.

A text file can be loaded and parsed to extract data:

I can provide guidance and hints but will not provide code.

The coding took some time and effort but was was well worth the reward.
Keep it simple and work through it in steps.
Save a working copy with comments along the way (number these) and keep adding to it.

Reference:
6.6: Nested Loops - Processing Tutorial

:)

1 Like

Maybe you can export from Pages to csv or tsv
format.

Then we have format we can parse from Processing.

  • Eg. number,name,generation,date of birth,date of death

  • So, values separated by comma or tabs

and a list of those

1 Like

The demo by @glv is good and also highlights the problem of resolution that I raised earlier and was dismissed as unimportant. In your data you have 12 generations and if all ancestors were present that is 4096 ancestors at generation 12. So using 1 pixel for the black dot and 1 white pixel to visually separate them then the image must be at least 8192 pixels wide.

This code displays the data you provided one dot per ancestor but fails at higher generations as the dots merge…

let ancestors = [1, 2, 3, 4, 5, 8, 9, 16, 17, 34, 35, 70, 71, 140, 141, 280, 281, 560, 561, 1120, 1121, 2240, 2241];

function setup() {
    console.clear(); console.log('GLOBAL mode');
    let p5canvas = createCanvas(1360, 300);
    chart = createAncestorImage(1280, 200, 20, 10, ancestors);
}

function draw() {
    background(128);
    image(chart, 0, 0);
}

function createAncestorImage(chartWidth, chartHeight, border, ygap, ans) {
    function level(an) { return 1 + Math.floor(Math.log2(an)); }
    function levelSize(lvl) { return 2 ** (lvl - 1); }
    function levelMidRange(lvl) { return (2 ** (lvl - 1) + 2 ** lvl - 1) / 2 };
    let pg = createGraphics(chartWidth + 2 * border, chartHeight + 2 * border);
    pg.background(255);
    pg.fill(0);
    ans.forEach(a => {
        let lvl = level(a), lvlSize = levelSize(lvl);
        let lvlMidRange = levelMidRange(lvl);
        let xgap = chartWidth / (lvlSize), dy = border + lvl * ygap;
        let px = border + (chartWidth / 2) + (a - lvlMidRange) * xgap;
        let py = border + lvl * ygap;
        pg.ellipse(px, py, 1, 1);
    });
    return pg;
}
2 Likes

The chart looks great but can be simplified i the following fashion:
(1) No text - just dots. - But see (3) below - then the lines could be skipped.
(2) A screen scrolling feature would solve the scaling problem. And then skip printing.
(3) To complicate things: Would be great (but not necessary) to be able to move the mouse over a dot and expand with some relevant text - perhaps even extracted from the text source file.
(4) I will provide an actual text sample tomorrow.

1 Like