如何将 L 系统逻辑应用于段

how to apply L-system logic to segments

编辑
这是一个正确应用长度和模型但未正确定位模型的新版本。我想这可能会有帮助。

http://codepen.io/pixelass/pen/78f9e97579f99dc4ae0473e33cae27d5?editors=001


我有 2 canvas 个实例

  1. 型号
  2. 结果

在模型视图中,用户可以拖动手柄来修改模型 然后结果视图应该将模型应用于每个段(相对)

这只是分形曲线的基本 l 系统逻辑,尽管我在将模型应用于线段时遇到问题。

看下图:红线应该复制模型,但我不知道如何正确应用逻辑

我这里有一个试用版:http://codepen.io/pixelass/pen/c4d7650af7ce4901425b326ad7a4b259

ES6

// simplify Math

'use strict';

Object.getOwnPropertyNames(Math).map(function(prop) {
  window[prop] = Math[prop];
});

// add missing math functions
var rad = (degree)=> {
  return degree * PI / 180;
};
var deg = (radians)=> {
  return radians * 180 / PI;
};

// get our drawing areas

var model = document.getElementById('model');
var modelContext = model.getContext('2d');

var result = document.getElementById('result');
var resultContext = result.getContext('2d');

var setSize = function setSize() {
  model.height = 200;
  model.width = 200;
  result.height = 400;
  result.width = 400;
};

// size of the grabbing dots
var dotSize = 5;
// flag to determine if we are grabbing a point
var grab = -1;
// set size to init instances
setSize();
//
var iterations = 1;

// define points
// this only defines the initial model

var returnPoints = function returnPoints(width) {
  return [{
    x: 0,
    y: width
  }, {
    x: width / 3,
    y: width
  }, {
    x: width / 2,
    y: width / 3*2
  }, {
    x: width / 3 * 2,
    y: width
  }, {
    x: width,
    y: width
  }];
};

// set initial state for model
var points = returnPoints(model.width);

// handle interaction
// grab points only if hovering
var grabPoint = function grabPoint(e) {
  var X = e.layerX;
  var Y = e.layerY;
  for (var i = 1; i < points.length - 1; i++) {
    if (abs(X - points[i].x) < dotSize && abs(Y - points[i].y) < dotSize) {
      model.classList.add('grabbing');
      grab = i;
    }
  }
};
// release point
var releasePoint = function releasePoint(e) {
  if (grab > -1) {
    model.classList.add('grab');
    model.classList.remove('grabbing');
  }
  grab = -1;
};

// set initial state for result

// handle mouse movement on the model canvas
var handleMove = function handleMove(e) {
  // determine current mouse position
  var X = e.layerX;
  var Y = e.layerY;
  // clear classes
  model.classList.remove('grabbing');
  model.classList.remove('grab');

  // check if hovering a dot
  for (var i = 1; i < points.length - 1; i++) {
    if (abs(X - points[i].x) < dotSize && abs(Y - points[i].y) < dotSize) {
      // indicate grabbable
      model.classList.add('grab');
    }
  }

  // if grabbing
  if (grab > -1) {
    // indicate grabbing
    model.classList.add('grabbing');
    // modify dot on the model canvas
    points[grab] = {
      x: X,
      y: Y
    };
    // modify dots on the result canvas
    drawSegment({
      x: points[grab - 1].x,
      y: points[grab - 1].y
    }, {
      x: X,
      y: Y
    });

  }
};

let m2 = points[1].x / points[4].x
let m3 = points[2].x / points[4].x
let m4 = points[3].x / points[4].x
let n2 = points[1].y / points[4].y
let n3 = points[2].y / points[4].y
let n4 = points[3].y / points[4].y

var drawSegment = function drawSegment(start, end) {
  var dx = end.x - start.x
  var dy = end.y - start.y
  var dist = sqrt(dx * dx + dy * dy)
  var angle = atan2(dy, dx)
  let x1 = end.x
  let y1 = end.y
  let x2 = round(cos(angle) * dist)
  let y2 = round(sin(angle) * dist)

  resultContext.srtokeStyle = 'red'
  resultContext.beginPath()
  resultContext.moveTo(x1, y1)
  resultContext.lineTo(x2, y2)
  resultContext.stroke()

  m2 = points[1].x / points[4].x
  m3 = points[2].x / points[4].x
  m4 = points[3].x / points[4].x
  n2 = points[1].y / points[4].y
  n3 = points[2].y / points[4].y
  n4 = points[3].y / points[4].y

};

var drawDots = function drawDots(points) {
  // draw dots
  for (var i = 1; i < points.length - 1; i++) {
    modelContext.lineWidth = 4; //
    modelContext.beginPath();
    modelContext.strokeStyle = 'hsla(' + 360 / 5 * i + ',100%,40%,1)';
    modelContext.fillStyle = 'hsla(0,100%,100%,1)';
    modelContext.arc(points[i].x, points[i].y, dotSize, 0, 2 * PI);
    modelContext.stroke();
    modelContext.fill();
  }
};

var drawModel = function drawModel(ctx, points, n) {


  var dx = points[1].x - points[0].x
  var dy = points[1].y - points[0].y
  var dist = sqrt(dx * dx + dy * dy)
  var angle = atan2(dy, dx)
  let x1 = points[1].x
  let y1 = points[1].y
  let x2 = round(cos(angle) * dist)
  let y2 = round(sin(angle) * dist)

  ctx.strokeStyle = 'hsla(0,0%,80%,1)';
  ctx.lineWidth = 1;
  ctx.beginPath();
  ctx.moveTo(points[0].x,       
             points[0].y)
  ctx.lineTo(points[1].x * m2,  
             points[1].y * n2)
  ctx.lineTo(points[1].x * m3,  
             points[1].y * n3)
  ctx.lineTo(points[1].x * m4,  
             points[1].y * n4)
  ctx.lineTo(points[1].x,       
             points[1].y)

  ctx.stroke();

    ctx.strokeStyle = 'hsla(100,100%,80%,1)';

  ctx.beginPath();
  ctx.moveTo(points[0].x,       
             points[0].y)
  ctx.lineTo(points[1].x,       
             points[1].y)

  ctx.stroke()
  if (n > 0 ) {

    drawModel(resultContext, [{
      x: points[0].x,
      y: points[0].y
    }, {
      x: points[1].x * m2,
      y: points[1].y * n2 
    }], n - 1);
    drawModel(resultContext, [{
      x: points[1].x * m2,
      y: points[1].y * n2
    }, {
      x: points[1].x * m3,
      y: points[1].y * n3 
    }], n - 1);
    /*
    drawModel(resultContext, [{
      x: points[1].x * m3,
      y: points[1].y * m3
    }, {
      x: points[1].x * m4,
      y: points[1].y * n4 
    }], n - 1);

    drawModel(resultContext, [{
      x: points[1].x * m4,
      y: points[1].y * m4
    }, {
      x: points[1].x,
      y: points[1].y 
    }], n - 1);*/
  } else {
 ctx.strokeStyle = 'hsla(0,100%,50%,1)';
  ctx.beginPath();
  ctx.moveTo(points[0].x,       
             points[0].y)
  ctx.lineTo(points[1].x * m2,  
             points[1].y * n2)
  ctx.lineTo(points[1].x * m3,  
             points[1].y * n3)
  ctx.lineTo(points[1].x * m4,  
             points[1].y * n4)
  ctx.lineTo(points[1].x,       
             points[1].y)

  ctx.stroke();
  }
};

var draw = function draw() {

  // clear both screens
  modelContext.fillStyle = 'hsla(0,0%,100%,.5)';
  modelContext.fillRect(0, 0, model.width, model.height);

  resultContext.fillStyle = 'hsla(0,0%,100%,1)';
  resultContext.fillRect(0, 0, result.width, result.height);

  // draw model
  drawModel(modelContext, [{
    x: 0,
    y: 200
  }, {
    x: 200,
    y: 200
  }]);

  drawModel(resultContext, [{
    x: 0,
    y: 400
  }, {
    x: 400,
    y: 400
  }],iterations);


  // draw the dots to indicate grabbing points
  drawDots(points);
  // redraw
  requestAnimationFrame(draw);
};

window.addEventListener('resize', setSize);
model.addEventListener('mousemove', handleMove);
model.addEventListener('mousedown', grabPoint);
window.addEventListener('mouseup', releasePoint);

setSize();
draw();

给定点、旧原点(模型线段的起点)、新原点(子线段的起点)、角度和比例(你有已经计算了这些):

var transformPoint = function transformPoint(point, oldOrigin, newOrigin, angle, dist) {

  // subtract old origin to rotate and scale relative to it:
  var x = point.x - oldOrigin.x;
  var y = point.y - oldOrigin.y;

  // rotate by angle
  var sine = sin(angle)
  var cosine = cos(angle)
  var rotatedX = (x * cosine) - (y * sine);
  var rotatedY = (x * sine) + (y * cosine);

  // scale
  rotatedX *= dist;
  rotatedY *= dist;

  // offset by new origin and return:
  return {x: rotatedX + newOrigin.x - oldOrigin.x, y: rotatedY + newOrigin.y - oldOrigin.y }
}

您需要通过旧原点平移它(以便您可以围绕它旋转),然后旋转,然后缩放,然后通过新原点平移。然后return点。

modelLogic[0] 是旧原点,因为它定义了模型中段的起点,points[0] 是新原点,因为它是转换映射到的。

您可以像这样从 drawModel 函数中调用该函数:

  let p1 = transformPoint(modelLogic[0], modelLogic[0], points[0], angle, dist);
  let p2 = transformPoint(modelLogic[1], modelLogic[0], points[0], angle, dist);
  let p3 = transformPoint(modelLogic[2], modelLogic[0], points[0], angle, dist);
  let p4 = transformPoint(modelLogic[3], modelLogic[0], points[0], angle, dist);
  let p5 = transformPoint(modelLogic[4], modelLogic[0], points[0], angle, dist);

并更改绘图代码以使用 returned 点 p1、p2 等而不是 x1、y1、x2、y2 等

或者,您可以创建一个矩阵来表示所有这些平移、旋转和缩放变换,并依次变换每个点。