27324


半径が点の範疇以上に指定されている円があり、Pathをその縁の中心を目的地にしています。  
例えば、次のようなSVGを描きました。

[code]

    <svg width="600px" height="400px" xmlns="http://www.w3.org/2000/svg" version="1.1">
    
    
    <!-- /////////////////////////////////////////////// -->
    <g>
    <defs>
    <marker id="endpoint" viewBox="-50 0 50 50" refX="0" refY="25" markerUnits="strokeWidth" markerWidth="20" markerHeight="20" orient="auto">
      <path d="M -50 0 L 0 25 L -50 50 z"></path>
    </marker>
    </defs>
    <text x="10" y="50">単にmarker-endを指定しても、circleの半径が大きいと円の下敷きになってしまう</text>
    <path d="M 0,100 C 50,100 80,131 130,131 C 130,131 180,130 200,100" 
     stroke="black"
     fill="none"
     marker-end="url(#endpoint)"
     > 
     </path>
     </g>
     <g>
     <circle cx="200" cy="100" r="20" stroke-width="2px" stroke="#aaa" fill="#fff" opacity="0.75"></circle>
     <!-- /////////////////////////////////////////////// -->
     <text x="10" y="240">このように、円の淵に矢印の先端が来るようにしたい。</text>
     <text x="10" y="260">ただし、進入角度は予測できず、線も一本だけとも限らない</text>
    <path d="M 0,300 C 50,300 80,331 130,331 C 130,331 180,330 200,300" 
     stroke="black"
     fill="none"
     > 
     </path>
     <path d="M 185 315 L 174 312 L 185 326 z"></path>
     
     <circle cx="200" cy="300" r="20" stroke-width="2px" stroke="#aaa" fill="#fff" opacity="0.75"></circle>
     </g>
     
      <text x="10" y="20">2016/07/03 14:30 本スニペットを追加</text>
     </svg>
[/code]

矢印が円の中に消えてしまうため、
marker内で生成する三角形をrefXで外側に来るようにずらしますが、この方法では、進入角度によっては矢印としての三角形が成り立ちません。  
方法が無いかと色々探しているのですが、見当たらない状況です。  
何か良い方法は無いでしょうか。よろしくお願いします。

(線は複数あり、また予測不能ないろいろな角度から侵入します。)


CC BY-SA 4.0
2016-07-02T09:37:40.260
27267
2019-12-13T17:49:33.437
2019-12-13T17:49:33.437
32986
8667
question
svg
SVGの、circle中心に伸びるpathのmarker-endの調整方法


svg に詳しいわけではありませんが以下の本家SOでの以下のQAが参考になりそうに思います。

[Arrow triangles on my SVG line - Stack
Overflow](https://stackoverflow.com/a/11809868/5120044) より抜粋

[code]

    <svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 -100 200 200">
    <defs>
      <marker id='mid' orient="auto"
        markerWidth='2' markerHeight='4'
        refX='0.1' refY='1'>
        <!-- triangle pointing right (+x) -->
        <path d='M0,0 V2 L1,1 Z' fill="orange"/>
      </marker>
      <marker id='head' orient="auto"
        markerWidth='2' markerHeight='4'
        refX='0.1' refY='2'>
        <!-- triangle pointing right (+x) -->
        <path d='M0,0 V4 L2,2 Z' fill="red"/>
      </marker>
    </defs>
    
    <path
      id='arrow-line'
      marker-mid='url(#mid)'
      marker-end='url(#head)'
      stroke-width='5'
      fill='none' stroke='black'  
      d='M0,0 L20,20 C40,40 40,40 60,20 L80,0'
      />
    
    </svg>
[/code]

要は `orient="auto"` が肝のようです。


CC BY-SA 3.0
2016-07-03T06:03:41.283
27291
2016-07-03T06:03:41.283
2017-05-23T12:38:56.467
-1
10492
27267
answer


本件、色々調べ方法が見つからず、また海外版（本家）のStackOverflowで問い合わせたところ、「No way」手段は無いよ、という回答に至りました。  
結果、Javascriptを介入させ、Javascriptによって制御を行う、という形を取りました。

[code]

    var paths = new Array();
    var autonext = -1;
    var draw = function(type, opts) {
      var svg = document.createElementNS("http://www.w3.org/2000/svg", type);
      for (var k in opts) {
        svg.setAttribute(k, opts[k]);
      }
      return svg;
    };
    
    
    var getCircleDestination = function(from, to) {
    
      var tx = parseFloat(to.getAttribute("cx"));
      var ty = parseFloat(to.getAttribute("cy"));
    
      var fx = from.center.x;
      var fy = from.center.y;
    
      var w = fx - tx;
      var h = fy - ty;
    
      var z = Math.sqrt(w * w + h * h);
      var r = parseFloat(to.getAttribute("r"));
    
      var dz = z / r;
    
      var dx = tx + w / dz;
      var dy = ty + h / dz;
    
    /* dx2,dy2・・・ここをより改良する必要があります */
      var dx2 = tx + w / (dz * 1.2); 
      var dy2 = ty + h / (dz * 1.2);
    
      var circle = draw("circle", {
        "cx": dx,
        "cy": dy,
        "r": 3,
        "fill": "red"
      });
    
      return {
        "s": {
          "x": dx2,
          "y": dy2
        },
        "svg": circle
      };
    };
    
    var nextline = function(){
      
      var x = Math.random() * canvas.clientWidth/4;
      var y = Math.random() * canvas.clientHeight;
      if(Math.random() * 100 < 50){x+=canvas.clientWidth/4*3;}
      var from = {
        "center": {
          "x": x,
          "y": y
        }
      };
    
      var svg = getCircleDestination(from, circle);
      var c = svg.svg;
      var stx = svg.s.x;
      var sty = svg.s.y;
    
      var ttx = svg.s.x + (Math.random() * 10 + 15) * (svg.s.x < x ? 1 : -1);
      var tty = svg.s.y + (Math.random() * 10 + 15) * (svg.s.y < y ? 1 : -1);
    
      var p = draw("path", {
        "d": "M" + x + "," + y + " Q " + ttx + "," + tty + " " + stx + "," + sty + " " + c.getAttribute("cx") + "," + c.getAttribute("cy"),
        "stroke": "black",
        "fill": "none",
        "marker-end": "url(#endpoint)"
      });
    
    
      canvas.appendChild(c);
      canvas.appendChild(p);
      paths.push({
        "c": c,
        "p": p
      });
      if (paths.length > 10) {
        var t = paths.shift();
        t.c.remove();
        t.p.remove();
      }
    }
    
    
    
    
    var canvas = draw("svg", {
      "width": "400",
      "height": "150",
      "style": "background-color:#f0f0f0;border:1px solid black;"
    });
    var def = draw("defs", {});
    var marker = draw("marker", {
      "id": "endpoint",
      "viewBox": "-50 0 50 50",
      "refX": 0,
      "refY": 25,
      "markerUnits": "strokeWidth",
      "markerWidth": 10,
      "markerHeight": 10,
      "orient": "auto"
    });
    var marker_path = draw("path", {
      "d": "M -50 0 L 0 25 L -50 50 z"
    });
    var btn = document.createElement("button");
    btn.style.display = "block";
    btn.innerHTML = "Add Line";
    
    canvas.appendChild(def);
    canvas.appendChild(marker);
    marker.appendChild(marker_path);
    
    document.getElementById("canvas").appendChild(canvas);
    document.getElementById("canvas").appendChild(btn);
    
    var circle = draw("circle", {
      "cx": 200,
      "cy": 75,
      "r": 10,
      "fill": "white",
      "stroke": "black",
      "stroke-width": 1,
      "opacity": 0.5
    });
    canvas.appendChild(circle);
    
    
    btn.addEventListener("click", function() { nextline(); });
    btn.addEventListener("mouseover",function(){if(autonext == -1){btn.innerHTML = 'Right Click to Interval';}else{btn.innerHTML = 'Right Click to Stop Interval';}});
    btn.addEventListener("mouseout",function(){btn.innerHTML = 'Add Line';});
    btn.addEventListener("contextmenu", function() {
      if(autonext == -1){
        autonext = setInterval(function(){nextline();},500);
      }else{
        clearTimeout(autonext);
        autonext = -1;
      }
      return false;
    });
[/code]

[code]

    <div id="canvas"></div>
[/code]

ご考慮いただきました皆様、有難う御座いました。


CC BY-SA 3.0
2016-07-04T09:01:01.860
27324
2016-07-04T16:05:13.307
2016-07-04T16:05:13.307
8667
8667
27267
answer