---
title: 几何模型
source: https://vjmap.com/guide/geomodel.html
---

# 几何模型

API文档地址： [https://vjmap.com/docs/geomodel/](https://vjmap.com/docs/geomodel/)

示例代码地址： [https://vjmap.com/demo/#/demo/map/models/01geomodels](https://vjmap.com/demo/#/demo/map/models/01geomodels)

## 快速开始

### 直接引入JS
```html
<script src="https://vjmap.com/demo/js/vjmap/vjgeo.min.js" type="text/javascript"></script>
```

### npm
```
npm install vjgeo
```

```
import vjgeo from 'vjgeo'
```




## 点
一个点由具有 2 个元素的数组表示。第一个元素是 x，第二个元素是 y。
```js
var p = [0, 0];
```

## 坐标

使用与基础数学和传统制图相同的坐标系，其中 x 值从左到右增加，y 值从下到上增加。允许负值。

## 路径

路径由具有以下强制属性的对象表示：

- 类型：字符串 - “线”、“圆”或“弧”
- 起点：点

## 线

一条线是一个类型为“线”的路径和这个附加属性：

```js
var line = { 
  type: 'line', 
  origin: [0, 0], 
  end: [1, 1] 
 };
```
 ## 圆

 ```js
 var circle = { 
  type: 'circle', 
  origin: [0, 0],
  radius: 1
 };
 ```

## 圆弧

```js
var arc = { 
  type: 'arc', 
  origin: [0, 0],
  radius: 1,
  startAngle: 0,
  endAngle: 45
 };
```

## 导出DWG

调用`vjgeo.exporter.toDWG`函数并将您的路径作为参数传递：

```js
var line = { 
  type: 'line', 
  origin: [0, 0], 
  end: [50, 50] 
 };
let dwgDoc = vjgeo.exporter.toDWG(line);
```
<mapFrame src="">
var line = { 
  type: 'line', 
  origin: [0, 0], 
  end: [50, 50] 
 };
let dwgDoc = vjgeo.exporter.toDWG(line);
</mapFrame>

完整的html页面代码如下，可复制下面的代码保存为index.html本地调试。修改示例中的相关代码片段即可
```html
<!DOCTYPE html>
<html lang='en'>
	<head>
		<meta charset='UTF-8'>
		<title>vjmap demo</title>
		<link rel='stylesheet' type='text/css' href='https://vjmap.com/demo/js/vjmap/vjmap.min.css'>
		<script type='text/javascript' src='https://vjmap.com/demo/js/vjmap/vjmap.min.js'></script>
		<script type='text/javascript' src='https://vjmap.com/demo/js/vjmap/vjgeo.min.js'></script>
	</head>
	<body style=' margin: 0;overflow: hidden;background-color:white;font-size: 16px'>
		<div id='map' style='left:0;right:0;top:0;bottom:0;position: absolute;z-index: 0;'></div>
	</body>
	<script>
	
(async() => {
	document.body.style.background = '#022B4F';
	const env = {
		serviceUrl: 'https://vjmap.com/server/api/v1',
		accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MSwiVXNlcm5hbWUiOiJyb290MSIsIk5pY2tOYW1lIjoicm9vdDEiLCJBdXRob3JpdHlJZCI6InJvb3QiLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjoxOTQyMzA1MjQxLCJpc3MiOiJ2am1hcCIsIm5iZiI6MTYyNjk0NDI0MX0.29OAA4Cnjtw780VxIxWqduLtszp1EtpSNHAmWjiL_OM',
	};
	try {
		let svc = new vjmap.Service(env.serviceUrl, env.accessToken);
		
		const mapBounds = '[-10000,-10000,10000,10000]';
		const mapExtent = vjmap.GeoBounds.fromString(mapBounds);
		const prj = new vjmap.GeoProjection(mapExtent);
		let map = new vjmap.Map({
			container: 'map',
			style: {
				version: svc.styleVersion(),
				glyphs: svc.glyphsUrl(),
				sources: {},
				layers: [],
			},
			center: prj.toLngLat(mapExtent.center()),
			zoom: 2,
			renderWorldCopies: false,
		});
		map.attach(svc, prj);
		await map.onLoad();

        const setDwgDocData = async(strDoc) => {
            const doc = JSON.parse(strDoc);
            let dwgDoc = new vjmap.DbDocument();
            dwgDoc.entitys = doc.entitys;
            let geojson = await map.createDbGeomData(dwgDoc, true, true, {renderAccuracy: 0.005});
            map.getDrawLayer().set(geojson);
            map.fitMapBounds()
        }
        
		// 示例中的代码写这个下面
		var line = { 
          type: 'line', 
          origin: [0, 0], 
          end: [50, 50] 
         };
        let dwgDoc = vjgeo.exporter.toDWG(line);

		// end
		setDwgDocData(dwgDoc);
		const mousePositionControl = new vjmap.MousePositionControl();
		map.addControl(mousePositionControl, "bottom-left");
	} catch (e) {
		console.error(e)
	}
})();
    </script>
</html>
```

也可以把这个路径数组做为参数传递

```js
var line = { 
  type: 'line', 
  origin: [0, 0], 
  end: [50, 50] 
 };
var circle = { 
  type: 'circle', 
  origin: [0, 0],
  radius: 50
 };
var pathArray = [ line, circle ];
let dwgDoc = vjgeo.exporter.toDWG(pathArray);
```
<mapFrame src="">
var line = { 
  type: 'line', 
  origin: [0, 0], 
  end: [50, 50] 
 };
var circle = { 
  type: 'circle', 
  origin: [0, 0],
  radius: 50
 };
var pathArray = [ line, circle ];
let dwgDoc = vjgeo.exporter.toDWG(pathArray);
</mapFrame>

## 模型

模型是vjgeo的核心对象，有以下几个属性

- origin: 基点
- paths: 模型路径对象
- models: 子模型对象

把路径做为模型参数的示例
```js
var line = { 
  type: 'line', 
  origin: [0, 0], 
  end: [50, 50] 
 };
var circle = { 
  type: 'circle', 
  origin: [0, 0],
  radius: 50
 };
var pathObject = { myLine: line, myCircle: circle };
var model = { paths: pathObject };
let dwgDoc = vjgeo.exporter.toDWG(model);
```
<mapFrame src="">
var line = { 
  type: 'line', 
  origin: [0, 0], 
  end: [50, 50] 
 };
var circle = { 
  type: 'circle', 
  origin: [0, 0],
  radius: 50
 };
var pathObject = { myLine: line, myCircle: circle };
var model = { paths: pathObject };
let dwgDoc = vjgeo.exporter.toDWG(model);
</mapFrame>

如果我们将模型代码包装在一个函数中，我们可以多次调用它。做这件事有很多种方法。首先，我们将保持代码不变，并返回模型变量。

```js
function myModel() {
 var line = { 
   type: 'line', 
   origin: [0, 0], 
   end: [50, 50] 
  };
 var circle = { 
   type: 'circle', 
   origin: [0, 0],
   radius: 50
  };
 var pathObject = { myLine: line, myCircle: circle };
 var model = { paths: pathObject };
 return model;
}
let dwgDoc = vjgeo.exporter.toDWG(myModel());
```

或者，我们可以更改我们的函数以使用new运算符，并且使用this关键字设置我们的模型属性：
```js
function myModel() {
 var line = { 
   type: 'line', 
   origin: [0, 0], 
   end: [50, 50] 
  };
 var circle = { 
   type: 'circle', 
   origin: [0, 0],
   radius: 50
  };
 var pathObject = { myLine: line, myCircle: circle };
 this.paths = pathObject;
}
let dwgDoc = vjgeo.exporter.toDWG(new myModel());
```
<mapFrame src="">
function myModel() {
 var line = { 
   type: 'line', 
   origin: [0, 0], 
   end: [50, 50] 
  };
 var circle = { 
   type: 'circle', 
   origin: [0, 0],
   radius: 50
  };
 var pathObject = { myLine: line, myCircle: circle };
 this.paths = pathObject;
}
let dwgDoc = vjgeo.exporter.toDWG(new myModel());
</mapFrame>

我们将创建一个具有 2 个 myModel 实例的新模型
```js
function myModel() {
 var line = { 
   type: 'line', 
   origin: [0, 0], 
   end: [50, 50] 
  };
 var circle = { 
   type: 'circle', 
   origin: [0, 0],
   radius: 50
  };
 var pathObject = { myLine: line, myCircle: circle };
 this.paths = pathObject;
}
var model1 = new myModel();
var model2 = new myModel();
model2.origin = [100, 0];
var model = { 
  models: { "myModel1": model1, "myModel2": model2 }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```
<mapFrame src="">
function myModel() {
 var line = { 
   type: 'line', 
   origin: [0, 0], 
   end: [50, 50] 
  };
 var circle = { 
   type: 'circle', 
   origin: [0, 0],
   radius: 50
  };
 var pathObject = { myLine: line, myCircle: circle };
 this.paths = pathObject;
}
var model1 = new myModel();
var model2 = new myModel();
model2.origin = [100, 0];
var model = { 
  models: { "myModel1": model1, "myModel2": model2 }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
</mapFrame>

给模型设置填充和颜色属性

填充属性用`isFill`属性，只能作用于路径对象如圆、圆弧、闭合的路径中。如果中间有洞的用`isHole`属性

颜色属性可用于模型或路径对象
```js
function myModel() {
			let points = [
                [100, 300], [100, 300], [100, 400], [0, 475], [-100, 400], [-100, 300],
                [-20, 300], [-20, 380], [20, 380], [20, 300], [20, 300]
            ];
            let polyline = new vjgeo.models.Polyline(true, points);
			polyline.data = {
                colorIndex: 2,
                isFill: true,
                alpha: 100
            }
			let hole = {
				paths: {
					circle: new vjgeo.paths.Circle([1000, 300], 300),
					rectLine1: new vjgeo.paths.Line([900, 200], [1100, 200]),
					rectLine2: new vjgeo.paths.Line([1100, 200], [1100, 400]),
					rectLine3: new vjgeo.paths.Line([1100, 400], [900, 400]),
					rectLine4: new vjgeo.paths.Line([900, 400], [900, 200])
				},
				data: {
					color: 0x00FFFF,
					isHole: true
				}
			}
			this.models = {polyline, hole}
}
var model1 = new myModel();
var model2 = new myModel();
model2.origin = [0, -800];
model2.models.polyline.data.colorIndex = 1; //修改颜色
model2.models.polyline.data.alpha = 255; //不透明
var model = { 
  models: { "myModel1": model1, "myModel2": model2 }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```
<mapFrame src="">
function myModel() {
			let points = [
                [100, 300], [100, 300], [100, 400], [0, 475], [-100, 400], [-100, 300],
                [-20, 300], [-20, 380], [20, 380], [20, 300], [20, 300]
            ];
            let polyline = new vjgeo.models.Polyline(true, points);
			polyline.data = {
                colorIndex: 2,
                isFill: true,
                alpha: 100
            }
			let hole = {
				paths: {
					circle: new vjgeo.paths.Circle([1000, 300], 300),
					rectLine1: new vjgeo.paths.Line([900, 200], [1100, 200]),
					rectLine2: new vjgeo.paths.Line([1100, 200], [1100, 400]),
					rectLine3: new vjgeo.paths.Line([1100, 400], [900, 400]),
					rectLine4: new vjgeo.paths.Line([900, 400], [900, 200])
				},
				data: {
					color: 0x00FFFF,
					isHole: true
				}
			}
			this.models = {polyline, hole}
}
var model1 = new myModel();
var model2 = new myModel();
model2.origin = [0, -800];
model2.models.polyline.data.colorIndex = 1;
model2.models.polyline.data.alpha = 255;
var model = { 
  models: { "myModel1": model1, "myModel2": model2 }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
</mapFrame>

也可以结合vjmap中的dwg实体类型进行创建
```js
function myModel() {
    let points = [
        [50, -500], [100, -500]
    ];
    let polyline = new vjgeo.models.Polyline(false, points);
    polyline.data = {
        colorIndex: 2
    }
    let dim = new vjgeo.models.DbEntity("DbAlignedDimension", [
        {
            point: [75, -520],
            attr: "dimLinePoint"
        },
        {
            point: [50, -500],
            attr: "xLine1Point"
        },
        {
            point: [100, -500],
            attr: "xLine2Point"
        }
    ], {
        dimScale: 10,
        textColor: 0x00FFFF,
        arrowColor: 0x00FF00,
        lineColor: 0xFF0000
    });
    this.models = {polyline, dim}
}
var model1 = new myModel();
var model2 = new myModel();
model2.origin = [0, -50];
vjgeo.model.scale(model2, 2, false, [50, -550]);
let text = new vjgeo.models.Text("hello vjmap", [100, -540], 10, Math.PI / 8);
text.data.colorIndex = 6;
var model = {
    models: {model1,model2,text}
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```
<mapFrame src="">
function myModel() {
			let points = [
                [50, -500], [100, -500]
            ];
            let polyline = new vjgeo.models.Polyline(false, points);
			polyline.data = {
                colorIndex: 2
            }
			let dim = new vjgeo.models.DbEntity("DbAlignedDimension", [
                {
                    point: [75, -520],
                    attr: "dimLinePoint"
                },
                {
                    point: [50, -500],
                    attr: "xLine1Point"
                },
                {
                    point: [100, -500],
                    attr: "xLine2Point"
                }
            ], {
                dimScale: 10,
                textColor: 0x00FFFF,
                arrowColor: 0x00FF00,
                lineColor: 0xFF0000
            });
			this.models = {polyline, dim}
}
var model1 = new myModel();
var model2 = new myModel();
model2.origin = [0, -50];
vjgeo.model.scale(model2, 2, false, [50, -550]);
let text = new vjgeo.models.Text("hello vjmap", [100, -540], 10, Math.PI / 8);
text.data.colorIndex = 6;
var model = { 
  models: {model1,model2,text}
};
let dwgDoc = vjgeo.exporter.toDWG(model);
</mapFrame>



## 内置模型

内置模型包括

- Belt
- Bezier Curve
- Bolt Circle
- Bolt Rectangle
- Connect the dots
- DbEntity
- Dogbone
- Dome
- Ellipse
- Elliptic Arc
- Holes
- Oval
- OvalArc
- Polygon
- Rectangle
- Ring
- RoundRectangle
- S curve
- Slot
- Square
- Star
- Text

##  移动模型

模型和路径可以移动到绝对位置，或相对于当前位置移动 [x, y] 量。请记住，由于路径包含在模型中，并且模型可能包含在模型中，因此它们的坐标将相对于包含模型。

为了说明这一点，让我们创建一个有几个正方形的模型

```js
function Squares() {
	this.models = {
		s1: new vjgeo.models.Square(100),
		s2: vjgeo.model.move(new vjgeo.models.Square(100), [120, 0]),
		s3: new vjgeo.models.Square(100)
	};
	//move the third square by setting its origin property.
	this.models.s3.origin = [240, 0];
}
var squares = new Squares();
let dwgDoc = vjgeo.exporter.toDWG(squares);
```

![image-20230422164828602](https://vjmap.com/blogimages/image-20230422164828602.png)

将模型移动到绝对位置的方法是设置其origin属性。vjgeo.model.move函数就是这样做的，但它还允许您在一行代码上执行更多操作。

要按相对量移动模型，请使用vjgeo.model.moveRelative：

```js
function Squares() {
    this.models = {
        s1: new vjgeo.models.Square(100),
        s2: vjgeo.model.move(new vjgeo.models.Square(100), [120, 0]),
        s3: new vjgeo.models.Square(100)
    };
    this.models.s3.origin = [240, 0];
}

var squares = new Squares();

//move some squares by a relative distance
vjgeo.model.moveRelative(squares.models.s2, [-10, 10]);
vjgeo.model.moveRelative(squares.models.s3, [-20, 20]);
let dwgDoc = vjgeo.exporter.toDWG(squares);
```

![image-20230422165000328](https://vjmap.com/blogimages/image-20230422165000328.png)

同样，可以使用vjgeo.path.move 绝对移动路径或使用vjgeo.path.moveRelative 相对移动路径：

```js
function Squares() {
    this.models = {
        s1: new vjgeo.models.Square(100),
        s2: vjgeo.model.move(new vjgeo.models.Square(100), [120, 0]),
        s3: new vjgeo.models.Square(100)
    };
    this.models.s3.origin = [240, 0];
}

var squares = new Squares();

//move a path by a relative distance
vjgeo.path.moveRelative(squares.models.s3.paths.ShapeLine3, [0, 20]);

//move a path to an absolute point
vjgeo.path.move(squares.models.s2.paths.ShapeLine1, [30, 20]);
let dwgDoc = vjgeo.exporter.toDWG(squares);
```

![image-20230422165118400](https://vjmap.com/blogimages/image-20230422165118400.png)

请注意，第二个正方形的原点为 [120, 0]，但我们将正方形内的一条线移动到绝对点 [30, 20]。由于直线包含在正方形模型中，因此它的坐标以正方形表示，这就是它看起来位于 [150, 20] 的原因。

## 自定义模型

鉴于基本模型和移动它们实例的能力，我们现在可以开始建模了。以下是一些示例来说明如何使用它们：

平板电脑支架：

```js
var outer = new vjgeo.models.RoundRectangle(200, 280, 8);
var inner = new vjgeo.models.RoundRectangle(160, 230, 8);
inner.origin = [20, 30];
var buttonhole = new vjgeo.paths.Circle([100, 15], 8);
var bolts = new vjgeo.models.BoltRectangle(180, 260, 2);
bolts.origin = [10, 10];
var tabletFaceMount = {
  paths: { buttonhole: buttonhole },
  models: { inner: inner, outer: outer, bolts: bolts }
};
let dwgDoc = vjgeo.exporter.toDWG(tabletFaceMount);
```
<mapFrame src="">
var outer = new vjgeo.models.RoundRectangle(200, 280, 8);
var inner = new vjgeo.models.RoundRectangle(160, 230, 8);
inner.origin = [20, 30];
var buttonhole = new vjgeo.paths.Circle([100, 15], 8);
var bolts = new vjgeo.models.BoltRectangle(180, 260, 2);
bolts.origin = [10, 10];
var tabletFaceMount = {
  paths: { buttonhole: buttonhole },
  models: { inner: inner, outer: outer, bolts: bolts }
};
let dwgDoc = vjgeo.exporter.toDWG(tabletFaceMount);
</mapFrame>

圆形转接板：

```js
var model = {
  models: {
    ring1: new vjgeo.models.Ring(40, 100),
    bc1: new vjgeo.models.BoltCircle(90, 4, 10),
    bc2: new vjgeo.models.BoltCircle(55, 7, 6, 30)
  }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422165505865](https://vjmap.com/blogimages/image-20230422165505865.png)

## 测量

要获取路径或模型的边界矩形，请使用：

- vjgeo.measure.pathExtents（路径：对象）
- vjgeo.model.modelExtents（模型：对象）

测量路径示例：

```js
var arc = new vjgeo.paths.Arc([0, 0], 100, 45, 135);
var m = vjgeo.measure.pathExtents(arc);

console.log('measurement:');
console.log(m);

var totalWidth = m.high[0] - m.low[0];
var totalHeight = m.high[1] - m.low[1];

var measureRect = new vjgeo.models.Rectangle(totalWidth, totalHeight);
measureRect.origin = m.low;

var model = {
    paths: {
        arc: arc
    },
    models: {
        measureRect: measureRect
    }
};

let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422165715082](https://vjmap.com/blogimages/image-20230422165715082.png)



## 归零和居中
要移动模型，使其下边缘和左边缘位于 x 和 y 轴上，请使用 model.zero。此函数接受 2 个布尔参数：zeroOnXAxis、zeroOnYAxis。如果不传递任何参数，它将在两个轴上归零。
```js
var model = {
  models: {
    crosshairs: {
      paths: {
        h: new vjgeo.paths.Line([-5, 0], [5, 0]),
        v: new vjgeo.paths.Line([0, -5], [0, 5])
      }
    },
    nut: {
      models: {
        polygon: new vjgeo.models.Polygon(6, 40)
      },
      paths: {
        inner: new vjgeo.paths.Circle(20)
      }
    }
  }
};
vjgeo.model.zero(model.models.nut);
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422104015846](https://vjmap.com/blogimages/image-20230422104015846.png)

要移动模型使其在 x 和 y 轴上居中，请使用model.center。此函数接受 2 个布尔参数：centerOnXAxis、centerOnYAxis。如果不传递任何参数，它将以两个轴为中心。

```js
var model = {
  models: {
    crosshairs: {
      paths: {
        h: new vjgeo.paths.Line([-5, 0], [5, 0]),
        v: new vjgeo.paths.Line([0, -5], [0, 5])
      }
    },

    box: {
      models: {
        outer: new vjgeo.models.Rectangle(60, 30),
        inner: new vjgeo.models.Oval(45, 15)
      }
    }
  }
};
var shortcut = model.models.box.models;
vjgeo.model.center(shortcut.outer);
vjgeo.model.center(shortcut.inner);
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422104256407](https://vjmap.com/blogimages/image-20230422104256407.png)

## 坐标系统一

模型中的路径相对于其父模型进行引用。有时您可能希望所有对象都在同一坐标空间内。让我们创建一个简单的演示模型：

```js
function box(origin) {
    this.models = {
        outer: new vjgeo.models.RoundRectangle(100, 100, 1)
    };
    this.paths = {
      inner: new vjgeo.paths.Circle([50, 50], 25)
    };
    this.origin = origin;
}

var box1 = new box([0, 0]);
var box2 = new box([150, 0]);

var model = {
    models: {
        box1: box1,
        box2: box2
    }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422105914067](https://vjmap.com/blogimages/image-20230422105914067.png)

在此示例中，虽然box1.paths.inner.origin和 不在同一位置，但它们都box2.paths.inner.origin具有原点，因为它们相对于包含它们的模型定位。[50, 50]为了使所有模型和路径都占据一个单一的坐标空间，我们可以使用vjgeo.model.originate：

```js
function box(origin) {
    this.models = {
        outer: new vjgeo.models.RoundRectangle(100, 100, 1)
    };
    this.paths = {
      inner: new vjgeo.paths.Circle([50, 50], 25)
    };

    this.origin = origin;
}

var box1 = new box([0, 0]);
var box2 = new box([150, 0]);

var model = {
    models: {
        box1: box1,
        box2: box2
    }
};

//move all path origins into the same space
vjgeo.model.originate(model);
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422105914067](https://vjmap.com/blogimages/image-20230422105914067.png)

## 缩放

要按比例缩放一个简单的点，请使用vjgeo.point.scale。

缩放路径示例：

```js
var model = {
  models: {
    inner: new vjgeo.models.Polygon(6, 40),
    outer: vjgeo.model.scale(new vjgeo.models.Polygon(6, 40), 1.7)
  }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422110247868](https://vjmap.com/blogimages/image-20230422110247868.png)

## 扭曲
要不成比例地缩放一个简单的点，请使用vjgeo.point.distort。

要不成比例地缩放路径，请使用 vjgeo.path.distort (path: object, scaleX: number, scaleY: number) 返回一个新对象并且不修改原始对象。返回对象的类型取决于被扭曲的路径类型：

- 一条线将返回一条线IPath对象，因为失真可以用另一条线表示。
-  圆弧将返回 BezierCurve IModel对象，因为变形不是圆形的。
 - 圆将返回一个 Ellipse IModel对象，因为变形不是圆形的。

```js
var circle = new vjgeo.paths.Circle(50);
var line = new vjgeo.paths.Line([-50,-50], [50, 50]);

var distortedLine = vjgeo.path.distort(line, 4, 1.5);

var ellipse = vjgeo.path.distort(circle, 4, 1.5);

var model = {
  paths: {
    circle: circle,
    line: line,
    distortedLine: distortedLine
  },
  models: {
    ellipse: ellipse
  }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422110503196](https://vjmap.com/blogimages/image-20230422110503196.png)

## 旋转

```js
var line1 = new vjgeo.paths.Line([0, 0], [100, 0]);
var line2 = new vjgeo.paths.Line([0, 0], [100, 0]);

var paths = [line1, vjgeo.path.rotate(line2, -30, [100, 0])];
let dwgDoc = vjgeo.exporter.toDWG(paths);
```

![image-20230422110640927](https://vjmap.com/blogimages/image-20230422110640927.png)

## 克隆
模型和路径是简单的 JavaScript 对象，因此它们很容易以 JavaScript 标准的方式克隆。vjgeo 提供了一些用于克隆的函数：

- vjgeo.cloneObject - 克隆模型或任何其他对象。
- vjgeo.path.clone - 克隆路径（比 cloneObject 更快）
- vjgeo.point.clone - 克隆一个点（比 cloneObject 更快）
克隆在许多情况下都很有用。例如，如果您需要一个模型的多个副本进行旋转：

```js
function sawtooth(numTeeth, r1, rd, offset) {
    var a = 360 / numTeeth;
    var a1 = 90 - a / 2;
    var r2 = r1 + rd;
    var p1 = vjgeo.point.fromPolar(vjgeo.angle.toRadians(a1), r1);
    var p2 = vjgeo.point.rotate(p1, a, [0, 0]);
    var p3 = [-offset, r2];
    this.paths = {
        outer: new vjgeo.paths.Arc(p1, p3, r2 / 4, false, false),
        inner: new vjgeo.paths.Arc(p2, p3, r1 / 4, false, false)
    };
}
var wheel = { models: {} };
var numTeeth = 30;
var tooth = new sawtooth(numTeeth, 100, 20, 10);
for (var i = 0; i < numTeeth; i++ ) {
    var clone = vjgeo.cloneObject(tooth);
    var a = 360 / numTeeth;
    vjgeo.model.rotate(clone, a * i, [0, 0]);
    wheel.models[i] = clone;
}
let dwgDoc = vjgeo.exporter.toDWG(wheel);
```

<mapFrame src="">
function sawtooth(numTeeth, r1, rd, offset) {
  var a = 360 / numTeeth;
  var a1 = 90 - a / 2;
  var r2 = r1 + rd;
  var p1 = vjgeo.point.fromPolar(vjgeo.angle.toRadians(a1), r1);
  var p2 = vjgeo.point.rotate(p1, a, [0, 0]);
  var p3 = [-offset, r2];
  this.paths = {
  	outer: new vjgeo.paths.Arc(p1, p3, r2 / 4, false, false),
   inner: new vjgeo.paths.Arc(p2, p3, r1 / 4, false, false)
  };
}
var wheel = { models: {} };
var numTeeth = 30;
var tooth = new sawtooth(numTeeth, 100, 20, 10);
for (var i = 0; i < numTeeth; i++ ) {
  var clone = vjgeo.cloneObject(tooth);
  var a = 360 / numTeeth;
  vjgeo.model.rotate(clone, a * i, [0, 0]);
  wheel.models[i] = clone;
}
let dwgDoc = vjgeo.exporter.toDWG(wheel);
</mapFrame>



## 镜像
使用vjgeo.angle.mirror获取角度的镜像，使用 vjgeo.point.mirror获取单点的镜像。

您可以使用以下函数创建路径和模型的镜像副本。镜像可以发生在 x 轴、y 轴或两者上。

- vjgeo.path.mirror (path: object, mirrorX: boolean, mirrorY: boolean)
- vjgeo.model.mirror（模型：对象，mirrorX：布尔值，mirrorY：布尔值）
这些函数中的每一个都返回一个新对象并且不修改原始对象。

镜像路径示例：

```js
var line1 = new vjgeo.paths.Line([0, 0], [100, 100]);
var line2 = vjgeo.path.mirror(line1, true, false);
var paths = [line1, line2];
let dwgDoc = vjgeo.exporter.toDWG(paths);
```

![image-20230422111031839](https://vjmap.com/blogimages/image-20230422111031839.png)

镜像模型示例：

```js
var ovalArc1 = new vjgeo.models.OvalArc(45, 135, 50, 10);
var model = {
    models: {
        ovalArc1: ovalArc1,
        ovalArc2: vjgeo.model.mirror(ovalArc1, false, true)
    }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422111141032](https://vjmap.com/blogimages/image-20230422111141032.png)

## 重复布局
vjgeo 提供了几个函数，可以克隆您的路径或模型并在各种布局中重复它们。
### 列
调用vjgeo.layout.cloneToColumn(path or model, count, [optional] margin)在列中重复克隆和布局。间隔将是路径或模型边界框的高度。额外的垂直边距是可选的。

```js
var dogbone = new vjgeo.models.Dogbone(50, 20, 2, -1, false);
var grooves = vjgeo.layout.cloneToColumn(dogbone, 5, 20);
let dwgDoc = vjgeo.exporter.toDWG(grooves);
```

![image-20230422111411278](https://vjmap.com/blogimages/image-20230422111411278.png)



### 行

连续 调用vjgeo.layout.cloneToRow(path or model, count, [optional] margin)重复克隆布局。间隔将是路径或模型边界框的宽度。额外的水平边距是可选的

```js
var oval = new vjgeo.models.Oval(20, 150);
var grill = vjgeo.layout.cloneToRow(oval, 12, 20);
let dwgDoc = vjgeo.exporter.toDWG(grill);
```

![image-20230422111602637](https://vjmap.com/blogimages/image-20230422111602637.png)



### 网格

```js
var roundSquare = new vjgeo.models.RoundRectangle(20, 20, 4);
var grid = vjgeo.layout.cloneToGrid(roundSquare, 11, 5, 5);
let dwgDoc = vjgeo.exporter.toDWG(grid);
```

![image-20230422111831510](https://vjmap.com/blogimages/image-20230422111831510.png)

### 砖块

```js
var brick = new vjgeo.models.Rectangle(20, 8);
var wall = vjgeo.layout.cloneToBrick(brick, 8, 7, 2);
let dwgDoc = vjgeo.exporter.toDWG(wall);
```

![image-20230422112051919](https://vjmap.com/blogimages/image-20230422112051919.png)

### 蜂窝

```js
var star = vjgeo.model.rotate(new vjgeo.models.Star(6, 50, 0, 2), 30);
var pattern = vjgeo.layout.cloneToHoneycomb(star, 8, 5, 30);
let dwgDoc = vjgeo.exporter.toDWG(pattern);
```

<mapFrame src="">
var star = vjgeo.model.rotate(new vjgeo.models.Star(6, 50, 0, 2), 30);
var pattern = vjgeo.layout.cloneToHoneycomb(star, 8, 5, 30);
let dwgDoc = vjgeo.exporter.toDWG(pattern);
</mapFrame>



### 径向

```js
var rect = vjgeo.model.move(new vjgeo.models.Rectangle(30, 10), [40, -5]);
var spinner = vjgeo.layout.cloneToRadial(rect, 16, 22.5);
let dwgDoc = vjgeo.exporter.toDWG(spinner);
```

![image-20230422112144513](https://vjmap.com/blogimages/image-20230422112144513.png)

### 路径布局

```js
var square = new vjgeo.models.Square(10);
var row = vjgeo.layout.cloneToRow(square, 10, 10);
var arc = new vjgeo.paths.Arc([0, 0], 150, 45, 135);
vjgeo.layout.childrenOnPath(row, arc);
let dwgDoc = vjgeo.exporter.toDWG(row);
```

![image-20230422124214205](https://vjmap.com/blogimages/image-20230422124214205.png)



```js
var square = new vjgeo.models.Square(10);
var row = vjgeo.layout.cloneToRow(square, 10, 10);
row.models[0].models = { triangle: new vjgeo.models.Polyline(true, [ [5, 8], [2, 2], [8, 2] ]) };
var arc = new vjgeo.paths.Arc([0, 0], 150, 45, 135);
//layout on the y-center
vjgeo.layout.childrenOnPath(row, arc, 0.5);
row.paths = { arc: arc };
let dwgDoc = vjgeo.exporter.toDWG(row);
```

![image-20230422124344022](https://vjmap.com/blogimages/image-20230422124344022.png)

如果您希望每个子项都放置在路径上但**不**旋转，请将**旋转**设置为 false

```js
var square = new vjgeo.models.Square(10);
var row = vjgeo.layout.cloneToRow(square, 10, 10);
row.models[0].models = { triangle: new vjgeo.models.Polyline(true, [ [5, 8], [2, 2], [8, 2] ]) };
var arc = new vjgeo.paths.Arc([0, 0], 150, 45, 135);
//layout on the y-center, reversed, not contained, not rotated
vjgeo.layout.childrenOnPath(row, arc, 0.5, true, false, false);
arc.layer = "red";
row.paths = { arc: arc };
let dwgDoc = vjgeo.exporter.toDWG(row);
```

![image-20230422124531817](https://vjmap.com/blogimages/image-20230422124531817.png)

## 相交点
您可以使用vjgeo.path.intersection 找到两条路径之间的交点。如果路径不相交，此函数将返回 null。否则，它将返回一个对象，该对象具有名为 intersectionPoints 的属性，该属性是一个点数组。此外，如果任一路径是圆弧或圆，则此对象将包含发生交叉点的角度。

相交点示例：

```js
var model = {
    paths: {
        line1: new vjgeo.paths.Line([0, 0], [20, 10]),
        line2: new vjgeo.paths.Line([2, 10], [50, 2])
    }
};
var int = vjgeo.path.intersection(model.paths.line1, model.paths.line2);
if (int) {
    var p = int.intersectionPoints[0];
    var id = JSON.stringify(vjgeo.point.rounded(p, 0.01));
    model.paths[id] = new vjgeo.paths.Circle(p, 1);
}
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422112346415](https://vjmap.com/blogimages/image-20230422112346415.png)

```js
var model = {
    paths: {
        circle1: new vjgeo.paths.Circle([0, 10], 20),
        circle2: new vjgeo.paths.Circle([20, 0], 20)
    }
};
var int = vjgeo.path.intersection(model.paths.circle1, model.paths.circle2);
if (int) {
    int.intersectionPoints.forEach(
        function(p, i) {
            var id = JSON.stringify(vjgeo.point.rounded(p, 0.01)) + ' intersects circle1 at ' + vjgeo.round(int.path1Angles[i], .01) + ' circle2 at ' + vjgeo.round(int.path2Angles[i], .01);
            model.paths[id] = new vjgeo.paths.Circle(p, 1);
        }
    );
}
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422112451151](https://vjmap.com/blogimages/image-20230422112451151.png)

## 相交线
要使线在斜坡交点处相交，请使用vjgeo.path.converge。此功能仅适用于直线，不适用于弧线。

converge 函数将尝试使用距离收敛点最近的线的末端。如果您需要指定应收敛线条的哪一端，请传递两个额外的布尔值。布尔值 true 表示使用线的起点，false 表示使用线的终点。

```js
var model = {
  origin: [0, 0],
  paths: {
    line1: new vjgeo.paths.Line([0, 0], [10, 5]),
    line2: new vjgeo.paths.Line([0, 10], [10, 4]),
    line3: new vjgeo.paths.Line([1, 0], [5, -2])
  }
};
var clone1 = vjgeo.cloneObject(model);
clone1.origin = [10, 0];
var clone2 = vjgeo.cloneObject(model);
clone2.origin = [20, 0];
vjgeo.path.converge(clone1.paths.line1, clone1.paths.line2);
vjgeo.path.converge(clone1.paths.line1, clone1.paths.line3);
vjgeo.path.converge(clone2.paths.line1, clone2.paths.line2, false, true);
vjgeo.path.converge(clone2.paths.line1, clone2.paths.line3, true, false);
let dwgDoc = vjgeo.exporter.toDWG({ models: { before: clone1, after: model, x: clone2 } });
```

<mapFrame src="">
var model = {
  origin: [0, 0],
  paths: {
    line1: new vjgeo.paths.Line([0, 0], [10, 5]),
    line2: new vjgeo.paths.Line([0, 10], [10, 4]),
    line3: new vjgeo.paths.Line([1, 0], [5, -2])
  }
};
var clone1 = vjgeo.cloneObject(model);
clone1.origin = [10, 0];
var clone2 = vjgeo.cloneObject(model);
clone2.origin = [20, 0];
vjgeo.path.converge(clone1.paths.line1, clone1.paths.line2);
vjgeo.path.converge(clone1.paths.line1, clone1.paths.line3);
vjgeo.path.converge(clone2.paths.line1, clone2.paths.line2, false, true);
vjgeo.path.converge(clone2.paths.line1, clone2.paths.line3, true, false);
let dwgDoc = vjgeo.exporter.toDWG({ models: { before: clone1, after: model, x: clone2 } });
</mapFrame>



## 修改模型

我们知道模型是相对简单的对象，具有众所周知的递归结构。这允许我们为不同的目的修改它们。让我们在一张图中修改和组合两个不同的模型。

对于这个例子，我们将使用椭圆来制作一个椭圆形的 L 形。我们首先创建一个具有两个椭圆的模型函数。

通过调试模型树，我们知道 Oval 是 RoundRectangle 并且圆弧的 id 是*BottomLeft*、*BottomRight*、*TopLeft*和*TopRight*。边的 id 是*Left*，*Right*，*Top*和*Bottom*。此外，我们需要注意这些线的方向，以便我们知道哪些是起点和终点。

要删除路径，我们使用 JavaScript **delete**关键字

```js
function ovalL(width, height, thickness) {
    var ovalH = new vjgeo.models.Oval(width, thickness);
    var ovalV = new vjgeo.models.Oval(thickness, height);
    delete ovalV.paths.BottomLeft;
    delete ovalV.paths.BottomRight;
    delete ovalH.paths.TopLeft;
    ovalH.paths.Top.end = 
        ovalV.paths.Right.origin = 
            [thickness, thickness];
    this.models = { h: ovalH, v: ovalV };
}

function ovalC(width, height, thickness) {
    var m = 
        this.models = 
            new ovalL(width, height, thickness).models;
    m.h2 = new vjgeo.models.Oval(width, thickness);
    m.h2.origin = [0, height - thickness];
    delete m.h2.paths.TopLeft;
    delete m.h2.paths.BottomLeft;
    delete m.v.paths.TopRight;
    
    //h2 has paths relative to h2 origin, 
    //we need to originate to share the point
    vjgeo.model.originate(this);
    
    //share the point
    m.h2.paths.Bottom.origin = 
        m.v.paths.Right.end =
            [thickness, height - thickness];
}

let dwgDoc = vjgeo.exporter.toDWG(new ovalC(100, 100, 37));
```

![image-20230422113019881](https://vjmap.com/blogimages/image-20230422113019881.png)

## 打断
如果路径上有一个点（例如，从十字路口），您可以使用vjgeo.path.breakAtPoint将路径分成两部分。这个函数会改变你传递给它的路径，所以它在那一点被打破，它会返回一个新的路径对象，这是另一个破碎的部分.

```js
var model = {
  paths: {
    arc: new vjgeo.paths.Arc([0, 0], 50, 0, 180)
  }
};

var arc2 = vjgeo.path.breakAtPoint(model.paths.arc, [0, 50]);
vjgeo.model.moveRelative(arc2, [-10, 0]);

model.paths.arc2 = arc2;
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422113208169](https://vjmap.com/blogimages/image-20230422113208169.png)



## 圆角

圆角是两条路径相交的圆角。vjgeo 提供了两种类型的圆角：传统圆角和狗骨圆角

### 传统圆角

```js
var model = {
  paths: {
    line1: new vjgeo.paths.Line([0, 20], [30, 10]),
    line2: new vjgeo.paths.Line([10, 0], [30, 10])
  }
};

//create a fillet
var arc = vjgeo.path.fillet(model.paths.line1, model.paths.line2, 2);

//add the fillet to the model
model.paths.arc = arc;

let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422113740243](https://vjmap.com/blogimages/image-20230422113740243.png)

### 狗骨圆角

```js
var dogbones = {
  models: {
    round: new vjgeo.models.Dogbone(100,50, 5, 0),
    horizontal: new vjgeo.models.Dogbone(100,50, 5, -1),
    vertical: new vjgeo.models.Dogbone(100,50, 5, 1)
  }
};

dogbones.models.horizontal.origin = [115, 0];
dogbones.models.vertical.origin = [230, 0];

let dwgDoc = vjgeo.exporter.toDWG(dogbones);
```

<mapFrame src="">
var dogbones = {
 models: {
  round: new vjgeo.models.Dogbone(100,50, 5, 0),
  horizontal: new vjgeo.models.Dogbone(100,50, 5, -1),
  vertical: new vjgeo.models.Dogbone(100,50, 5, 1)
 }
};
dogbones.models.horizontal.origin = [115, 0];
dogbones.models.vertical.origin = [230, 0];
let dwgDoc = vjgeo.exporter.toDWG(dogbones);
</mapFrame>



## 级联函数

vjgeo 提供了一种级联以面向对象的样式修改绘图的简便方法，类似jQuery库。

调用vjgeo.$(x)获取返回的级联容器对象。然后，您可以在 x 上调用一系列级联函数。每个函数的输出成为下一个函数的输入。级联容器仅适用于输出与作为第一个参数输入的对象类型相同的对象的函数，该对象必须是以下类型之一：

- Model
- Path
- Point

级联容器将具有操作容器本身的特殊属性（与操作 x 相对）。这些以 $ 为前缀：
- $initial : object获取你传入的原始 x 对象。
- $result : object获取所有级联函数调用的最终结果。
- $reset() : function() - 将容器重置为 $initial。

```js
var starburst = {};

vjgeo.$(new vjgeo.paths.Arc([5, 5], 5, 180, 270))
.addTo(starburst, 'arc1')
.mirror(true, false)
.addTo(starburst, 'arc2')
.mirror(false, true)
.addTo(starburst, 'arc3')
.mirror(true, false)
.addTo(starburst, 'arc4');

let dwgDoc = vjgeo.exporter.toDWG(starburst);
```

![image-20230422114256089](https://vjmap.com/blogimages/image-20230422114256089.png)

## 布尔运算

您可以使用vjgeo.model.combine函数组合模型，传递这些参数：
- 第一个要组合的模型，我们称之为“modelA”
- 第二个要组合的模型，我们称之为“modelB”
- 布尔值以包含模型 B 中模型 A 的路径
- 布尔值以包括模型 A 的路径，这些路径在模型 B 之外
- 布尔值以包含模型 A 中模型 B 的路径
- 布尔值以包括模型 B 在模型 A 之外的路径
- 每个模型都必须是封闭的几何体，并且不应自相交。这些示例显示了 4 个布尔参数的效果

```js
function example(origin) {
    this.models = {
        rect: new vjgeo.models.Rectangle(100, 50),
        oval: vjgeo.model.move(new vjgeo.models.Oval(100, 50), [50, 25])
    };
    this.origin = origin;
}

var examples = {
    models: {
        x1: new example([0, 0]),
        x2: new example([200, 0]),
        x3: new example([400, 0]),
        x4: new example([500, 0])
    }
};

//save us some typing :)
var x = examples.models;

vjgeo.model.combine(x.x2.models.rect, x.x2.models.oval, false, true, false, true);
vjgeo.model.combine(x.x3.models.rect, x.x3.models.oval, false, true, true, false);
vjgeo.model.combine(x.x4.models.rect, x.x4.models.oval, true, false, true, false);
let dwgDoc = vjgeo.exporter.toDWG(examples);
```

![image-20230422121618114](https://vjmap.com/blogimages/image-20230422121618114.png)

布尔标志快捷方式：
vjgeo.model.combineIntersection
vjgeo.model.combineSubtraction
vjgeo.model.combineUnion

```js
function example(origin) {
    this.models = {
        rect: new vjgeo.models.Rectangle(100, 50),
        oval: vjgeo.model.move(new vjgeo.models.Oval(100, 50), [50, 25])
    };
    this.origin = origin;
}

var examples = {
    models: {
        x1: new example([0, 0]),
        x2: new example([200, 0]),
        x3: new example([400, 0]),
        x4: new example([500, 0])
    }
};

//save us some typing :)
var x = examples.models;

vjgeo.model.combineUnion(x.x2.models.rect, x.x2.models.oval);
vjgeo.model.combineSubtraction(x.x3.models.rect, x.x3.models.oval);
vjgeo.model.combineIntersection(x.x4.models.rect, x.x4.models.oval);
let dwgDoc = vjgeo.exporter.toDWG(examples);
```
 ### 布尔运算顺序

将模型与布尔运算相结合是一项强大的功能，但在某些情况下可能具有挑战性。可能需要重新建模您的绘图才能获得某些结果。我们将通过示例项目探索操作顺序概念。

```js
var star = new vjgeo.models.Star(28, 25, 20);
var plus = vjgeo.model.rotate({
    models: {
        v: vjgeo.model.center(new vjgeo.models.Rectangle(3, 90)),
        h: vjgeo.model.center(new vjgeo.models.Rectangle(110, 3))
    }
}, -12.5);
vjgeo.model.combineUnion(plus.models.v, plus.models.h);
vjgeo.model.combineUnion(star, plus);
var starplus = {
    models: {
        star: star,
        plus: plus
    }
};
var frame = {
    models: {
        outer: vjgeo.model.center(new vjgeo.models.RoundRectangle(100, 80, 4)),
        inner: vjgeo.model.center(new vjgeo.models.Rectangle(90, 70))
    }
};
vjgeo.model.combineSubtraction(frame.models.inner, starplus); //subtract from the inner frame only
var model = {
    models: {
        starplus: starplus,
        frame: frame
    }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```


<mapFrame src="">
var star = new vjgeo.models.Star(28, 25, 20);
var plus = vjgeo.model.rotate({
    models: {
        v: vjgeo.model.center(new vjgeo.models.Rectangle(3, 90)),
        h: vjgeo.model.center(new vjgeo.models.Rectangle(110, 3))
    }
}, -12.5);
vjgeo.model.combineUnion(plus.models.v, plus.models.h);
vjgeo.model.combineUnion(star, plus);
var starplus = {
    models: {
        star: star,
        plus: plus
    }
};
var frame = {
    models: {
        outer: vjgeo.model.center(new vjgeo.models.RoundRectangle(100, 80, 4)),
        inner: vjgeo.model.center(new vjgeo.models.Rectangle(90, 70))
    }
};
vjgeo.model.combineSubtraction(frame.models.inner, starplus); //subtract from the inner frame only
var model = {
    models: {
        starplus: starplus,
        frame: frame
    }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
</mapFrame>

## 扩展路径

可以扩展路径以生成完美包围它们的封闭几何模型。

```js
var model = {
  paths: {
    p1: new vjgeo.paths.Line([0, 2], [10, 2]),
    p2: new vjgeo.paths.Circle([35, 2], 5)
  }
};

model.models = {
    x1: vjgeo.path.expand(model.paths.p1, 2),
    x2: vjgeo.path.expand(model.paths.p2, 2)
};

let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422122453673](https://vjmap.com/blogimages/image-20230422122453673.png)

### 扩展模型中的所有路径：

```js
var star = vjgeo.model.rotate(new vjgeo.models.Star(5, 100), 18);
var expanded = vjgeo.model.expandPaths(star, 10);

var model = {
    models: {
        star: star,
        outline: expanded
    }
};

let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422122619375](https://vjmap.com/blogimages/image-20230422122619375.png)

```js

this.models = {
  frame: new vjgeo.models.Polyline(true, [ [0, h], [w, 0], [0, 0] ])
};

var angled = this.models.frame.paths.ShapeLine1;

var bracepoints = [
  [0, 0],
  vjgeo.point.middle(angled, 1/3),
  [w/2 , 0],
  vjgeo.point.middle(angled, 2/3)
];

this.models.brace = new vjgeo.models.Polyline(false, bracepoints);
}

var truss = new trussWireframe(200, 50);
var expansion = vjgeo.model.expandPaths(truss, 3, 1);

let dwgDoc = vjgeo.exporter.toDWG(expansion);
```

![image-20230422122917496](https://vjmap.com/blogimages/image-20230422122917496.png)

## 路径简化

可以使用vjgeo.model.simplify来简化路径 - 但是需注意： 您的模型必须在调用 simplify 函数之前创建。 这是为了确保所有分段路径共享相同的坐标空间。

```js
function trussWireframe(w, h) {

this.models = {
  frame: new vjgeo.models.Polyline(true, [ [0, h], [w, 0], [0, 0] ])
};

var angled = this.models.frame.paths.ShapeLine1;

var bracepoints = [
  [0, 0],
  vjgeo.point.middle(angled, 1/3),
  [w/2 , 0],
  vjgeo.point.middle(angled, 2/3)
];

this.models.brace = new vjgeo.models.Polyline(false, bracepoints);
}

var truss = new trussWireframe(200, 50);
var expansion = vjgeo.model.expandPaths(truss, 3, 1);

//call originate before calling simplify:
vjgeo.model.originate(expansion);
vjgeo.model.simplify(expansion);

let dwgDoc = vjgeo.exporter.toDWG(expansion);
```

![image-20230422123236101](https://vjmap.com/blogimages/image-20230422123236101.png)



## 贝塞尔曲线

```js
	var points = [[0, 0], [50, 50], [50, -50], [100, 0]];

var curve1 = new vjgeo.models.BezierCurve(points);
curve1.origin = [0, 20];

//more accurate
var curve2 = new vjgeo.models.BezierCurve(points, 0.1);

var model = {
  models: {
    c1: curve1, c2: curve2
  }
};

let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422123355527](https://vjmap.com/blogimages/image-20230422123355527.png)

## 导入SVG

```js
var pathData = "M942.08 696.32H81.92V81.92h860.16zM122.88 655.36h778.24V122.88H122.88z M491.52 675.84h40.96v163.84h-40.96zM204.8 860.16h614.4v40.96H204.8z"
var model = vjgeo.importer.fromSVGPathData(pathData);
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422171404339](https://vjmap.com/blogimages/image-20230422171404339.png)

### 导入GeoJSON

```js
var geojson = {
    "type": "FeatureCollection",
    "features": [
        {
            "id": "1",
            "type": "Feature",
            "properties": {
                "color": "#00FFFF"
            },
            "geometry": {
                "coordinates": [
                    [
                        587680772.4127743,
                        3103807303.19205
                    ],
                    [
                        587681877.0860132,
                        3103809140.85324
                    ],
                    [
                        587670560.3140597,
                        3103815943.7041655
                    ],
                    [
                        587668980.1131245,
                        3103813314.986614
                    ],
                    [
                        587665615.6670034,
                        3103815337.456052
                    ],
                    [
                        587666093.402162,
                        3103816132.184601
                    ],
                    [
                        587651779.2146056,
                        3103824736.8719153
                    ],
                    [
                        587651338.2282269,
                        3103824003.2762012
                    ],
                    [
                        587648218.4697058,
                        3103825878.656586
                    ]
                ],
                "type": "LineString"
            }
        }
    ]
}
let model = vjgeo.importer.fromGeoJsonData(geojson)

let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422172523617](https://vjmap.com/blogimages/image-20230422172523617.png)

## 导出DWG

```
var star = new vjgeo.models.Star(8, 50, 15, 2)
let dwgDoc = vjgeo.exporter.toDWG(star);
```

![image-20230422172907802](https://vjmap.com/blogimages/image-20230422172907802.png)

## 导出GeoJSON

导出DWG时加入{isGeoJson: true}选项即可

```js
var star = new vjgeo.models.Star(8, 50, 15, 2)
let geojson = vjgeo.exporter.toDWG(star, {isGeoJson: true});
```

![image-20230422173219313](https://vjmap.com/blogimages/image-20230422173219313.png)

## 导出JSON

```js
var star = new vjgeo.models.Star(8, 50, 15, 2)
let json = JSON.stringify(star);;
console.log(json)
```



![image-20230422173355722](https://vjmap.com/blogimages/image-20230422173355722.png)

## 模型树结构

模型是一个树状结构，可能包含路径，也可能包含继承关系中的其他模型。

示例

```js
var plate = {
    models: {
        outer: vjgeo.model.center(new vjgeo.models.RoundRectangle(120, 100, 10)),
        bolts: vjgeo.model.center(new vjgeo.models.BoltRectangle(100, 80, 5))
    },
    paths: {
        hole: new vjgeo.paths.Circle(25)
    }
};
let dwgDoc = vjgeo.exporter.toDWG(plate);
```

![image-20230422124741482](https://vjmap.com/blogimages/image-20230422124741482.png)

```
plate
+-models
| +-outer
| | +-paths
| |   +-TopLeft
| |   +-Top
| |   +-TopRight
| |   +-Left
| |   +-Right
| |   +-BottomLeft
| |   +-Bottom
| |   +-BottomRight
| +-bolts
|   +-paths
|     +-TopLeft_bolt
|     +-TopRight_bolt
|     +-BottomLeft_bolt
|     +-BottomRight_bolt
+paths
 +-hole
```

从根开始，我们可以轻松地遍历树。例如，让我们更改 BottomRight_bolt 孔的半径：

```js
var plate = {
    models: {
        outer: vjgeo.model.center(new vjgeo.models.RoundRectangle(120, 100, 10)),
        bolts: vjgeo.model.center(new vjgeo.models.BoltRectangle(100, 80, 5))
    },
    paths: {
        hole: new vjgeo.paths.Circle(25)
    }
};
//change radius of BottomRight bolt hole
plate.models.bolts.paths.BottomRight_bolt.radius = 2;
let dwgDoc = vjgeo.exporter.toDWG(plate);
```

![image-20230422124936380](https://vjmap.com/blogimages/image-20230422124936380.png)

## 模型路由

我们知道我们可以使用点符号来引用深层对象：

```
var bottomRight_bolt = plate.models.bolts.paths.BottomRight_bolt;
```

`plate`从到的 引用`BottomRight_bolt`是硬编码的。假设我们有一个`plate2`重复的模型`plate`——我们需要硬编码对它的引用`BottomRight_bolt`：

```
var bottomRight_bolt2 = plate2.models.bolts.paths.BottomRight_bolt;
```

代替硬编码的点符号，我们可以有一种抽象的方式来使用**路由**来引用深层对象。它只是一个字符串数组，表示点之间的段名称。我们不将根对象放在路由中。`plate`我们可以同时应用的路线`plate2`是：

```
var route = ["models", "bolts", "paths", "BottomRight_bolt"];
```

通过路由数组键值来定位模型：

```js
var plate = {
    models: {
        outer: vjgeo.model.center(new vjgeo.models.RoundRectangle(120, 100, 10)),
        bolts: vjgeo.model.center(new vjgeo.models.BoltRectangle(100, 80, 5))
    },
    paths: {
        hole: new vjgeo.paths.Circle(25)
    }
};

var plate2 = vjgeo.cloneObject(plate);
plate2.origin = [130, 0];

//route to the BottomRight_bolt circle
var route = ["models", "bolts", "paths", "BottomRight_bolt"];

//create a local variables for BottomRight_bolt holes
var bottomRight_bolt = vjgeo.travel(plate, route).result;
bottomRight_bolt.radius = 2;

var bottomRight_bolt2 = vjgeo.travel(plate2, route).result;
bottomRight_bolt2.radius = 3;

var plates = {
    models: {
        plate: plate,
        plate2: plate2
    }
};

let dwgDoc = vjgeo.exporter.toDWG(plates);
```

![image-20230422125531861](https://vjmap.com/blogimages/image-20230422125531861.png)

### 遍历模型树

您可以通过使用您的模型和具有以下可选属性的对象 调用vjgeo.model.walk来遍历模型树：

#### walkPath 对象

walkPath 对象具有以下属性：

- **layer**：包含此路径的图层名称（如果有）。
- **modelContext**：包含此路径的模型。
- **offset**：此路径所在的 [0, 0] 的绝对坐标。
- **pathContext**：路径本身。
- **pathId**：此路径在其父 model.paths 容器中的 ID。
- **route**：属性名称数组，用于从树的根部定位此路径。
- **routeKey**：路由的字符串表示，可以安全地用作此路径的唯一密钥标识符。



#### walkModel 对象

walkModel 对象具有以下属性：

- **childId**：此模型在其父 model.models 容器中的 ID。
- **childModel**：模型本身
- **layer**：包含此路径的图层名称（如果有）。
- **offset**：此模型所在的 [0, 0] 的绝对坐标。
- **parentModel**：包含此模型的模型。
- **route**：属性名称数组，用于从树的根部定位此模型。
- **routeKey**：路由的字符串表示，可以安全地用作此模型的唯一密钥标识符。



 我们将创建一个 RoundRectangle 并遍历它的树。我们有一个`onPath`函数，它将为模型中的每条路径调用。如果路径是弧形，我们将其反转：

```js
function invertArc(arc) {
  var chord = new vjgeo.paths.Chord(arc);
  var midPoint = vjgeo.point.middle(chord);
  vjgeo.path.rotate(arc, 180, midPoint);
}

var shape = new vjgeo.models.RoundRectangle(100, 50, 10);

var walkOptions = {
  onPath: function (wp) {
    if (wp.pathContext.type === 'arc') {
      invertArc(wp.pathContext);
    }
  }
};

vjgeo.model.walk(shape, walkOptions);
let dwgDoc = vjgeo.exporter.toDWG(shape);
```

![image-20230422125919721](https://vjmap.com/blogimages/image-20230422125919721.png)

##  链条Chain

当 2 条或更多条路径首尾相接时，我们称其为**链**。这里有 3 条线首尾相连，形成一个有 3 个链接的链条；每条线路径都被视为链中的一个链接：

![image-20230422161520619](https://vjmap.com/blogimages/image-20230422161520619.png)

当链接没有任何松散的末端并相互连接时，我们称之为**循环**链条。通常，循环链被用来表示一个*封闭的几何体*。这是一条由 2 条线和一条弧线组成的循环链：

![image-20230422161542212](https://vjmap.com/blogimages/image-20230422161542212.png)

圆本质上是一个封闭的几何体。在 vjgeo 中，一个圆圈包含一条只有一个链接的循环链条。

![image-20230422161559929](https://vjmap.com/blogimages/image-20230422161559929.png)

一条链可以递归地包含其他链。如果一条链本身是一条循环链，那么它只能包含其他链。以下是一条链包含另一条链的一些示例：

![image-20230422161615707](https://vjmap.com/blogimages/image-20230422161615707.png)

**这是一个没有**任何链条的 模型。尽管线路重叠，但它们并不首尾相接。

![image-20230422161632608](https://vjmap.com/blogimages/image-20230422161632608.png)

### 查找单链

让我们从绘制矩形开始。矩形是一个模型，但我们也隐含地知道矩形由 4 条首尾相连的路径组成。让我们现在使用vjgeo.model.findSingleChain(model)找到这个链：

```js
var model = new vjgeo.models.Rectangle(100, 50);
let dwgDoc = vjgeo.exporter.toDWG(model);
//now find the chain
var chain = vjgeo.model.findSingleChain(model);
document.write('found a chain with ' + chain.links.length + ' links and endless=' + chain.endless);
```

```
found a chain with 4 links and endless=true
```

现在，让我们将两个矩形模型组合成一个并集。请注意，由于两个矩形模型是独立的，链将继续不受约束

```js
var drawing = {
    models: {
        r1: new vjgeo.models.Rectangle(100, 50),
        r2: vjgeo.model.move(new vjgeo.models.Rectangle(100, 50), [50, 25])
    }
};
vjgeo.model.combineUnion(drawing.models.r1, drawing.models.r2);
let dwgDoc = vjgeo.exporter.toDWG(drawing);

//now find the chain
var chain = vjgeo.model.findSingleChain(drawing);
document.write('found a chain with ' + chain.links.length + ' links and endless=' + chain.endless);

```

```
found a chain with 8 links and endless=true
```

### 链条关键点
调用 vjgeo.model.toKeyPoints(chain, [optional] maxArcFacet) 传递你的链，以及圆弧和圆上的最大面长度：

```js
var rect = new vjgeo.models.RoundRectangle(100, 50, 10);
var chain = vjgeo.model.findSingleChain(rect);
var keyPoints = vjgeo.chain.toKeyPoints(chain, 5);
var model = {
  models: {
    rect: rect,
    dots: new vjgeo.models.Holes(1, keyPoints)
  }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422162704231](https://vjmap.com/blogimages/image-20230422162704231.png)

要使点沿链条间隔一致，请调用 vjgeo.model.toPoints(chain, distance) 传递您的链条以及点之间的距离：

```js
var rect = new vjgeo.models.RoundRectangle(100, 50, 10);
var chain = vjgeo.model.findSingleChain(rect);
var spacing = 10;
var keyPoints = vjgeo.chain.toPoints(chain, spacing);
var model = {
  models: {
    rect: rect,
    dots: new vjgeo.models.Holes(1, keyPoints)
  }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422162811461](https://vjmap.com/blogimages/image-20230422162811461.png)

您可以使用链的**pathLength**属性来确保您的距离在整个链上平均分配：

```js
var rect = new vjgeo.models.RoundRectangle(100, 50, 10);

var chain = vjgeo.model.findSingleChain(rect);
var minimumSpacing = 10;
var divisions = Math.floor(chain.pathLength / minimumSpacing);
var spacing = chain.pathLength / divisions;

console.log(spacing);

var keyPoints = vjgeo.chain.toPoints(chain, spacing);

var model = {
  models: {
    rect: rect,
    dots: new vjgeo.models.Holes(1, keyPoints)
  }
};
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422163014215](https://vjmap.com/blogimages/image-20230422163014215.png)

### 链条圆角

通过使用以下参数调用vjgeo.chain.fillet可以在链中的所有路径之间 添加圆角：
- chainToFillet：包含将被修改为在其关节处具有圆角的路径的链。
- filletRadius：圆角的半径。
这将修改链条的所有路径以适应彼此之间的弧线，并且它将返回一个包含所有适合的圆角的新模型。这个新模型应该添加到您的树中

```js
var model = {
	paths: {
	"0": new vjgeo.paths.Line([0, 0], [100, 0]),
	"1": new vjgeo.paths.Line([100, 0], [100, 100]),
	"2": new vjgeo.paths.Line([100, 100], [200, 100])
	},

	//create a placeholder in the tree for more models
	models: {}
};
//find the chain
var chain = vjgeo.model.findSingleChain(model);
//add fillets to the chain
var filletsModel = vjgeo.chain.fillet(chain, 10);
//put the fillets in the tree
model.models.fillets = filletsModel;
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422163215981](https://vjmap.com/blogimages/image-20230422163215981.png)

### 链条布局

类似于路径布局，您可以使用链作为模型中一行子模型的布局指南。调用vjgeo.layout.childrenOnChain(parentModel: Model, onChain: chain)，x轴将投影到你的onChain上：

```js
var square = new vjgeo.models.Square(5);
var row = vjgeo.layout.cloneToRow(square, 10, 10);
var curve = new vjgeo.models.BezierCurve([0, 0], [33, 25], [66, -25], [100, 0]);
var chain = vjgeo.model.findSingleChain(curve);
vjgeo.layout.childrenOnChain(row, chain, 0.5, false, true);

var model = {
    models: {
        curve: curve,
        row: row
    }
};

curve.data.colorIndex = vjgeo.exporter.colors.red;
row.data = {
	color: 0x00FFFF
}
let dwgDoc = vjgeo.exporter.toDWG(model);
```

![image-20230422163946062](https://vjmap.com/blogimages/image-20230422163946062.png)

