Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在javascript/typescript中绘制形状并填充颜色_Javascript_Typescript_Canvas_Graphics_Drawing - Fatal编程技术网

在javascript/typescript中绘制形状并填充颜色

在javascript/typescript中绘制形状并填充颜色,javascript,typescript,canvas,graphics,drawing,Javascript,Typescript,Canvas,Graphics,Drawing,我正在用typescript/javascript在画布上绘制图像。 我得到的坐标来自直线和圆弧,我从数据库中读取这些数据(直线和圆弧的顺序是随机的)。 这幅画画得很好,但我需要用颜色填充画出的形状。使用moveTo和lineTo执行此操作,然后调用fill不起作用。因为直线和圆弧的随机顺序。第二,因为弧线也可以向内,所以它给“房间”的外面涂上了颜色 示例图片: 我的绘图代码片段: let canvas = <HTMLCanvasElement>document.getEl

我正在用typescript/javascript在画布上绘制图像。 我得到的坐标来自直线和圆弧,我从数据库中读取这些数据(直线和圆弧的顺序是随机的)。 这幅画画得很好,但我需要用颜色填充画出的形状。使用moveTo和lineTo执行此操作,然后调用fill不起作用。因为直线和圆弧的随机顺序。第二,因为弧线也可以向内,所以它给“房间”的外面涂上了颜色

示例图片:

我的绘图代码片段:

    let canvas = <HTMLCanvasElement>document.getElementById("canvas-view");
    canvas.setAttribute('width', '500');
    canvas.setAttribute('height', '500');
    let ctx = canvas.getContext("2d");
    if (ctx != null)
    {
        for (let count = 0; count < image.lines.length; count++)
        {
            ctx.beginPath();
            ctx.moveTo(image.lines[count].startPoint.x, image.lines[count].startPoint.y);
            ctx.lineTo(image.lines[count].endPoint.x, image.lines[count].endPoint.y);
            ctx.stroke();
        }

        for (let count = 0; count < image.arcs.length; count++)
        {
            ctx.beginPath();
            ctx.arc(image.arcs[count].center.x, image.arcs[count].center.y, image.arcs[count].radius, this.DegreesToRadians(image.arcs[count].startAngle), this.DegreesToRadians(image.arcs[count].endAngle));
            ctx.stroke();
        }
    }
let canvas=document.getElementById(“画布视图”);
canvas.setAttribute('width','500');
canvas.setAttribute('height','500');
设ctx=canvas.getContext(“2d”);
如果(ctx!=null)
{
for(让count=0;count
有没有办法给这个形状的内部上色?

对随机线和弧进行排序 如何从直线和圆弧段的随机列表中创建连续路径,每条直线和圆弧的方向都是随机的

步骤1圆弧端点 要填充形状,您需要等待,直到拥有所有直线和圆弧。然后你必须对它们进行排序,这样它们才能形成一个连续的轮廓

为此,您需要起点和终点,这是直线的起点和终点,但不是圆弧的坐标,因此您需要计算它们

此函数用于向圆弧添加起点和终点

function arcEnds(arc){
    const d2r = d => d * Math.PI / 180; // deg to rad
    arc.startPoint = {
       x : Math.cos(d2r(arc.startAngle)) * arc.radius + arc.center.x,
       y : Math.sin(d2r(arc.startAngle)) * arc.radius + arc.center.y
    }
    arc.endPoint = {
       x : Math.cos(d2r(arc.endAngle)) * arc.radius + arc.center.x,
       y : Math.sin(d2r(arc.endAngle)) * arc.radius + arc.center.y
    }
    return arc;
}
步骤2创建单个阵列 要优化直线的构造,需要创建一个同时包含直线和圆弧的阵列。执行此操作时,还可以计算圆弧端点

function createSegmentArray(lines, arcs) {
     return [...lines, ...arcs.map(arcEnds)]; // add lines and end point calculated arcs
}
步骤3创建匹配点的函数 由于在端点计算中可能会出现浮点错误,因此需要有一个公差,以便确定两点是否相等

有两种方法可以查看两点是否位于同一位置。我将使用距离的平方,因为它有点整洁

// 1 pixel tolerance
const isSame = (p1,p2) => (((p1.x - p2.x) ** 2) + ((p1.y - p2.y) ** 2)) < 1; 
创建第二个数组以容纳已排序的段

 const orderedSegs = [];
步骤5按匹配的终点或起点排序 然后从第一段开始,将其从segs阵列中移除,并找到具有匹配的结束或开始的段(我们还不知道方向)。如果未找到匹配的端点,则形状未闭合且无法填充,因此抛出一个错误以指示此情况。您必须添加一个catch处理程序。或者,您可能更愿意中断循环,只标记一个错误

您还需要反转方向错误的线段。对于直线,您只需交换端点,对于圆,您需要交换端点和端点角度,并添加标志以指示方向已反转

 var current = segs.shift();
 while(seg.length > 0){
     let reverse; // if true the segment needs to be reversed
     const index = segs.findIndex(seg => { // find segment with matching end or start
         if(isSame(current.endPoint, seg.startPoint)){
            reverse = false;
            return true
         }
         if(isSame(current.endPoint, seg.endPoint)){
            reverse = true;
            return true
         }
         return false;
      })
      if(index === -1){ throw new Error("The shape is not closed and can not be filled") }
      orderedSegs.push(current);
      current = segs.splice(index,1)[0]; // get the connected seg
      if(reverse){
           if(current.center){ // is a circle
                const t = current.endPoint;
                current.endPoint = current.startPoint;
                current.startPoint = t;
                const t1 = current.endAngle;
                current.endAngle= current.startAngle;
                current.startAngle = t1;
                current.reversed = true;
           }else{
                const t = current.endPoint;
                current.endPoint = current.startPoint;
                current.startPoint = t;
           }
      }
      // loop until no more segments
  }
  // push the last seg onto the array
  orderedSegs.push(current);
步骤6渲染结果。 现在,所有点都按连接顺序进行了渲染,但必须反转圆弧的方向

  const d2r = d => d * Math.PI / 180; // deg to rad
  var i;
  ctx.beginPath();
 // arcs have start points now so dont have to check type for first point
  ctx.moveTo(orderedSegs[0].startPoint.x, orderedSegs[0].startPoint.y);
  for(i = 0; i < orderedSegs.length; i++){
       var seg = orderedSegs[i];
       if(seg.center){ // is a arc
           ctx.arc(
               seg.center.x, seg.center.y, seg.radius,
               d2r(seg.startAngle), d2r(seg.endAngle), seg.reverse
           );
       }else{
           ctx.lineTo(seg.endPoint.x, seg.endPoint.y);
       }
  }
  ctx.fill();
  ctx.stroke();
constd2r=d=>d*Math.PI/180;//度至拉德
var i;
ctx.beginPath();
//圆弧现在有起点,所以不必检查第一个点的类型
ctx.moveTo(orderedSegs[0].startPoint.x,orderedSegs[0].startPoint.y);
对于(i=0;i
这就是过程

警告
  • 如果形状中有孔,则此操作将不起作用。对于每个段,必须有一个具有匹配终点或起点的段

  • 如果在第一个和最后一个之间没有段,它将工作。它假定它们之间有一条线段,并不表示路径未闭合。您需要测试
    orderedSegs
    数组中第一段的起点是否与
    isSame
    数组中最后一段的终点相同

  • 如果存在两个以上线段连接的点,则此操作将不起作用。如果是这样,创建的路径将沿着第一个连接的段移动,该段可能不是正确的段。它将不会完成排序,并将抛出错误,即使它可能已找到正确的闭合路径

  • 这将忽略圆弧方向,并假设圆弧在方向上是统一的(所有CW或所有CCW)。如果不是这样,则必须在渲染路径时进行适当的更正

  • 上面创建的圆弧的起点和终点可能与直线的终点不同。它们只是带有x,y的对象。如果希望它们是相同类型的对象,则需要在
    arcEnds
    函数中执行此操作

  • 由于我没有数据集,所以无法测试上述代码,因此很可能存在任何数量的打字错误。该代码仅作为流程逻辑的指南。您应该使用它来创建自己的版本,而不仅仅是复制和粘贴,因为它可能会由于输入错误而引发错误

  •   const d2r = d => d * Math.PI / 180; // deg to rad
      var i;
      ctx.beginPath();
     // arcs have start points now so dont have to check type for first point
      ctx.moveTo(orderedSegs[0].startPoint.x, orderedSegs[0].startPoint.y);
      for(i = 0; i < orderedSegs.length; i++){
           var seg = orderedSegs[i];
           if(seg.center){ // is a arc
               ctx.arc(
                   seg.center.x, seg.center.y, seg.radius,
                   d2r(seg.startAngle), d2r(seg.endAngle), seg.reverse
               );
           }else{
               ctx.lineTo(seg.endPoint.x, seg.endPoint.y);
           }
      }
      ctx.fill();
      ctx.stroke();