7 <style type="text/css">
\r
17 <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/8.1.0/math.js"
\r
18 integrity="sha512-ne87j5uORxbrU7+bsqeJJWfWj5in65R9PCjaQL161xtH5cesZgULVbeVAkzAAN7hnYOcrHeBas9Wbd/Lm8gXFA=="
\r
19 crossorigin="anonymous"></script>
\r
20 <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
\r
21 <script type="text/javascript">
\r
45 let a = this.x - that.x;
\r
46 let b = this.y - that.y;
\r
47 return Math.sqrt(a * a + b * b)
\r
52 constructor(length, angle, minAngle, maxAngle) {
\r
53 this.length = length;
\r
55 this.minAngle = minAngle;
\r
56 this.maxAngle = maxAngle;
\r
60 if (this.angle < this.minAngle)
\r
61 this.angle = this.minAngle;
\r
62 if (this.angle > this.maxAngle)
\r
63 this.angle = this.maxAngle;
\r
68 constructor(base, segments) {
\r
70 this.segments = segments;
\r
74 let currentPoint = this.base;
\r
75 drawCircle(currentPoint, 10, '#0000FF');
\r
79 for (let i in this.segments) {
\r
80 let segment = this.segments[i];
\r
82 angleSum += segment.angle;
\r
84 let endPoint = currentPoint.add(new Point(
\r
85 Math.cos(angleSum * Math.PI / 180) * segment.length,
\r
86 Math.sin(angleSum * Math.PI / 180) * segment.length
\r
89 drawLine(currentPoint, endPoint);
\r
90 drawCircle(currentPoint, 10);
\r
92 currentPoint = endPoint;
\r
94 drawCircle(currentPoint, 10, '#FF0000');
\r
97 moveTo(targetPoint) {
\r
103 while (targetPoint.dist(this.getEndPoint()) > EPS) {
\r
104 if (++iteration > 100) break;
\r
105 let dO = this.getDeltaOrientation(targetPoint);
\r
106 for (let i in this.segments) {
\r
107 this.segments[i].angle += dO._data[i] * H;
\r
108 if (this.segments[i].angle < -360) this.segments[i].angle = this.segments[i].angle % -360;
\r
109 if (this.segments[i].angle > +360) this.segments[i].angle = this.segments[i].angle % +360;
\r
111 this.segments[i].restrictAngle();
\r
116 getDeltaOrientation(targetPoint) {
\r
117 let Jt = this.getJacobianTranspose(targetPoint);
\r
118 let V = targetPoint.sub(this.getEndPoint());
\r
119 return math.multiply(Jt, [V.x, V.y, 1]);
\r
122 getJacobianTranspose(targetPoint) {
\r
124 for (let i in this.segments) {
\r
125 let Ra = [0, 0, 1];
\r
126 let E = this.getEndPoint();
\r
127 let A = this.getSegmentEndpoint(i);
\r
129 let diff = [E.x - A.x, E.y - A.y, 0];
\r
131 let row = math.cross(Ra, diff);
\r
134 let transpose = math.matrix(rows);
\r
135 //let jacobi = math.transpose(transpose);
\r
136 //let pseudo = math.multiply(math.inv(math.multiply(transpose, jacobi)), transpose);
\r
142 return this.getSegmentEndpoint(this.segments.length);
\r
145 getSegmentEndpoint(index) {
\r
146 let currentPoint = this.base;
\r
149 for (let i in this.segments) {
\r
150 if (index == i) break;
\r
152 let segment = this.segments[i];
\r
154 angleSum += segment.angle;
\r
156 let endPoint = currentPoint.add(new Point(
\r
157 Math.cos(angleSum * Math.PI / 180) * segment.length,
\r
158 Math.sin(angleSum * Math.PI / 180) * segment.length
\r
161 currentPoint = endPoint;
\r
164 return currentPoint;
\r
168 let mousePos = new Point(0, 0);
\r
171 new Point(window.innerWidth / 2, window.innerHeight / 2),
\r
175 for (let i = 0; i < 3; i++)
\r
178 function draw(dt) {
\r
179 ctx.clearRect(0, 0, w, h);
\r
181 arm.moveTo(mousePos);
\r
184 window.requestAnimationFrame(draw);
\r
187 function drawCircle(pos, radius, color) {
\r
188 let filled = false;
\r
191 ctx.fillStyle = color;
\r
195 ctx.arc(pos.x, pos.y, radius, 0, 2 * Math.PI);
\r
203 function drawLine(from, to) {
\r
205 ctx.moveTo(from.x, from.y);
\r
206 ctx.lineTo(to.x, to.y);
\r
211 let canvas = document.getElementById('canvas');
\r
212 let resize = function () {
\r
213 w = window.innerWidth;
\r
214 h = window.innerHeight - 200;
\r
217 document.getElementById('inputBaseX').setAttribute('max', w);
\r
218 document.getElementById('inputBaseY').setAttribute('max', h);
\r
220 window.onresize = resize;
\r
222 if (canvas.getContext) {
\r
223 ctx = canvas.getContext('2d');
\r
224 draw(performance.now());
\r
226 canvas.onmousemove = (evt) => {
\r
227 mousePos.x = evt.clientX;
\r
228 mousePos.y = evt.clientY;
\r
232 function AddSegment() {
\r
233 arm.segments.push(new Segment(100, 0, -360, 360));
\r
238 <body onload="init();">
\r
239 <canvas id="canvas"></canvas>
\r
242 <input v-model.number="arm.base.x" type="range" min="0" id="inputBaseX" style="width: 100px;" />
\r
244 <input v-model.number="arm.base.y" type="range" min="0" id="inputBaseY" style="width: 100px;" />
\r
246 <table style="width: 100%;">
\r
248 <col span="1" style="width: 20%;">
\r
249 <col span="1" style="width: 20%;">
\r
250 <col span="1" style="width: 20%;">
\r
251 <col span="1" style="width: 20%;">
\r
252 <col span="1" style="width: 20%;">
\r
256 <th>Min. Angle</th>
\r
257 <th>Max. Angle</th>
\r
260 <tr v-for="segment in arm.segments">
\r
261 <td><input v-model.number="segment.length" type="range" max="300" /></td>
\r
262 <td><input v-model.number="segment.minAngle" min="-360" max="0" type="range" /></td>
\r
263 <td><input v-model.number="segment.maxAngle" min="0" max="360" type="range" /></td>
\r
264 <td>{{ Number(segment.angle).toFixed(4) }}</td>
\r
265 <td><button v-on:click="arm.segments.splice(arm.segments.indexOf(segment), 1)"
\r
266 v-if="arm.segments.length > 1">Delete</button></td>
\r
269 <button onclick="AddSegment()">Add</button>
\r
272 var app = new Vue({
\r