Gozintograph: Unterschied zwischen den Versionen

 
(96 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
Ein '''Gozintograph''' (von engl. *goes into* = „geht hinein“) ist ein gerichteter Graph, der die Zerlegung eines Endprodukts in seine Einzelteile oder Komponenten beschreibt.   
Ein '''Gozintograph''' (von engl. ''goes into'' = „geht hinein“) ist ein gerichteter Graph, der die Zerlegung eines Endprodukts in seine Einzelteile oder Komponenten beschreibt.   
Jede Kante stellt dabei eine „Gozinto“-Beziehung dar: Sie zeigt von einer Komponente (Teil) auf das Produkt, in das sie eingeht. Der Gozintograph ist ein zentrales Hilfsmittel in der Produktionsplanung und Stücklistenverwaltung.
Jede Kante stellt dabei eine „Gozinto“-Beziehung dar: Sie zeigt von einer Komponente auf das Produkt, in das sie eingeht. Der Gozintograph ist ein zentrales Hilfsmittel in der Produktionsplanung und Stücklistenverwaltung.


== Definition ==
== Definition ==
Ein Gozintograph ist ein gerichteter, azyklischer Graph \( G = (V, E) \), wobei:
* \( V \) die Menge der Knoten darstellt (Produkte oder Teile),
* \( E \subseteq V \times V \) die gerichteten Kanten darstellt, welche „geht-in“-Beziehungen symbolisieren.


Eine Kante \( (v_i, v_j, a_{ij}) \) mit der Beschriftung \( a_{ij} \) zeigt an, dass zur Herstellung eines Teils \( v_j \) genau \( a_{ij} \) Einheiten von Teil \( v_i \) benötigt werden.
Ein Gozintograph ist ein gerichteter, azyklischer Graph
 
:<math>G=(V,E)</math>
 
mit:
* <math>V</math> als Menge der Knoten (Produkte oder Komponenten),
* <math>E \subseteq V \times V</math> als Menge der gerichteten Kanten.
 
Zusätzlich besitzt jede Kante ein Gewicht <math>a_{ij} \in \mathbb{N}</math>. 
Eine Kante <math>(v_i,v_j)\in E</math> mit Gewicht <math>a_{ij}</math> bedeutet, dass zur Herstellung des Produkts <math>v_j</math> genau <math>a_{ij}</math> Einheiten der Komponente <math>v_i</math> benötigt werden.
 
Da rekursive Stücklisten ausgeschlossen werden, enthält ein Gozintograph keine Zyklen.


== Zusammenhang zu Matrizen ==
== Zusammenhang zu Matrizen ==
Die Informationen eines Gozintographen lassen sich in einer sogenannten '''Gozintomatrix''' darstellen. 
Diese ist eine Matrix \( A = (a_{ij}) \), bei der das Element \( a_{ij} \) die Anzahl der Einheiten von Komponente \( i \) angibt, die für die Herstellung von Produkt \( j \) benötigt wird. 
In der Produktionsplanung kann die benötigte Gesamtmenge aller Einzelteile über die Gleichung


\[ \mathbf{x} = (I - A)^{-1} \mathbf{y} \]
Die Informationen eines Gozintographen lassen sich in einer sogenannten '''Gozintomatrix''' darstellen.
 
Dies ist eine Matrix
 
:<math>A=(a_{ij})</math>
 
bei der das Element <math>a_{ij}</math> die Anzahl der Einheiten der Komponente <math>i</math> angibt, die unmittelbar zur Herstellung des Produkts <math>j</math> benötigt werden.


bestimmt werden, wobei \( \mathbf{y} \) den Vektor der Endprodukte und \( \mathbf{x} \) den Vektor der benötigten Teilemengen beschreibt.
Unter der Voraussetzung, dass der Gozintograph zyklusfrei ist und <math>I-A</math> invertierbar ist, kann der Gesamtbedarf aller Komponenten über die Gleichung
 
:<math>
\mathbf{x}=(I-A)^{-1}\mathbf{y}
</math>
 
bestimmt werden, wobei:
* <math>\mathbf{y}</math> den Vektor der Endprodukte,
* <math>\mathbf{x}</math> den Vektor der insgesamt benötigten Komponentenmengen
 
beschreibt.
 
== Beispiele ==


==Beispiele==
=== Produktion eines Produkts aus Einzelteilen ===
=== Produktion eines Produkts aus Einzelteilen ===


Im folgenden Beispiel werden fünf Bauteile \( B_1, B_2, B_3, B_4, B_5 \) aus vier Einzelteilen \( E_1, E_2, E_3, E_4 \) gefertigt.   
Im folgenden Beispiel werden fünf Bauteile <math>B_1,B_2,B_3,B_4,B_5</math> aus vier Einzelteilen <math>E_1,E_2,E_3,E_4</math> gefertigt.   
Die Pfeile zeigen, welche Einzelteile in welches Bauteil eingehen. Die Zahlen an den Pfeilen geben die Stückzahl an.
Die Pfeile zeigen, welche Einzelteile in welches Bauteil eingehen. Die Zahlen an den Pfeilen geben die benötigte Stückzahl an.
 
<html>
<html>
<div id="gozinto_small" style="width:95vw; max-width:1000px; height:70vw; max-height:600px; margin-top:20px;"></div>
<style>
  .gozinto-wrap {
    width:95vw;
    height:50vw;
    max-width:1100px;
    max-height:450px;
    border:0;
    margin:0;
    padding:0;
  }


<script src="https://jsxgraph.org/distrib/jsxgraphcore.js"></script>
  svg {
    width:100%;
    height:100%;
    touch-action:none;
    user-select:none;
    background:white;
  }
 
  .node-rect {
    fill:#3498db;
    stroke:#1f4e78;
    stroke-width:2;
    cursor:grab;
  }
 
  .node-text, .count-text {
    font-family:sans-serif;
    font-size:14px;
    fill:#000;
    pointer-events:none;
  }
 
  .edge-line {
    stroke:#000;
    stroke-width:2;
    fill:none;
  }
 
  .edge-arrow {
    fill:#000;
  }
 
  .count-circle {
    fill:#fff;
    stroke:#000;
    stroke-width:1.5;
  }
</style>
 
<div class="gozinto-wrap">
<svg id="gozinto_svg_2"
    viewBox="0 0 1200 450"
    preserveAspectRatio="xMinYMin meet">
</svg>
</div>


<script>
<script>
document.addEventListener("DOMContentLoaded", function () {
(function(){
 
const svg=document.getElementById("gozinto_svg_2");
 
const scale=100;
const yOffset=0;
const xOffsetGlobal=120;
 
function svgEl(name,attrs){
  const el=document.createElementNS("http://www.w3.org/2000/svg",name);
  for(const k in (attrs||{})) el.setAttribute(k,attrs[k]);
  return el;
}
 
function getSVGcoords(evt){
  const pt=svg.createSVGPoint();
  pt.x=evt.clientX;
  pt.y=evt.clientY;
  return pt.matrixTransform(svg.getScreenCTM().inverse());
}
 
function createNode(id,cx,cy,w,h,label){
 
  cx+=xOffsetGlobal/scale;
 
  const g=svgEl("g",{"data-id":id});
 
  const rect=svgEl("rect",{
    class:"node-rect",
    x:(cx-w/2)*scale,
    y:(cy-h/2)*scale+yOffset,
    width:w*scale,
    height:h*scale,
    rx:6,
    ry:6
  });
 
  const text=svgEl("text",{
    class:"node-text",
    x:cx*scale,
    y:cy*scale+yOffset,
    "text-anchor":"middle",
    "dominant-baseline":"middle"
  });
 
  text.textContent=label;
 
  g.appendChild(rect);
  g.appendChild(text);
 
  svg.appendChild(g);
 
  const node={id,cx,cy,w,h,rect,text,g};
 
  let dragging=false,start={};
 
  rect.addEventListener("pointerdown",e=>{
    rect.setPointerCapture(e.pointerId);
    dragging=true;
 
    const p=getSVGcoords(e);


     var brd = JXG.JSXGraph.initBoard('gozinto_small', {
     start={
        boundingbox: [-1, 10, 15, -1],
      px:p.x,
        axis: false,
      py:p.y,
        showNavigation: false
      cx:node.cx,
    });
      cy:node.cy
    };
  });
 
  rect.addEventListener("pointermove",e=>{
 
    if(!dragging) return;
 
    const p=getSVGcoords(e);
 
    node.cx=start.cx+(p.x-start.px)/scale;
    node.cy=start.cy+(p.y-start.py)/scale;
 
    updateNode(node);
    updateAllEdges();
  });
 
  rect.addEventListener("pointerup",e=>{
    dragging=false;
    rect.releasePointerCapture(e.pointerId);
  });
 
  return node;
}
 
function updateNode(n){
  n.rect.setAttribute("x",(n.cx-n.w/2)*scale);
  n.rect.setAttribute("y",(n.cy-n.h/2)*scale+yOffset);
 
  n.text.setAttribute("x",n.cx*scale);
  n.text.setAttribute("y",n.cy*scale+yOffset);
}
 
function intersectRectBorder(node,tx,ty){
 
  const cx=node.cx;
  const cy=node.cy;
 
  const w2=node.w/2;
  const h2=node.h/2;
 
  const dx=tx-cx;
  const dy=ty-cy;


    // -------------------------------------------------------------
  let pts=[];
    // Hilfsfunktion: Schnittpunkt mit einem Rechteckrand
    // -------------------------------------------------------------
    function edgePointRect(rect, targetPoint) {
        return brd.create('intersection', [
            brd.create('line', [
                ()=>rect.centerX(),
                ()=>rect.centerY(),
                targetPoint
            ], {visible:false}),


            // Die vier Polygonkanten
  if(Math.abs(dx)>1e-9){
            brd.create('segment', [rect.p1, rect.p2], {visible:false}),
            brd.create('segment', [rect.p2, rect.p3], {visible:false}),
            brd.create('segment', [rect.p3, rect.p4], {visible:false}),
            brd.create('segment', [rect.p4, rect.p1], {visible:false})
        ], {visible:false});
    }


     // -------------------------------------------------------------
     let t1=(-w2)/dx;
    // Hilfsfunktion: Punkt am Kreisrand (statt im Zentrum)
     let y1=cy+t1*dy;
    // -------------------------------------------------------------
     function edgePointCircle(pCenter, R, targetPoint) {
        return brd.create('point', [
            ()=> pCenter.X() + R * (targetPoint.X() - pCenter.X()) /
                  Math.sqrt( (targetPoint.X()-pCenter.X())**2 + (targetPoint.Y()-pCenter.Y())**2 ),


            ()=> pCenter.Y() + R * (targetPoint.Y() - pCenter.Y()) /
    if(t1>0 && y1>=cy-h2 && y1<=cy+h2)
                  Math.sqrt( (targetPoint.X()-pCenter.X())**2 + (targetPoint.Y()-pCenter.Y())**2 )
      pts.push({x:cx-w2,y:y1,t:t1});
        ], {visible:false, fixed:true});
    }


     // -------------------------------------------------------------
     let t2=(w2)/dx;
    // Rechteck (nicht skalierbar, nur verschiebbar)
     let y2=cy+t2*dy;
    // -------------------------------------------------------------
     function createRect(x,y,w,h,labelText) {


        let p1 = brd.create('point',[x,y],      {visible:false, fixed:true});
    if(t2>0 && y2>=cy-h2 && y2<=cy+h2)
        let p2 = brd.create('point',[x+w,y],    {visible:false, fixed:true});
      pts.push({x:cx+w2,y:y2,t:t2});
        let p3 = brd.create('point',[x+w,y-h],  {visible:false, fixed:true});
  }
        let p4 = brd.create('point',[x,y-h],    {visible:false, fixed:true});


        let poly = brd.create('polygon',[p1,p2,p3,p4], {
  if(Math.abs(dy)>1e-9){
            fillColor:'#3498db',
            fillOpacity:0.8,
            vertices:{visible:false},
            borders:{strokeWidth:2},
            withLines:false
        });


        // Gruppe verschiebbar machen?
    let t3=(-h2)/dy;
        poly.draggable = true;
    let x3=cx+t3*dx;


        brd.create('text',
    if(t3>0 && x3>=cx-w2 && x3<=cx+w2)
            [ ()=> (p1.X()+p3.X())/2,
      pts.push({x:x3,y:cy-h2,t:t3});
              ()=> (p1.Y()+p3.Y())/2,
              labelText ],
            {anchorX:'middle', anchorY:'middle', strokeColor:'black', fontSize:14}
        );


        return {
    let t4=(h2)/dy;
            p1:p1, p2:p2, p3:p3, p4:p4,
    let x4=cx+t4*dx;
            centerX:()=> (p1.X()+p3.X())/2,
            centerY:()=> (p1.Y()+p3.Y())/2
        };
    }


     // -------------------------------------------------------------
     if(t4>0 && x4>=cx-w2 && x4<=cx+w2)
    // Kreis-Knoten (mit Randverbindung)
      pts.push({x:x4,y:cy+h2,t:t4});
    // -------------------------------------------------------------
  }
    function createCircleNode(x,y,labelText){
        let p = brd.create('point', [x,y], {visible:false});
        let R = 0.35;


        brd.create('circle', [p, R], {
  pts.sort((a,b)=>a.t-b.t);
            strokeColor:'black',
            fillColor:'white'
        });


        brd.create('text',[()=>p.X(), ()=>p.Y(), labelText],
  return pts[0]||{x:cx,y:cy};
            {anchorX:'middle', anchorY:'middle', fontSize:12});
}


        p.radius = R;
function pointOnCircle(cx,cy,R,tx,ty){
        return p;
    }


    // -------------------------------------------------------------
  const dx=tx-cx;
    // Verbindung: Rechteckrand → Kreisrand → Rechteckrand
  const dy=ty-cy;
    // mit leichten Offsets damit Spitzen nicht überlappen
    // -------------------------------------------------------------
    function makeConnection(E, B, amount, yMid, offsetX){


        // Kreis
  const d=Math.sqrt(dx*dx+dy*dy);
        let c = createCircleNode(
            ()=> (E.centerX()+B.centerX())/2 + offsetX,
            yMid,
            amount
        );


        // Punkte am Rand
  if(d<1e-9) return {x:cx,y:cy};
        let pE = edgePointRect(E, c);
        let pC1 = edgePointCircle(c, c.radius, pE);


        let pC2 = edgePointCircle(c, c.radius, B);
  return {
        let pB = edgePointRect(B, c);
    x:cx+R*dx/d,
    y:cy+R*dy/d
  };
}


        // Linien: E → Kreis
function makeArrowHead(x,y,ux,uy,size){
        brd.create('line', [pE, pC1], {
            straightFirst:false, straightLast:false,
            strokeWidth:2
        });


        // Linien: Kreis → B
  let px=-uy;
        brd.create('arrow', [pC2, pB], {
  let py=ux;
            strokeWidth:2
 
        });
  return `M ${x} ${y}
    }
          L ${x-ux*size+px*size*0.5} ${y-uy*size+py*size*0.5}
          L ${x-ux*size-px*size*0.5} ${y-uy*size-py*size*0.5} Z`;
}


    // -------------------------------------------------------------
const edges=[];
    // Einzelteile
    // -------------------------------------------------------------
    let E1 = createRect(0,8,2,1,'E1');
    let E2 = createRect(3,8,2,1,'E2');
    let E3 = createRect(6,8,2,1,'E3');
    let E4 = createRect(9,8,2,1,'E4');


    // -------------------------------------------------------------
function makeConnection(fromNode,toNode,amount,yMid,xOffset){
    // Bauteile
    // -------------------------------------------------------------
    let B1 = createRect(0.5,3,2,1,'B1');
    let B2 = createRect(3.5,3,2,1,'B2');
    let B3 = createRect(6.5,3,2,1,'B3');
    let B4 = createRect(9.5,3,2,1,'B4');
    let B5 = createRect(12.5,3,2,1,'B5');


    // -------------------------------------------------------------
  const g=svgEl("g",{});
    // Verbindungen mit Offsets damit nichts überlappt
    // -------------------------------------------------------------
    makeConnection(E1, B1, "2", 6, -0.2);
    makeConnection(E2, B1, "1", 6, +0.2);


    makeConnection(E1, B2, "2", 6, -0.3);
  const lineA=svgEl("path",{class:"edge-line"});
    makeConnection(E2, B2, "1", 6, +0.3);
  const lineB=svgEl("path",{class:"edge-line"});
  const circle=svgEl("circle",{class:"count-circle"});
  const text=svgEl("text",{class:"count-text"});
  const arrow=svgEl("path",{class:"edge-arrow"});


    makeConnection(E1, B3, "1", 6, -0.4);
  text.textContent=amount;
    makeConnection(E2, B3, "1", 6, 0);
    makeConnection(E3, B3, "1", 6, +0.4);


    makeConnection(E1, B4, "2", 6, -0.4);
  g.appendChild(lineA);
    makeConnection(E3, B4, "1", 6, 0);
  g.appendChild(lineB);
    makeConnection(E4, B4, "1", 6, +0.4);
  g.appendChild(circle);
  g.appendChild(text);
  g.appendChild(arrow);


    makeConnection(E1, B5, "1", 6, -0.2);
  svg.appendChild(g);
    makeConnection(E4, B5, "2", 6, +0.2);


});
  let e={
</script>
    fromNode,
</html>
    toNode,
    amount,
    yMid,
    xOffset,
    circle,
    text,
    lineA,
    lineB,
    arrow
  };


== Beispiel 2: Rezeptstruktur eines Gerichts ==
  edges.push(e);
<html>
<div id="gozinto2" style="width:90vw; max-width:700px; height:60vw; max-height:500px; margin-top:20px;"></div>
<script>
var brd2 = JXG.JSXGraph.initBoard('gozinto2', {
    boundingbox: [-1, 8, 10, -1],
    axis: false,
    showNavigation: false,
    showCopyright: false
});


function box2(x1,y1,x2,y2,text){
  updateEdge(e);
    var poly = brd2.create('polygon',[[x1,y1],[x2,y1],[x2,y2],[x1,y2]],{
        fillColor:'#3498db',fillOpacity:0.8,vertices:{visible:false},
        borders:{strokeColor:'#1f4e78',strokeWidth:2}
    });
    brd2.create('text',[(x1+x2)/2,(y1+y2)/2,text],
        {anchorX:'middle',anchorY:'middle',strokeColor:'white',fontSize:14});
    return poly;
}
}


// Zutaten
function updateEdge(e){
var M=box2(0,6,1.5,7,'Mehl');
var W=box2(2,6,3.5,7,'Wasser');
var H=box2(4,6,5.5,7,'Hefe');
var T=box2(2,3.5,3.5,4.5,'Teig');
var S=box2(5,3.5,6.5,4.5,'Soße');
var P=box2(3,1,4.5,2,'Pizza');


// Pfeile mit Kreisen
  const cx=(e.fromNode.cx+e.toNode.cx)/2+(e.xOffset||0);
function edge2(from,to,label){
  const cy=e.yMid;
    var midX=(from[0]+to[0])/2;
  const R=0.14;
    var midY=(from[1]+to[1])/2;
 
    var dirX=(to[0]-from[0]);
  const pF=intersectRectBorder(e.fromNode,cx,cy);
    var dirY=(to[1]-from[1]);
  const pT=intersectRectBorder(e.toNode,cx,cy);
    var len=Math.sqrt(dirX*dirX+dirY*dirY);
 
    var ux=dirX/len, uy=dirY/len;
  const pCircleIn=pointOnCircle(cx,cy,R,pF.x,pF.y);
    var gap=0.3;
  const pCircleOut=pointOnCircle(cx,cy,R,pT.x,pT.y);
    var p1=[midX-ux*gap, midY-uy*gap];
 
    var p2=[midX+ux*gap, midY+uy*gap];
  const px=p=>[p.x*scale,p.y*scale+yOffset];
    brd2.create('arrow',[from,p1],{strokeColor:'#000',strokeWidth:1.5});
 
    brd2.create('arrow',[p2,to],{strokeColor:'#000',strokeWidth:1.5});
  const F=px(pF);
    brd2.create('circle',[ [midX,midY],0.2 ],{fillColor:'white',strokeColor:'black'});
  const Ci=px(pCircleIn);
    brd2.create('text',[midX,midY,label],{anchorX:'middle',anchorY:'middle',fontSize:11});
  const Co=px(pCircleOut);
  const T=px(pT);
 
  e.lineA.setAttribute("d",`M ${F[0]} ${F[1]} L ${Ci[0]} ${Ci[1]}`);
  e.lineB.setAttribute("d",`M ${Co[0]} ${Co[1]} L ${T[0]} ${T[1]}`);
 
  e.circle.setAttribute("cx",cx*scale);
  e.circle.setAttribute("cy",cy*scale+yOffset);
  e.circle.setAttribute("r",R*scale);
 
  e.text.setAttribute("x",cx*scale-5);
  e.text.setAttribute("y",cy*scale+yOffset+5);
 
  let ux=T[0]-Co[0];
  let uy=T[1]-Co[1];
 
  let L=Math.sqrt(ux*ux+uy*uy);
 
  if(L<1e-6) L=1;
 
  ux/=L;
  uy/=L;
 
  e.arrow.setAttribute("d",makeArrowHead(T[0],T[1],ux,uy,10));
}
 
function updateAllEdges(){
  edges.forEach(updateEdge);
}
}


// Beziehungen
const nodes={};
edge2([0.75,6],[2.75,4.5],'2'); // Mehl->Teig
 
edge2([2.75,6],[2.75,4.5],'1'); // Wasser->Teig
nodes.E1=createNode("E1",0,0.5,1.0,0.5,"E1");
edge2([4.75,6],[2.75,4.5],'0.5'); // Hefe->Teig
nodes.E2=createNode("E2",2.5,0.5,1.0,0.5,"E2");
nodes.E3=createNode("E3",5.0,0.5,1.0,0.5,"E3");
nodes.E4=createNode("E4",7.5,0.5,1.0,0.5,"E4");
 
nodes.B1=createNode("B1",0.75,4.5,1.0,0.5,"B1");
nodes.B2=createNode("B2",2.5,4.5,1.0,0.5,"B2");
nodes.B3=createNode("B3",5.0,4.5,1.0,0.5,"B3");
nodes.B4=createNode("B4",7.5,4.5,1.0,0.5,"B4");
nodes.B5=createNode("B5",10,4.5,1.0,0.5,"B5");
 
makeConnection(nodes.E1,nodes.B1,"2",2.2,-0.2);
makeConnection(nodes.E2,nodes.B1,"1",2.2,0.2);


edge2([2.75,4.5],[3.75,2],'1'); // Teig->Pizza
makeConnection(nodes.E1,nodes.B2,"2",2.2,-0.2);
edge2([5.75,4.5],[3.75,2],'1'); // Soße->Pizza
makeConnection(nodes.E2,nodes.B2,"1",2.2,0.2);
 
makeConnection(nodes.E1,nodes.B3,"1",2.2,-0.25);
makeConnection(nodes.E2,nodes.B3,"1",2.2,0.0);
makeConnection(nodes.E3,nodes.B3,"1",2.2,0.25);
 
makeConnection(nodes.E1,nodes.B4,"2",2.2,-0.3);
makeConnection(nodes.E3,nodes.B4,"1",2.2,0.0);
makeConnection(nodes.E4,nodes.B4,"1",2.2,0.3);
 
makeConnection(nodes.E1,nodes.B5,"1",2.2,-0.2);
makeConnection(nodes.E4,nodes.B5,"2",2.2,0.2);
 
updateAllEdges();
 
})();
</script>
</script>
</html>
</html>


=== Rezeptstruktur eines Gerichts ===
Die Gozintomatrix zum oberen Gozintographen kann aus folgender Tabelle abgeleitet werden:
Im nächsten Beispiel wird der Gozintograph genutzt, um die Zutatenstruktur eines Rezepts zu zeigen. 
 
Das Endprodukt „Pizza“ besteht aus mehreren Zwischenprodukten („Teig“, „Soße“) und Basiszutaten.
{| class="wikitable"
Auch hier zeigen Pfeile mit Zahlen, welche Mengen von Zutaten in die jeweiligen Komponenten eingehen.
! !! B1 !! B2 !! B3 !! B4 !! B5
|-
| '''E1''' || 2 || 2 || 1 || 2 || 1
|-
| '''E2''' || 1 || 1 || 1 || 0 || 0
|-
| '''E3''' || 0 || 0 || 1 || 1 || 0
|-
| '''E4''' || 0 || 0 || 0 || 1 || 2
|}
 
und ist durch
 
:<math>
A=
\begin{pmatrix}
2 & 2 & 1 & 2 & 1 \\
1 & 1 & 1 & 0 & 0 \\
0 & 0 & 1 & 1 & 0 \\
0 & 0 & 0 & 1 & 2
\end{pmatrix}
</math>
 
gegeben.
 
=== Produktion von Spielwaren aus Rohstoffen über Zwischenprodukte ===
 
Ein Spielwarenhersteller produziert aus drei Rohstoffen <math>R_1,R_2,R_3</math> zunächst die beiden Zwischenprodukte <math>Z_1,Z_2</math>, aus denen anschließend die drei Endprodukte <math>E_1,E_2,E_3</math> gefertigt werden.
 
Die Pfeile im Gozintographen geben an, wie viele Mengeneinheiten eines Materials zur Produktion einer Mengeneinheit des entstehenden Produkts benötigt werden.


<!-- GOZINTOGRAPH: Rohstoffe → Zwischenprodukte → Endprodukte -->
<html>
<html>
<div id="gozinto2" style="width:90vw; max-width:600px; height:60vw; max-height:500px; margin-top:20px;"></div>
<style>
  .gozinto-wrap2 {
    width:95vw;
    height:70vw;
    max-width:1200px;
    max-height:800px;
    border:0;
    margin:0;
    padding:0;
  }
 
  .gozinto-wrap2 svg {
    width:100%;
    height:100%;
    touch-action:none;
    user-select:none;
    background:white;
  }
 
  .node-rect {
    fill:#3498db;
    stroke:#1f4e78;
    stroke-width:2;
    cursor:grab;
  }
 
  .node-text,
  .count-text {
    font-family:sans-serif;
    font-size:14px;
    fill:#000;
    pointer-events:none;
  }
 
  .edge-line {
    stroke:#000;
    stroke-width:2;
    fill:none;
  }
 
  .edge-arrow {
    fill:#000;
  }
 
  .count-circle {
    fill:#fff;
    stroke:#000;
    stroke-width:1.5;
  }
</style>
 
<div class="gozinto-wrap2">
<svg id="gozinto_svg"
    viewBox="0 0 1180 640"
    preserveAspectRatio="xMinYMin meet">
</svg>
</div>
 
<script>
<script>
var brd2 = JXG.JSXGraph.initBoard('gozinto2', {
(function(){
     boundingbox: [-1, 8, 10, -1],
 
    axis: false,
  const svg = document.getElementById("gozinto_svg");
    showCopyright: false,
 
    showNavigation: false
  const scale = 100;
});
  const yOffset = 0;
  const xOffsetGlobal = 120;
 
  function svgEl(name, attrs){
     const el = document.createElementNS("http://www.w3.org/2000/svg", name);
 
    for(const k in (attrs||{}))
      el.setAttribute(k, attrs[k]);
 
    return el;
  }
 
  function getSVGcoords(evt){
    const pt = svg.createSVGPoint();
 
    pt.x = evt.clientX;
    pt.y = evt.clientY;
 
    return pt.matrixTransform(svg.getScreenCTM().inverse());
  }
 
  // -------------------------------------------------
  // NODE
  // -------------------------------------------------
 
  function createNode(id, cx, cy, w, h, label){
 
    cx += xOffsetGlobal / scale;
 
    const g = svgEl("g", {"data-id":id});
 
    const rect = svgEl("rect", {
      class:"node-rect",
      x:(cx-w/2)*scale,
      y:(cy-h/2)*scale + yOffset,
      width:w*scale,
      height:h*scale,
      rx:6,
      ry:6
    });


function box2(x1,y1,x2,y2,color){
    const text = svgEl("text", {
    return brd2.create('polygon',[[x1,y1],[x2,y1],[x2,y2],[x1,y2]],{
      class:"node-text",
        fillColor:color,fillOpacity:0.8,vertices:{visible:false},borders:{strokeColor:'#333'}
      x:cx*scale,
      y:cy*scale+yOffset,
      "text-anchor":"middle",
      "dominant-baseline":"middle"
     });
     });
}


var M1=box2(0,6,1,7,'#e67e22'); // Mehl
    text.textContent = label;
var W1=box2(2,6,3,7,'#e67e22'); // Wasser
 
var H1=box2(4,6,5,7,'#e67e22'); // Hefe
    g.appendChild(rect);
var T1=box2(1,3,2,4,'#3498db'); // Teig
    g.appendChild(text);
var T2=box2(5,3,6,4,'#3498db'); // Soße
 
var T3=box2(3,1,4,2,'#2ecc71'); // Pizza
    svg.appendChild(g);
 
    const node = {id,cx,cy,w,h,rect,text,g};
 
    // Dragging
 
    let dragging=false,start={};
 
    rect.addEventListener("pointerdown", e=>{
 
      rect.setPointerCapture(e.pointerId);
 
      dragging=true;
 
      const p=getSVGcoords(e);
 
      start={
        px:p.x,
        py:p.y,
        cx:node.cx,
        cy:node.cy
      };
    });
 
    rect.addEventListener("pointermove", e=>{
 
      if(!dragging) return;
 
      const p=getSVGcoords(e);
 
      node.cx=start.cx+(p.x-start.px)/scale;
      node.cy=start.cy+(p.y-start.py)/scale;
 
      updateNode(node);
      updateAllEdges();
    });
 
    rect.addEventListener("pointerup", e=>{
 
      dragging=false;
 
      rect.releasePointerCapture(e.pointerId);
    });
 
    return node;
  }
 
  function updateNode(n){
 
    n.rect.setAttribute("x",(n.cx-n.w/2)*scale);
    n.rect.setAttribute("y",(n.cy-n.h/2)*scale+yOffset);
 
    n.text.setAttribute("x",n.cx*scale);
    n.text.setAttribute("y",n.cy*scale+yOffset);
  }
 
  // -------------------------------------------------
  // GEOMETRIE
  // -------------------------------------------------
 
  function intersectRectBorder(node, tx, ty){
 
    const cx=node.cx;
    const cy=node.cy;
 
    const w2=node.w/2;
    const h2=node.h/2;
 
    const dx=tx-cx;
    const dy=ty-cy;
 
    let pts=[];
 
    if(Math.abs(dx)>1e-9){
 
      let t1=(-w2)/dx;
      let y1=cy+t1*dy;
 
      if(t1>0 && y1>=cy-h2 && y1<=cy+h2)
        pts.push({x:cx-w2,y:y1,t:t1});
 
      let t2=(w2)/dx;
      let y2=cy+t2*dy;
 
      if(t2>0 && y2>=cy-h2 && y2<=cy+h2)
        pts.push({x:cx+w2,y:y2,t:t2});
    }
 
    if(Math.abs(dy)>1e-9){
 
      let t3=(-h2)/dy;
      let x3=cx+t3*dx;
 
      if(t3>0 && x3>=cx-w2 && x3<=cx+w2)
        pts.push({x:x3,y:cy-h2,t:t3});
 
      let t4=(h2)/dy;
      let x4=cx+t4*dx;
 
      if(t4>0 && x4>=cx-w2 && x4<=cx+w2)
        pts.push({x:x4,y:cy+h2,t:t4});
    }
 
    pts.sort((a,b)=>a.t-b.t);
 
    return pts[0] || {x:cx,y:cy};
  }
 
  function pointOnCircle(cx,cy,R,tx,ty){
 
    const dx=tx-cx;
    const dy=ty-cy;
 
    const d=Math.sqrt(dx*dx+dy*dy);
 
    if(d<1e-9) return {x:cx,y:cy};
 
    return {
      x:cx+R*dx/d,
      y:cy+R*dy/d
    };
  }
 
  function makeArrowHead(x,y,ux,uy,size){
 
    let px=-uy;
    let py=ux;
 
    return `M ${x} ${y}
            L ${x-ux*size+px*size*0.5} ${y-uy*size+py*size*0.5}
            L ${x-ux*size-px*size*0.5} ${y-uy*size-py*size*0.5} Z`;
  }
 
  // -------------------------------------------------
  // KANTEN
  // -------------------------------------------------
 
  const edges=[];
 
  function makeConnection(fromNode,toNode,amount,yMid,xOffset){
 
    const g=svgEl("g",{});
 
    const lineA=svgEl("path",{class:"edge-line"});
    const lineB=svgEl("path",{class:"edge-line"});
 
    const circle=svgEl("circle",{
      class:"count-circle"
    });
 
    const text=svgEl("text",{
      class:"count-text"
    });
 
    const arrow=svgEl("path",{
      class:"edge-arrow"
    });
 
    text.textContent=amount;
 
    g.appendChild(lineA);
    g.appendChild(lineB);
    g.appendChild(circle);
    g.appendChild(text);
    g.appendChild(arrow);
 
    svg.appendChild(g);
 
    let e={
      fromNode,
      toNode,
      amount,
      yMid,
      xOffset,
      circle,
      text,
      lineA,
      lineB,
      arrow
    };
 
    edges.push(e);
 
    updateEdge(e);
  }
 
  function updateEdge(e){
 
    const cx=(e.fromNode.cx+e.toNode.cx)/2+(e.xOffset||0);
    const cy=e.yMid;
 
    const R=0.14;
 
    const pF=intersectRectBorder(e.fromNode,cx,cy);
    const pT=intersectRectBorder(e.toNode,cx,cy);
 
    const pCircleIn=pointOnCircle(cx,cy,R,pF.x,pF.y);
    const pCircleOut=pointOnCircle(cx,cy,R,pT.x,pT.y);
 
    const px=p=>[p.x*scale,p.y*scale+yOffset];
 
    const F=px(pF);
    const Ci=px(pCircleIn);
    const Co=px(pCircleOut);
    const T=px(pT);
 
    e.lineA.setAttribute(
      "d",
      `M ${F[0]} ${F[1]} L ${Ci[0]} ${Ci[1]}`
    );
 
    e.lineB.setAttribute(
      "d",
      `M ${Co[0]} ${Co[1]} L ${T[0]} ${T[1]}`
    );
 
    e.circle.setAttribute("cx",cx*scale);
    e.circle.setAttribute("cy",cy*scale+yOffset);
    e.circle.setAttribute("r",R*scale);
 
    e.text.setAttribute("x",cx*scale-5);
    e.text.setAttribute("y",cy*scale+yOffset+5);
 
    let ux=T[0]-Co[0];
    let uy=T[1]-Co[1];
 
    let L=Math.sqrt(ux*ux+uy*uy);
 
    if(L<1e-6) L=1;
 
    ux/=L;
    uy/=L;
 
    e.arrow.setAttribute(
      "d",
      makeArrowHead(T[0],T[1],ux,uy,10)
    );
  }
 
  function updateAllEdges(){
    edges.forEach(updateEdge);
  }
 
  // -------------------------------------------------
  // NODES
  // -------------------------------------------------
 
  const nodes={};
 
  // Rohstoffe
 
  nodes.R1=createNode("R1",0,0.8,1.0,0.5,"R1");
  nodes.R2=createNode("R2",2.5,0.8,1.0,0.5,"R2");
  nodes.R3=createNode("R3",5.0,0.8,1.0,0.5,"R3");
 
  // Zwischenprodukte
 
  nodes.Z1=createNode("Z1",1.2,3.2,1.0,0.5,"Z1");
  nodes.Z2=createNode("Z2",3.6,3.2,1.0,0.5,"Z2");
 
  // Endprodukte
 
  nodes.E1=createNode("E1",0.5,5.6,1.0,0.5,"E1");
  nodes.E2=createNode("E2",2.8,5.6,1.0,0.5,"E2");
  nodes.E3=createNode("E3",5.1,5.6,1.0,0.5,"E3");
 
  // -------------------------------------------------
  // VERBINDUNGEN
  // -------------------------------------------------
 
  // Rohstoffe -> Zwischenprodukte
 
  makeConnection(nodes.R1,nodes.Z1,"3",1.8,-0.3);
  makeConnection(nodes.R1,nodes.Z2,"1",1.8,0.4);
 
  makeConnection(nodes.R2,nodes.Z1,"4",1.8,-0.2);
  makeConnection(nodes.R2,nodes.Z2,"2",1.8,0.2);
 
  makeConnection(nodes.R3,nodes.Z2,"3",1.8,0.0);
 
  // Zwischenprodukte -> Endprodukte
 
  makeConnection(nodes.Z1,nodes.E1,"2",4.4,-0.2);
  makeConnection(nodes.Z1,nodes.E2,"1",4.4,0.2);


brd2.create('text',[0.4,7.2,'Mehl']);
  makeConnection(nodes.Z2,nodes.E1,"1",4.4,-0.3);
brd2.create('text',[2.4,7.2,'Wasser']);
  makeConnection(nodes.Z2,nodes.E2,"3",4.4,0.0);
brd2.create('text',[4.4,7.2,'Hefe']);
  makeConnection(nodes.Z2,nodes.E3,"2",4.4,0.3);
brd2.create('text',[1.4,4.2,'Teig']);
brd2.create('text',[5.4,4.2,'Soße']);
brd2.create('text',[3.4,2.2,'Pizza']);


function arrow2(fromX, fromY, toX, toY, label){
  updateAllEdges();
    brd2.create('arrow',[[fromX,fromY],[toX,toY]],{strokeColor:'#555'});
    brd2.create('text',[(fromX+toX)/2,(fromY+toY)/2+0.2,label],{fontSize:10});
}


// Zutaten -> Teig
})();
arrow2(0.5,6,1.5,4,'2');
arrow2(2.5,6,1.5,4,'1');
arrow2(4.5,6,1.5,4,'0.5');
// Teig + Soße -> Pizza
arrow2(1.5,3,3.5,2,'1');
arrow2(5.5,3,3.5,2,'1');
</script>
</script>
</html>
</html>
Die vollständigen Mengen seien wie folgt definiert:
{| class="wikitable"
! !! Z1 !! Z2
|-
| '''R1''' || 3 || 1
|-
| '''R2''' || 4 || 2
|-
| '''R3''' || 0 || 3
|}
{| class="wikitable"
! !! E1 !! E2 !! E3
|-
| '''Z1''' || 2 || 1 || 0
|-
| '''Z2''' || 1 || 3 || 2
|}
Aus diesen Tabellen ergibt sich die '''Gozintomatrix Rohstoffe → Endprodukte''' durch Matrixmultiplikation:
:<math>
RZ=
\begin{pmatrix}
3 & 1 \\
4 & 2 \\
0 & 3
\end{pmatrix}
</math>
:<math>
ZE=
\begin{pmatrix}
2 & 1 & 0 \\
1 & 3 & 2
\end{pmatrix}
</math>
:<math>
RE=RZ \cdot ZE
=
\begin{pmatrix}
3 & 1 \\
4 & 2 \\
0 & 3
\end{pmatrix}
\cdot
\begin{pmatrix}
2 & 1 & 0 \\
1 & 3 & 2
\end{pmatrix}
</math>
Berechnung:
:<math>
RE=
\begin{pmatrix}
7 & 6 & 2 \\
10 & 10 & 4 \\
3 & 9 & 6
\end{pmatrix}
</math>
Die Matrix zeigt, wie viele Mengeneinheiten der Rohstoffe <math>R_1,R_2,R_3</math> jeweils zur Herstellung einer Einheit der Endprodukte <math>E_1,E_2,E_3</math> notwendig sind.


[[Kategorie:Lineare_Algebra]]
[[Kategorie:Lineare_Algebra]]
[[Kategorie:AHR_WuV_Mathe_GK]]
[[Kategorie:AHR_WuV_Mathe_GK]]