1 // --------------------------------------------------------------------
2 // ipeluageo.cpp
3 // --------------------------------------------------------------------
4 /*
5
6 This file is part of the extensible drawing editor Ipe.
7 Copyright (c) 1993-2020 Otfried Cheong
8
9 Ipe is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
13
14 As a special exception, you have permission to link Ipe with the
15 CGAL library and distribute executables, as long as you follow the
16 requirements of the Gnu General Public License in regard to all of
17 the software in the executable aside from CGAL.
18
19 Ipe is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
22 License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with Ipe; if not, you can find it at
26 "http://www.gnu.org/copyleft/gpl.html", or write to the Free
27 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28
29 */
30
31 #include "ipelua.h"
32
33 #include <cstring>
34
35 using namespace ipe;
36 using namespace ipelua;
37
38 // --------------------------------------------------------------------
39
push_vector(lua_State * L,const Vector & v0)40 void ipelua::push_vector(lua_State *L, const Vector &v0)
41 {
42 Vector *v = (Vector *) lua_newuserdata(L, sizeof(Vector));
43 luaL_getmetatable(L, "Ipe.vector");
44 lua_setmetatable(L, -2);
45 new (v) Vector(v0);
46 }
47
vector_constructor(lua_State * L)48 int ipelua::vector_constructor(lua_State *L)
49 {
50 if (lua_gettop(L) == 0)
51 push_vector(L, Vector::ZERO);
52 else
53 push_vector(L, Vector(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
54 return 1;
55 }
56
direction_constructor(lua_State * L)57 int ipelua::direction_constructor(lua_State *L)
58 {
59 Angle alpha(luaL_checknumber(L, 1));
60 push_vector(L, Vector(alpha));
61 return 1;
62 }
63
vector_get(lua_State * L)64 static int vector_get(lua_State *L)
65 {
66 Vector *v = check_vector(L, 1);
67 const char *key = lua_tolstring(L, 2, nullptr);
68 if (!strcmp(key, "x"))
69 lua_pushnumber(L, v->x);
70 else if (!strcmp(key, "y"))
71 lua_pushnumber(L, v->y);
72 else if (!luaL_getmetafield(L, 1, key))
73 lua_pushnil(L);
74 return 1;
75 }
76
vector_tostring(lua_State * L)77 static int vector_tostring(lua_State *L)
78 {
79 Vector *v = check_vector(L, 1);
80 lua_pushfstring(L, "(%f, %f)", v->x, v->y);
81 return 1;
82 }
83
vector_add(lua_State * L)84 static int vector_add(lua_State *L)
85 {
86 Vector *v1 = check_vector(L, 1);
87 Vector *v2 = check_vector(L, 2);
88 push_vector(L, *v1 + *v2);
89 return 1;
90 }
91
vector_unm(lua_State * L)92 static int vector_unm(lua_State *L)
93 {
94 Vector *v = check_vector(L, 1);
95 push_vector(L, Vector(-v->x, -v->y));
96 return 1;
97 }
98
vector_sub(lua_State * L)99 static int vector_sub(lua_State *L)
100 {
101 Vector *v1 = check_vector(L, 1);
102 Vector *v2 = check_vector(L, 2);
103 push_vector(L, *v1 - *v2);
104 return 1;
105 }
106
vector_eq(lua_State * L)107 static int vector_eq(lua_State *L)
108 {
109 Vector *v1 = check_vector(L, 1);
110 Vector *v2 = check_vector(L, 2);
111 lua_pushboolean(L, *v1 == *v2);
112 return 1;
113 }
114
vector_dot(lua_State * L)115 static int vector_dot(lua_State *L)
116 {
117 Vector *v1 = check_vector(L, 1);
118 Vector *v2 = check_vector(L, 2);
119 lua_pushnumber(L, dot(*v1, *v2));
120 return 1;
121 }
122
vector_mul(lua_State * L)123 static int vector_mul(lua_State *L)
124 {
125 if (lua_type(L, 1) == LUA_TNUMBER) {
126 double scalar = luaL_checknumber(L, 1);
127 Vector *v = check_vector(L, 2);
128 push_vector(L, scalar * *v);
129 } else {
130 Vector *v = check_vector(L, 1);
131 double scalar = luaL_checknumber(L, 2);
132 push_vector(L, *v * scalar);
133 }
134 return 1;
135 }
136
vector_len(lua_State * L)137 static int vector_len(lua_State *L)
138 {
139 Vector *v = check_vector(L, 1);
140 lua_pushnumber(L, v->len());
141 return 1;
142 }
143
vector_sqLen(lua_State * L)144 static int vector_sqLen(lua_State *L)
145 {
146 Vector *v = check_vector(L, 1);
147 lua_pushnumber(L, v->sqLen());
148 return 1;
149 }
150
vector_normalized(lua_State * L)151 static int vector_normalized(lua_State *L)
152 {
153 Vector *v = check_vector(L, 1);
154 push_vector(L, v->normalized());
155 return 1;
156 }
157
vector_orthogonal(lua_State * L)158 static int vector_orthogonal(lua_State *L)
159 {
160 Vector *v = check_vector(L, 1);
161 push_vector(L, v->orthogonal());
162 return 1;
163 }
164
vector_factorize(lua_State * L)165 static int vector_factorize(lua_State *L)
166 {
167 Vector *v = check_vector(L, 1);
168 Vector *unit = check_vector(L, 2);
169 lua_pushnumber(L, v->factorize(*unit));
170 return 1;
171 }
172
vector_angle(lua_State * L)173 static int vector_angle(lua_State *L)
174 {
175 Vector *v = check_vector(L, 1);
176 lua_pushnumber(L, double(v->angle()));
177 return 1;
178 }
179
180 static const struct luaL_Reg vector_methods[] = {
181 { "__index", vector_get },
182 { "__tostring", vector_tostring },
183 { "__add", vector_add },
184 { "__unm", vector_unm },
185 { "__sub", vector_sub },
186 { "__eq", vector_eq },
187 { "__mul", vector_mul },
188 { "__concat", vector_dot }, // for historical reasons
189 { "__pow", vector_dot },
190 { "len", vector_len },
191 { "sqLen", vector_sqLen },
192 { "normalized", vector_normalized },
193 { "orthogonal", vector_orthogonal },
194 { "factorize", vector_factorize },
195 { "angle", vector_angle },
196 { nullptr, nullptr },
197 };
198
199 // --------------------------------------------------------------------
200
push_matrix(lua_State * L,const Matrix & m0)201 void ipelua::push_matrix(lua_State *L, const Matrix &m0)
202 {
203 Matrix *m = (Matrix *) lua_newuserdata(L, sizeof(Matrix));
204 luaL_getmetatable(L, "Ipe.matrix");
205 lua_setmetatable(L, -2);
206 new (m) Matrix(m0);
207 }
208
matrix_constructor(lua_State * L)209 int ipelua::matrix_constructor(lua_State *L)
210 {
211 int top = lua_gettop(L);
212 if (top == 0)
213 push_matrix(L, Matrix());
214 else if (top == 4 || top == 6) {
215 double a[6];
216 a[4] = a[5] = 0.0;
217 for (int i = 0; i < top; ++i)
218 a[i] = luaL_checknumber(L, i+1);
219 push_matrix(L, Matrix(a[0], a[1], a[2], a[3], a[4], a[5]));
220 } else if (top == 1 && lua_type(L, 1) == LUA_TTABLE) {
221 double a[6];
222 for (int i = 0; i < 6; ++i) {
223 lua_rawgeti(L, 1, i+1);
224 a[i] = luaL_checknumber(L, -1);
225 lua_pop(L, 1);
226 }
227 push_matrix(L, Matrix(a[0], a[1], a[2], a[3], a[4], a[5]));
228 } else
229 luaL_error(L, "incorrect arguments for constructor");
230 return 1;
231 }
232
rotation_constructor(lua_State * L)233 int ipelua::rotation_constructor(lua_State *L)
234 {
235 double alpha = luaL_checknumber(L, 1);
236 push_matrix(L, Matrix(Linear(Angle(alpha))));
237 return 1;
238 }
239
translation_constructor(lua_State * L)240 int ipelua::translation_constructor(lua_State *L)
241 {
242 if (lua_gettop(L) == 1) {
243 Vector *v = check_vector(L, 1);
244 push_matrix(L, Matrix(*v));
245 } else {
246 double x = luaL_checknumber(L, 1);
247 double y = luaL_checknumber(L, 2);
248 push_matrix(L, Matrix(Vector(x, y)));
249 }
250 return 1;
251 }
252
matrix_tostring(lua_State * L)253 static int matrix_tostring(lua_State *L)
254 {
255 Matrix *m = check_matrix(L, 1);
256 lua_pushfstring(L, "[%f %f %f %f %f %f]",
257 m->a[0], m->a[1], m->a[2], m->a[3],
258 m->a[4], m->a[5]);
259 return 1;
260 }
261
matrix_eq(lua_State * L)262 static int matrix_eq(lua_State *L)
263 {
264 Matrix *m1 = check_matrix(L, 1);
265 Matrix *m2 = check_matrix(L, 2);
266 lua_pushboolean(L, *m1 == *m2);
267 return 1;
268 }
269
matrix_coeff(lua_State * L)270 static int matrix_coeff(lua_State *L)
271 {
272 Matrix *m = check_matrix(L, 1);
273 lua_newtable(L);
274 for (int i = 0; i < 6; ++i) {
275 lua_pushnumber(L, m->a[i]);
276 lua_rawseti(L, -2, i+1);
277 }
278 return 1;
279 }
280
matrix_isIdentity(lua_State * L)281 static int matrix_isIdentity(lua_State *L)
282 {
283 Matrix *m = check_matrix(L, 1);
284 lua_pushboolean(L, m->isIdentity());
285 return 1;
286 }
287
matrix_isSingular(lua_State * L)288 static int matrix_isSingular(lua_State *L)
289 {
290 Matrix *m = check_matrix(L, 1);
291 double t = m->a[0]*m->a[3]-m->a[1]*m->a[2];
292 lua_pushboolean(L, t == 0);
293 return 1;
294 }
295
matrix_inverse(lua_State * L)296 static int matrix_inverse(lua_State *L)
297 {
298 Matrix *m = check_matrix(L, 1);
299 double t = m->a[0]*m->a[3]-m->a[1]*m->a[2];
300 luaL_argcheck(L, t != 0, 1, "matrix is singular");
301 push_matrix(L, m->inverse());
302 return 1;
303 }
304
matrix_translation(lua_State * L)305 static int matrix_translation(lua_State *L)
306 {
307 Matrix *m = check_matrix(L, 1);
308 push_vector(L, m->translation());
309 return 1;
310 }
311
matrix_linear(lua_State * L)312 static int matrix_linear(lua_State *L)
313 {
314 Matrix *m = check_matrix(L, 1);
315 push_matrix(L, Matrix(m->linear()));
316 return 1;
317 }
318
matrix_elements(lua_State * L)319 static int matrix_elements(lua_State *L)
320 {
321 Matrix *m = check_matrix(L, 1);
322 lua_createtable(L, 6, 0);
323 for (int i = 0; i < 6; ++i) {
324 lua_pushnumber(L, m->a[i]);
325 lua_rawseti(L, -2, i+1);
326 }
327 return 1;
328 }
329
matrix_mul(lua_State * L)330 static int matrix_mul(lua_State *L)
331 {
332 Matrix *lhs = check_matrix(L, 1);
333 if (is_type(L, 2, "Ipe.matrix")) {
334 Matrix *rhs = check_matrix(L, 2);
335 push_matrix(L, *lhs * *rhs);
336 } else if (is_type(L, 2, "Ipe.arc")) {
337 Arc *rhs = check_arc(L, 2);
338 push_arc(L, *lhs * *rhs);
339 } else {
340 Vector *v = check_vector(L, 2);
341 push_vector(L, *lhs * *v);
342 }
343 return 1;
344 }
345
346 static const struct luaL_Reg matrix_methods[] = {
347 { "__tostring", matrix_tostring },
348 { "__eq", matrix_eq },
349 { "coeff", matrix_coeff },
350 { "isIdentity", matrix_isIdentity },
351 { "linear", matrix_linear },
352 { "translation", matrix_translation },
353 { "__mul", matrix_mul },
354 { "isSingular", matrix_isSingular },
355 { "inverse", matrix_inverse },
356 { "elements", matrix_elements },
357 { nullptr, nullptr }
358 };
359
360 // --------------------------------------------------------------------
361
push_rect(lua_State * L,const Rect & r0)362 void ipelua::push_rect(lua_State *L, const Rect &r0)
363 {
364 Rect *r = (Rect *) lua_newuserdata(L, sizeof(Rect));
365 luaL_getmetatable(L, "Ipe.rect");
366 lua_setmetatable(L, -2);
367 new (r) Rect(r0);
368 }
369
rect_constructor(lua_State * L)370 int ipelua::rect_constructor(lua_State *L)
371 {
372 push_rect(L, Rect());
373 return 1;
374 }
375
rect_tostring(lua_State * L)376 static int rect_tostring(lua_State *L)
377 {
378 Rect *r = check_rect(L, 1);
379 lua_pushfstring(L, "Rect(%f,%f,%f,%f)",
380 r->bottomLeft().x, r->bottomLeft().y,
381 r->topRight().x, r->topRight().y);
382 return 1;
383 }
384
rect_isEmpty(lua_State * L)385 static int rect_isEmpty(lua_State *L)
386 {
387 Rect *r = check_rect(L, 1);
388 lua_pushboolean(L, r->isEmpty());
389 return 1;
390 }
391
rect_topRight(lua_State * L)392 static int rect_topRight(lua_State *L)
393 {
394 Rect *r = check_rect(L, 1);
395 push_vector(L, r->topRight());
396 return 1;
397 }
398
rect_bottomLeft(lua_State * L)399 static int rect_bottomLeft(lua_State *L)
400 {
401 Rect *r = check_rect(L, 1);
402 push_vector(L, r->bottomLeft());
403 return 1;
404 }
405
rect_topLeft(lua_State * L)406 static int rect_topLeft(lua_State *L)
407 {
408 Rect *r = check_rect(L, 1);
409 push_vector(L, r->topLeft());
410 return 1;
411 }
412
rect_bottomRight(lua_State * L)413 static int rect_bottomRight(lua_State *L)
414 {
415 Rect *r = check_rect(L, 1);
416 push_vector(L, r->bottomLeft());
417 return 1;
418 }
419
rect_left(lua_State * L)420 static int rect_left(lua_State *L)
421 {
422 Rect *r = check_rect(L, 1);
423 lua_pushnumber(L, r->left());
424 return 1;
425 }
426
rect_right(lua_State * L)427 static int rect_right(lua_State *L)
428 {
429 Rect *r = check_rect(L, 1);
430 lua_pushnumber(L, r->right());
431 return 1;
432 }
433
rect_bottom(lua_State * L)434 static int rect_bottom(lua_State *L)
435 {
436 Rect *r = check_rect(L, 1);
437 lua_pushnumber(L, r->bottom());
438 return 1;
439 }
440
rect_top(lua_State * L)441 static int rect_top(lua_State *L)
442 {
443 Rect *r = check_rect(L, 1);
444 lua_pushnumber(L, r->top());
445 return 1;
446 }
447
rect_width(lua_State * L)448 static int rect_width(lua_State *L)
449 {
450 Rect *r = check_rect(L, 1);
451 lua_pushnumber(L, r->width());
452 return 1;
453 }
454
rect_height(lua_State * L)455 static int rect_height(lua_State *L)
456 {
457 Rect *r = check_rect(L, 1);
458 lua_pushnumber(L, r->height());
459 return 1;
460 }
461
rect_add(lua_State * L)462 static int rect_add(lua_State *L)
463 {
464 Rect *r = check_rect(L, 1);
465 if (is_type(L, 2, "Ipe.vector"))
466 r->addPoint(*check_vector(L, 2));
467 else
468 r->addRect(*check_rect(L, 2));
469 return 0;
470 }
471
rect_clipTo(lua_State * L)472 static int rect_clipTo(lua_State *L)
473 {
474 Rect *r1 = check_rect(L, 1);
475 Rect *r2 = check_rect(L, 2);
476 r1->clipTo(*r2);
477 return 0;
478 }
479
rect_contains(lua_State * L)480 static int rect_contains(lua_State *L)
481 {
482 Rect *r = check_rect(L, 1);
483 if (is_type(L, 2, "Ipe.vector"))
484 lua_pushboolean(L, r->contains(*check_vector(L, 2)));
485 else
486 lua_pushboolean(L, r->contains(*check_rect(L, 2)));
487 return 1;
488 }
489
rect_intersects(lua_State * L)490 static int rect_intersects(lua_State *L)
491 {
492 Rect *r1 = check_rect(L, 1);
493 Rect *r2 = check_rect(L, 2);
494 lua_pushboolean(L, r1->intersects(*r2));
495 return 1;
496 }
497
498 static const struct luaL_Reg rect_methods[] = {
499 { "__tostring", rect_tostring },
500 { "isEmpty", rect_isEmpty },
501 { "topRight", rect_topRight },
502 { "bottomLeft", rect_bottomLeft },
503 { "topLeft", rect_topLeft },
504 { "bottomRight", rect_bottomRight },
505 { "left", rect_left },
506 { "right", rect_right },
507 { "bottom", rect_bottom },
508 { "top", rect_top },
509 { "width", rect_width },
510 { "height", rect_height },
511 { "add", rect_add },
512 { "clipTo", rect_clipTo },
513 { "contains", rect_contains },
514 { "intersects", rect_intersects },
515 { nullptr, nullptr }
516 };
517
518 // --------------------------------------------------------------------
519
push_line(lua_State * L,const Line & l0)520 void ipelua::push_line(lua_State *L, const Line &l0)
521 {
522 Line *l = (Line *) lua_newuserdata(L, sizeof(Line));
523 luaL_getmetatable(L, "Ipe.line");
524 lua_setmetatable(L, -2);
525 new (l) Line(l0);
526 }
527
line_constructor(lua_State * L)528 int ipelua::line_constructor(lua_State *L)
529 {
530 Vector *p = check_vector(L, 1);
531 Vector *dir = check_vector(L, 2);
532 push_line(L, Line(*p, *dir));
533 return 1;
534 }
535
line_through(lua_State * L)536 int ipelua::line_through(lua_State *L)
537 {
538 Vector *p = check_vector(L, 1);
539 Vector *q = check_vector(L, 2);
540 push_line(L, Line::through(*p, *q));
541 return 1;
542 }
543
line_bisector(lua_State * L)544 int ipelua::line_bisector(lua_State *L)
545 {
546 Vector *p = check_vector(L, 1);
547 Vector *q = check_vector(L, 2);
548 luaL_argcheck(L, *p != *q, 2, "points are not distinct");
549 Vector mid = 0.5 * (*p + *q);
550 Vector dir = (*p - *q).normalized().orthogonal();
551 push_line(L, Line(mid, dir));
552 return 1;
553 }
554
line_tostring(lua_State * L)555 static int line_tostring(lua_State *L)
556 {
557 Line *l = check_line(L, 1);
558 lua_pushfstring(L, "Line[(%f,%f)->(%f,%f)]",
559 l->iP.x, l->iP.y, l->dir().x, l->dir().y);
560 return 1;
561 }
562
line_side(lua_State * L)563 static int line_side(lua_State *L)
564 {
565 Line *l = check_line(L, 1);
566 Vector *p = check_vector(L, 2);
567 double s = l->side(*p);
568 if (s > 0.0)
569 lua_pushnumber(L, 1.0);
570 else if (s < 0.0)
571 lua_pushnumber(L, -1.0);
572 else
573 lua_pushnumber(L, 0.0);
574 return 1;
575 }
576
line_point(lua_State * L)577 static int line_point(lua_State *L)
578 {
579 Line *l = check_line(L, 1);
580 push_vector(L, l->iP);
581 return 1;
582 }
583
line_dir(lua_State * L)584 static int line_dir(lua_State *L)
585 {
586 Line *l = check_line(L, 1);
587 push_vector(L, l->dir());
588 return 1;
589 }
590
line_normal(lua_State * L)591 static int line_normal(lua_State *L)
592 {
593 Line *l = check_line(L, 1);
594 push_vector(L, l->normal());
595 return 1;
596 }
597
line_distance(lua_State * L)598 static int line_distance(lua_State *L)
599 {
600 Line *l = check_line(L, 1);
601 Vector *v = check_vector(L, 2);
602 lua_pushnumber(L, l->distance(*v));
603 return 1;
604 }
605
line_intersects(lua_State * L)606 static int line_intersects(lua_State *L)
607 {
608 Line *l1 = check_line(L, 1);
609 Line *l2 = check_line(L, 2);
610 Vector pt;
611 if (l1->intersects(*l2, pt))
612 push_vector(L, pt);
613 else
614 lua_pushnil(L);
615 return 1;
616 }
617
line_project(lua_State * L)618 static int line_project(lua_State *L)
619 {
620 Line *l = check_line(L, 1);
621 Vector *v = check_vector(L, 2);
622 push_vector(L, l->project(*v));
623 return 1;
624 }
625
626 static const struct luaL_Reg line_methods[] = {
627 { "__tostring", line_tostring },
628 { "side", line_side },
629 { "point", line_point },
630 { "dir", line_dir },
631 { "normal", line_normal },
632 { "distance", line_distance },
633 { "intersects", line_intersects },
634 { "project", line_project },
635 { nullptr, nullptr }
636 };
637
638 // --------------------------------------------------------------------
639
push_segment(lua_State * L,const Segment & s0)640 void ipelua::push_segment(lua_State *L, const Segment &s0)
641 {
642 Segment *s = (Segment *) lua_newuserdata(L, sizeof(Segment));
643 luaL_getmetatable(L, "Ipe.segment");
644 lua_setmetatable(L, -2);
645 new (s) Segment(s0);
646 }
647
segment_constructor(lua_State * L)648 int ipelua::segment_constructor(lua_State *L)
649 {
650 Vector *p = check_vector(L, 1);
651 Vector *q = check_vector(L, 2);
652 push_segment(L, Segment(*p, *q));
653 return 1;
654 }
655
segment_tostring(lua_State * L)656 static int segment_tostring(lua_State *L)
657 {
658 Segment *s = check_segment(L, 1);
659 lua_pushfstring(L, "Segment[(%f,%f)-(%f,%f)]",
660 s->iP.x, s->iP.y, s->iQ.x, s->iQ.y);
661 return 1;
662 }
663
segment_endpoints(lua_State * L)664 static int segment_endpoints(lua_State *L)
665 {
666 Segment *s = check_segment(L, 1);
667 push_vector(L, s->iP);
668 push_vector(L, s->iQ);
669 return 2;
670 }
671
segment_line(lua_State * L)672 static int segment_line(lua_State *L)
673 {
674 Segment *s = check_segment(L, 1);
675 push_line(L, s->line());
676 return 1;
677 }
678
segment_project(lua_State * L)679 static int segment_project(lua_State *L)
680 {
681 Segment *s = check_segment(L, 1);
682 Vector *v = check_vector(L, 2);
683 Vector pt;
684 if (s->project(*v, pt))
685 push_vector(L, pt);
686 else
687 lua_pushnil(L);
688 return 1;
689 }
690
segment_distance(lua_State * L)691 static int segment_distance(lua_State *L)
692 {
693 Segment *s = check_segment(L, 1);
694 Vector *v = check_vector(L, 2);
695 lua_pushnumber(L, s->distance(*v));
696 return 1;
697 }
698
segment_intersects(lua_State * L)699 static int segment_intersects(lua_State *L)
700 {
701 Segment *s = check_segment(L, 1);
702 Vector pt;
703 if (is_type(L, 2, "Ipe.segment")) {
704 Segment *rhs = check_segment(L, 2);
705 if (s->intersects(*rhs, pt))
706 push_vector(L, pt);
707 else
708 lua_pushnil(L);
709 } else {
710 Line *rhs = check_line(L, 2);
711 if (s->intersects(*rhs, pt))
712 push_vector(L, pt);
713 else
714 lua_pushnil(L);
715 }
716 return 1;
717 }
718
719 static const struct luaL_Reg segment_methods[] = {
720 { "__tostring", segment_tostring },
721 { "endpoints", segment_endpoints },
722 { "line", segment_line },
723 { "project", segment_project },
724 { "distance", segment_distance },
725 { "intersects", segment_intersects },
726 { nullptr, nullptr }
727 };
728
729 // --------------------------------------------------------------------
730
push_bezier(lua_State * L,const Bezier & b0)731 void ipelua::push_bezier(lua_State *L, const Bezier &b0)
732 {
733 Bezier *b = (Bezier *) lua_newuserdata(L, sizeof(Bezier));
734 luaL_getmetatable(L, "Ipe.bezier");
735 lua_setmetatable(L, -2);
736 new (b) Bezier(b0);
737 }
738
bezier_constructor(lua_State * L)739 int ipelua::bezier_constructor(lua_State *L)
740 {
741 Vector *p[4];
742 for (int i = 0; i < 4; ++i)
743 p[i] = check_vector(L, i + 1);
744 push_bezier(L, Bezier(*p[0], *p[1], *p[2], *p[3]));
745 return 1;
746 }
747
quad_constructor(lua_State * L)748 int ipelua::quad_constructor(lua_State *L)
749 {
750 Vector *p[3];
751 for (int i = 0; i < 3; ++i)
752 p[i] = check_vector(L, i + 1);
753 push_bezier(L, Bezier::quadBezier(*p[0], *p[1], *p[2]));
754 return 1;
755 }
756
bezier_tostring(lua_State * L)757 static int bezier_tostring(lua_State *L)
758 {
759 check_bezier(L, 1);
760 lua_pushfstring(L, "Bezier@%p", lua_topointer(L, 1));
761 return 1;
762 }
763
bezier_controlpoints(lua_State * L)764 static int bezier_controlpoints(lua_State *L)
765 {
766 Bezier *b = check_bezier(L, 1);
767 for (int i = 0; i < 4; ++i)
768 push_vector(L, b->iV[i]);
769 return 4;
770 }
771
bezier_point(lua_State * L)772 static int bezier_point(lua_State *L)
773 {
774 Bezier *b = check_bezier(L, 1);
775 double t = luaL_checknumber(L, 2);
776 push_vector(L, b->point(t));
777 return 1;
778 }
779
bezier_bbox(lua_State * L)780 static int bezier_bbox(lua_State *L)
781 {
782 Bezier *b = check_bezier(L, 1);
783 push_rect(L, b->bbox());
784 return 1;
785 }
786
bezier_intersect(lua_State * L)787 static int bezier_intersect(lua_State *L)
788 {
789 Bezier *b = check_bezier(L, 1);
790 std::vector<Vector> pts;
791 if (is_type(L, 2, "Ipe.segment")) {
792 Segment *rhs = check_segment(L, 2);
793 b->intersect(*rhs, pts);
794 } else if (is_type(L, 2, "Ipe.line")) {
795 Line *rhs = check_line(L, 2);
796 b->intersect(*rhs, pts);
797 } else if (is_type(L, 2, "Ipe.bezier")) {
798 Bezier *rhs = check_bezier(L, 2);
799 b->intersect(*rhs, pts);
800 }
801 lua_createtable(L, pts.size(), 0);
802 for (int i = 0; i < int(pts.size()); ++i) {
803 push_vector(L, pts[i]);
804 lua_rawseti(L, -2, i+1);
805 }
806 return 1;
807 }
808
bezier_snap(lua_State * L)809 static int bezier_snap(lua_State *L)
810 {
811 Bezier *b = check_bezier(L, 1);
812 Vector *v = check_vector(L, 2);
813 double t;
814 Vector pos;
815 double bound = 10e9;
816 if (b->snap(*v, t, pos, bound)) {
817 lua_pushnumber(L, t);
818 push_vector(L, pos);
819 return 2;
820 } else
821 return 0;
822 }
823
824 static const struct luaL_Reg bezier_methods[] = {
825 { "__tostring", bezier_tostring },
826 { "controlpoints", bezier_controlpoints },
827 { "point", bezier_point },
828 { "bbox", bezier_bbox },
829 { "intersect", bezier_intersect },
830 { "snap", bezier_snap },
831 { nullptr, nullptr }
832 };
833
834 // --------------------------------------------------------------------
835
836 /*
837 inline Arc(const Matrix &m, Angle alpha, Angle beta);
838 Arc(const Matrix &m0, const Vector &begp, const Vector &endp);
839 */
840
push_arc(lua_State * L,const Arc & a0)841 void ipelua::push_arc(lua_State *L, const Arc &a0)
842 {
843 Arc *a = (Arc *) lua_newuserdata(L, sizeof(Arc));
844 luaL_getmetatable(L, "Ipe.arc");
845 lua_setmetatable(L, -2);
846 new (a) Arc(a0);
847 }
848
arc_constructor(lua_State * L)849 int ipelua::arc_constructor(lua_State *L)
850 {
851 Matrix *m = check_matrix(L, 1);
852 if (lua_gettop(L) == 1) {
853 push_arc(L, Arc(*m));
854 } else if (is_type(L, 2, "Ipe.vector")) {
855 Vector *v1 = check_vector(L, 2);
856 Vector *v2 = check_vector(L, 3);
857 push_arc(L, Arc(*m, *v1, *v2));
858 } else {
859 double alpha = luaL_checknumber(L, 2);
860 double beta = luaL_checknumber(L, 3);
861 push_arc(L, Arc(*m, Angle(alpha), Angle(beta)));
862 }
863 return 1;
864 }
865
arc_tostring(lua_State * L)866 static int arc_tostring(lua_State *L)
867 {
868 (void) check_arc(L, 1);
869 lua_pushfstring(L, "Arc@%p", lua_topointer(L, 1));
870 return 1;
871 }
872
arc_endpoints(lua_State * L)873 static int arc_endpoints(lua_State *L)
874 {
875 Arc *b = check_arc(L, 1);
876 push_vector(L, b->beginp());
877 push_vector(L, b->endp());
878 return 2;
879 }
880
arc_angles(lua_State * L)881 static int arc_angles(lua_State *L)
882 {
883 Arc *b = check_arc(L, 1);
884 lua_pushnumber(L, b->iAlpha);
885 lua_pushnumber(L, b->iBeta);
886 return 2;
887 }
888
arc_bbox(lua_State * L)889 static int arc_bbox(lua_State *L)
890 {
891 Arc *b = check_arc(L, 1);
892 push_rect(L, b->bbox());
893 return 1;
894 }
895
arc_matrix(lua_State * L)896 static int arc_matrix(lua_State *L)
897 {
898 Arc *b = check_arc(L, 1);
899 push_matrix(L, b->iM);
900 return 1;
901 }
902
arc_isEllipse(lua_State * L)903 static int arc_isEllipse(lua_State *L)
904 {
905 Arc *b = check_arc(L, 1);
906 lua_pushboolean(L, b->isEllipse());
907 return 1;
908 }
909
arc_intersect(lua_State * L)910 static int arc_intersect(lua_State *L)
911 {
912 Arc *a = check_arc(L, 1);
913 std::vector<Vector> pts;
914 if (is_type(L, 2, "Ipe.segment")) {
915 Segment *rhs = check_segment(L, 2);
916 a->intersect(*rhs, pts);
917 } else if (is_type(L, 2, "Ipe.line")) {
918 Line *rhs = check_line(L, 2);
919 a->intersect(*rhs, pts);
920 } else if (is_type(L, 2, "Ipe.arc")) {
921 Arc *rhs = check_arc(L, 2);
922 a->intersect(*rhs, pts);
923 } else if (is_type(L, 2, "Ipe.bezier")) {
924 Bezier *rhs = check_bezier(L, 2);
925 a->intersect(*rhs, pts);
926 }
927 lua_createtable(L, pts.size(), 0);
928 for (int i = 0; i < int(pts.size()); ++i) {
929 push_vector(L, pts[i]);
930 lua_rawseti(L, -2, i+1);
931 }
932 return 1;
933 }
934
arc_snap(lua_State * L)935 static int arc_snap(lua_State *L)
936 {
937 Arc *a = check_arc(L, 1);
938 Vector *v = check_vector(L, 2);
939 Vector pos;
940 Angle alpha;
941 (void) a->distance(*v, 10e9, pos, alpha);
942 lua_pushnumber(L, double(alpha));
943 push_vector(L, pos);
944 return 2;
945 }
946
947 static const struct luaL_Reg arc_methods[] = {
948 { "__tostring", arc_tostring },
949 { "endpoints", arc_endpoints },
950 { "angles", arc_angles },
951 { "bbox", arc_bbox },
952 { "matrix", arc_matrix },
953 { "isEllipse", arc_isEllipse },
954 { "intersect", arc_intersect },
955 { "snap", arc_snap },
956 { nullptr, nullptr }
957 };
958
959 // --------------------------------------------------------------------
960
open_ipegeo(lua_State * L)961 int ipelua::open_ipegeo(lua_State *L)
962 {
963 luaL_newmetatable(L, "Ipe.vector");
964 luaL_setfuncs(L, vector_methods, 0);
965 lua_pop(L, 1);
966
967 make_metatable(L, "Ipe.matrix", matrix_methods);
968 make_metatable(L, "Ipe.rect", rect_methods);
969 make_metatable(L, "Ipe.line", line_methods);
970 make_metatable(L, "Ipe.segment", segment_methods);
971 make_metatable(L, "Ipe.bezier", bezier_methods);
972 make_metatable(L, "Ipe.arc", arc_methods);
973
974 return 0;
975 }
976
977 // --------------------------------------------------------------------
978