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