Gozintograph: Unterschied zwischen den Versionen

Markierung: Zurückgesetzt
Keine Bearbeitungszusammenfassung
Markierung: Manuelle Zurücksetzung
Zeile 46: Zeile 46:
Die Pfeile zeigen, welche Einzelteile in welches Bauteil eingehen. Die Zahlen an den Pfeilen geben die benötigte Stückzahl an.
Die Pfeile zeigen, welche Einzelteile in welches Bauteil eingehen. Die Zahlen an den Pfeilen geben die benötigte Stückzahl an.


<!-- VARIANTE B – FINAL OPTIMIERT --> <html> <style> .gozinto-wrap { width:95vw; height:50vw; max-width:1100px; max-height:400px; border:0; margin:0; padding:0; } svg { width:100%; height:100%; touch-action:none; user-select:none; background:white; } .node-rect { fill:#3498db; stroke:#1f4e78; stroke-width:2; cursor:grab; } /* Einheitliche Schriftgröße */ .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> (function(){ const svg = document.getElementById("gozinto_svg_2"); // leicht reduzierte Abstände const scale = 100; const yOffset = 0; // früher 5 20 → Grafik rückt nach oben const xOffsetGlobal = 120; // gesamte Grafik leicht nach rechts (zentrieren) 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; // gesamte Grafik nach rechts versetzt 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}; // Draggen 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, cy=node.cy, w2=node.w/2, h2=node.h/2; const dx=tx-cx, 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, 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, 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; } 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), Ci=px(pCircleIn), Co=px(pCircleOut), 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], 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={}; // Einzelteile oben nodes.E1=createNode("E1",0,0.5,1.0,0.5,"E1"); 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"); // Bauteile darunter 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"); // ------------ Verbindungen ------------ makeConnection(nodes.E1,nodes.B1,"2",2.2,-0.2); makeConnection(nodes.E2,nodes.B1,"1",2.2, 0.2); makeConnection(nodes.E1,nodes.B2,"2",2.2,-0.2); 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> </html>
<!-- VARIANTE B – FINAL OPTIMIERT -->
<html>
<style>
  .gozinto-wrap {
    width:95vw;
    height:50vw;
    max-width:1100px;
    max-height:400px;
    border:0;
    margin:0;
    padding:0;
  }
 
  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>
(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);
      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);
  }
 
  function intersectRectBorder(node, tx, ty){
    const cx=node.cx, cy=node.cy, w2=node.w/2, h2=node.h/2;
    const dx=tx-cx, 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, 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`;
  }
 
  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),
          Ci=px(pCircleIn),
          Co=px(pCircleOut),
          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);
  }
 
  const nodes={};
 
  nodes.E1=createNode("E1",0,0.5,1.0,0.5,"E1");
  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);
 
  makeConnection(nodes.E1,nodes.B2,"2",2.2,-0.2);
  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>
</html>


Die Gozintomatrix zum oberen Gozintographen kann aus folgender Tabelle abgeleitet werden:
Die Gozintomatrix zum oberen Gozintographen kann aus folgender Tabelle abgeleitet werden: