function init(data){
let lafe = {}
lafe.data = JSON.parse(JSON.stringify(data))
lafe.margin = {top: 10, right: 10, bottom: 10, left: 10}
lafe.width = lafe.data.layout.width - lafe.margin.left - lafe.margin.right
lafe.height = lafe.data.layout.height - lafe.margin.top - lafe.margin.bottom
lafe.zoom = false
lafe.px = 1.0/lafe.width
lafe.py = 1.0/lafe.height
lafe.options = [
{ "code":"dShw",
"type":"checkbox",
"value":true,
"title":"Show intermediate demands",
"x":0.0,"y":0.0,"dx":0.0,"dy":0.0},
{ "code":"lclick",
"type":"label",
"title":"Left-click on a node to highlight genealogy",
"x":0.0,"y":0.0,"dx":0.0,"dy":0.0},
{ "code":"pGrp",
"type":"checkbox",
"value":true,
"title":"Group by product",
"x":0.0,"y":0.0,"dx":0.0,"dy":0.0},
{ "code":"dArg",
"type":"checkbox",
"value":true,
"title":"Auto-arrange demands",
"x":0.0,"y":0.0,"dx":0.0,"dy":0.0},
{ "code":"rclick",
"type":"label",
"title":"Right-click on a node to toggle zoom",
"x":0.0,"y":0.0,"dx":0.0,"dy":0.0},
{ "code":"pNrm",
"type":"checkbox",
"value":true,
"title":"Normalize by product",
"x":0.0,"y":0.0,"dx":0.0,"dy":0.0}
]
lafe.oMap = getMap(lafe.options)
//Keeping only consistent data and storing them
//into arrays (to keep original order) and maps (for convenient access)
lafe.demands = lafe.data.demands
.filter(d=>lafe.data.lot2demand.filter(l2d=>l2d.odemand==d.code).length>0)
lafe.dMap = getMap(lafe.demands)
lafe.resources = lafe.data.resources
.filter(r=>lafe.data.lots.filter(l=>l.resource==r.code).length>0)
lafe.rMap = getMap(lafe.resources)
lafe.stages = lafe.data.stages
.filter(s=>lafe.data.products.filter(p=>p.stage==s.code).length>0)
lafe.sMap = getMap(lafe.stages)
lafe.products = lafe.data.products
.filter(p=>lafe.sMap.has(p.stage))
.filter(p=>lafe.data.lots.filter(l=>l.product==p.code).length>0)
lafe.pMap = getMap(lafe.products)
lafe.lots = lafe.data.lots
.filter(l=>lafe.pMap.has(l.product)&&lafe.rMap.has(l.resource))
lafe.lMap = getMap(lafe.lots)
lafe.l2l = lafe.data.lot2lot
.filter(l2l=>lafe.lMap.has(l2l.ilot)&&lafe.lMap.has(l2l.olot))
lafe.l2d = lafe.data.lot2demand
.filter(l2d=>lafe.lMap.has(l2d.ilot)&&lafe.dMap.has(l2d.odemand))
//building relationships between objects and adding required properties
lafe.demands.forEach(d=>{
d.il2dMap = d3.map() //input l2d links
d.size = 0
})
lafe.stages.forEach(s=>{
s.bdepth=0; //backward depth of the stage
s.lMap = d3.map() //lots of the stage
s.pMap = d3.map() //products of the stage
s.pCount = //total number of products of the stage
s.lCount = //total number of lots of the stage
s.lSizeMax = 0.0 //maximum size of all lots of the stage
s.x = 0.0 //relative x coordinate of the stage
s.y = 0.0 //relative y coordinate of the stage
s.dx = 0.0 //relative width of the stage
s.dy = 0.0 //relative height of the stage
});
lafe.products.forEach(p=>{
p.stage = lafe.sMap.get(p.stage) //stage the product belongs to
p.lMap = d3.map() //lots of the product
p.lCount = //total number of lots of the product
p.lSizeMax = 0.0 //maximum size of all lots of the product
p.x = 0.0 //relative x coordinate of the product
p.y = 0.0 //relative y coordinate of the product
p.dx = 0.0 //relative width of the product
p.dy = 0.0 //relative height of the product
p.visible = true //visible state of the product
})
lafe.lots.forEach(l=>{
l.resource = lafe.rMap.get(l.resource); //resource the lot belongs to
l.product = lafe.pMap.get(l.product); //product the lot belongs to
l.date = new Date(l.date) //make date object from date string
l.il2lMap = d3.map(); //input l2l links
l.ol2lMap = d3.map(); //output l2l links
l.ol2dMap = d3.map(); //output l2d links
l.il2lSizes = d3.map() //total size of input l2l links per input product
l.ol2lSize = 0.0 //total size of output l2l links
l.ol2dSize = 0.0 //total size of output l2d links
l.x = 0.0 //relative x coordinate of the lot
l.y = 0.0 //relative y coordinate of the lot
l.dx = 0.0 //relative width of the lot
l.dy = 0.0 //relative height of the lot
l.selected = false //selected state of the lot
l.visible = true //visible state of the lot
//filling parent properties
l.product.lMap.set(l.code,l)
l.product.stage.lMap.set(l.code,l)
l.product.stage.pMap.set(l.product.code,l.product)
if(l.size>l.product.lSizeMax) l.product.lSizeMax = l.size
if(l.size>l.product.stage.lSizeMax) l.product.stage.lSizeMax = l.size
})
lafe.l2l.forEach(l2l=>{
l2l.ilot = lafe.lMap.get(l2l.ilot); //input (origin) lot of the lot 2 lot link
l2l.olot = lafe.lMap.get(l2l.olot); //output (destination) lot of the lot 2 lot link
l2l.ix = 0.0 //relative x coordinate of the input of the lot 2 lot link (upper-left corner of the band)
l2l.iy = 0.0 //relative y coordinate of the input of the lot 2 lot link (upper-left corner of the band)
l2l.idy = 0.0 //relative height of the input of the lot 2 lot link (left part of the band)
l2l.ox = 0.0 //relative x coordinate of the output of the lot 2 lot link (upper-right corner of the band)
l2l.oy = 0.0 //relative y coordinate of the output of the lot 2 lot link (upper-right corner of the band)
l2l.ody = 0.0 //relative height of the output of the lot 2 lot link (right part of the band)
//filling parent properties
l2l.ilot.ol2lMap.set(l2l.olot.code,l2l)
l2l.olot.il2lMap.set(l2l.ilot.code,l2l)
l2l.ilot.ol2lSize += l2l.size
addValue(l2l.olot.il2lSizes,l2l.ilot.product.code,l2l.size)
})
lafe.l2d.forEach(l2d=>{
l2d.ilot = lafe.lMap.get(l2d.ilot); //input (orgin) lot of the lot 2 demand link
l2d.odemand = lafe.dMap.get(l2d.odemand); //output (destinaion) demand of the lot 2 demand link
l2d.ix = 0.0 //relative x coordinate of the input of the lot 2 demand link (upper-left corner of the band)
l2d.iy = 0.0 //relative y coordinate of the input of the lot 2 demand link (upper-left corner of the band)
l2d.idy = 0.0 //relative height of the input of the lot 2 demand (left part of the band)
l2d.ox = 0.0 //relative x coordinate of the output of the lot 2 demand (upper-right corner of the band)
l2d.oy = 0.0 //relative y coordinate of the output of the lot 2 demand (upper-right corner of the band)
l2d.ody = 0.0 //relative height of the output of the lot 2 lot link (right part of the band)
//filling parent properties
l2d.odemand.size += l2d.size
l2d.ilot.ol2dMap.set(l2d.odemand.code,l2d)
l2d.odemand.il2dMap.set(l2d.ilot.code,l2d)
l2d.ilot.ol2dSize += l2d.size
})
//Recursively computing backward depth of stages,
//starting from demands and propagating backward
//considering also possible lots with no output links
lafe.demands.forEach(d=>bPropagate(d.il2dMap,0))
lafe.lots.filter(l=>(l.ol2lMap.size()+l.ol2dMap.size())==0).forEach(l=>bPropagate(l.il2lMap,1))
//Defining groups according to backward depths
//groups are used to group stages in such a way to
//minimize overlaping of links
//all lots inside a group will be positioned separately along the y axis
lafe.gCount = d3.set(lafe.stages.map(s=>s.bdepth)).size()
lafe.groups = [...Array(lafe.gCount).keys()].map(i=>{
let group = {
"code":"group-"+(i+1),
"index":i,
"bdepth":lafe.gCount-1-i,
"first":(i==0),
"last":(i==lafe.gCount-1),
"sCount":0, //number of stages in the group
"pCount":0, //number of products in the group
"lCount":0, //number of lots in the group
"tCount":0, //number of targets in the group
}
return group
})
lafe.gMap =getMap(lafe.groups)
//Assigning stages to groups and splitting demands into targets.
//One target is created for each couple group x demand for which l2d links exist
lafe.groups.forEach(g=>{
g.sMap = d3.map(lafe.stages.filter(s=>s.bdepth==g.bdepth),s=>s.code)
g.sCount = g.sMap.size()
g.tMap = d3.map() //targets of the group
g.sMap.each(s=>{
s.group = g
s.lMap.each(l=>{
l.ol2dMap.each(l2d=>{
//l2d is a lot to demand link with a lot part of the group
//a target is created for that demand within the group if not existing yet
let target = g.tMap.get(l2d.odemand.code)
if (target===undefined){
//create a new target (a target is a group-demand couple)
target = {
"code":g.code+"|"+l2d.odemand.code,
"sortkey":l2d.odemand.sortkey,
"group":g,
"demand":l2d.odemand,
"l2dMap":d3.map(), //map of all l2d links with this target as destination
"size":0, //total demand size of the target
"sizes":d3.map(), //demand size per product of the the target
"selected":false, //selected state of the target
"visible":true, //visible state of the target
"x":0.0, //relative x coordinate of the target
"y":0.0, //relative y coordinate of the target
"dx":0.0, //relative width of the target
"dy":0.0 //relative height of the target
}
g.tMap.set(l2d.odemand.code,target)
}
l2d.target = target
target.l2dMap.set(l2d.ilot.code,l2d)
target.size += l2d.size
addValue(target.sizes,l2d.ilot.product.code,l2d.size)
})
})
})
})
lafe.targets = lafe.groups.map(g=>sortedValues(g.tMap)).flat() //all targets of the graph
lafe.oCount = lafe.oMap.size() //total number of options
lafe.sCount = lafe.sMap.size() //total number of stages
lafe.pCount = lafe.pMap.size() //total number of products
lafe.lCount = lafe.lMap.size() //total number of lots
//elementary y padding space is the smallest y relative space used for vertical blank spacing between nodes //it corresponds to two pixels if possible, depending on the preferred height of the chart
//and the maxium number of nodes (bars) to align vertically
lafe.lPad = 1 //number of elementary y paddings used for lots
lafe.pPad = 2 //number of elementary y paddings used for products
lafe.sPad = 2 //number of elementary y paddings used for stages
lafe.tPad = 2 //number of elementary y paddings used for targets
return lafe
}