1 // Copyright 2016-2021 Doug Moen
2 // Licensed under the Apache License, version 2.0
3 // See accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0
4
5 #include <libcurv/shape.h>
6
7 #include <libcurv/context.h>
8 #include <libcurv/exception.h>
9 #include <libcurv/frame.h>
10 #include <libcurv/function.h>
11 #include <libcurv/program.h>
12 #include <libcurv/render.h>
13 #include <libcurv/sc_context.h>
14
15 #include <cmath>
16
17 namespace curv {
18
Shape_Program(Program & prog)19 Shape_Program::Shape_Program(
20 Program& prog)
21 :
22 sstate_(prog.sstate_),
23 nub_(nub_phrase(prog.phrase_))
24 {
25 // mark initial state (no shape has been recognized yet)
26 is_2d_ = false;
27 is_3d_ = false;
28 }
29
30 BBox
from_value(Value val,const Context & cx)31 BBox::from_value(Value val, const Context& cx)
32 {
33 auto list = val.to<List>(cx);
34 list->assert_size(2, cx);
35
36 At_Index mincx(0, cx);
37 auto mins = list->at(0).to<List>(mincx);
38 mins->assert_size(3, mincx);
39
40 At_Index maxcx(1, cx);
41 auto maxs = list->at(1).to<List>(maxcx);
42 maxs->assert_size(3, maxcx);
43
44 BBox b;
45 b.xmin = mins->at(0).to_num(cx);
46 b.ymin = mins->at(1).to_num(cx);
47 b.zmin = mins->at(2).to_num(cx);
48 b.xmax = maxs->at(0).to_num(cx);
49 b.ymax = maxs->at(1).to_num(cx);
50 b.zmax = maxs->at(2).to_num(cx);
51 return b;
52 }
53
54 bool
recognize(Value val,Render_Opts * opts)55 Shape_Program::recognize(Value val, Render_Opts* opts)
56 {
57 static Symbol_Ref is_2d_key = make_symbol("is_2d");
58 static Symbol_Ref is_3d_key = make_symbol("is_3d");
59 static Symbol_Ref bbox_key = make_symbol("bbox");
60 static Symbol_Ref dist_key = make_symbol("dist");
61 static Symbol_Ref colour_key = make_symbol("colour");
62 static Symbol_Ref render_key = make_symbol("render");
63
64 At_Program cx(*this);
65
66 auto r = val.maybe<Record>();
67 if (r == nullptr) return false;
68 Value is_2d_val = r->find_field(is_2d_key, cx);
69 if (is_2d_val.is_missing()) return false;
70 Value is_3d_val = r->find_field(is_3d_key, cx);
71 if (is_3d_val.is_missing()) return false;
72 Value bbox_val = r->find_field(bbox_key, cx);
73 if (bbox_val.is_missing()) return false;
74 Value dist_val = r->find_field(dist_key, cx);
75 if (dist_val.is_missing()) return false;
76 Value colour_val = r->find_field(colour_key, cx);
77 if (colour_val.is_missing()) return false;
78
79 is_2d_ = is_2d_val.to_bool(At_Field("is_2d", cx));
80 is_3d_ = is_3d_val.to_bool(At_Field("is_3d", cx));
81 if (!is_2d_ && !is_3d_)
82 throw Exception(cx,
83 "at least one of is_2d and is_3d must be true");
84 bbox_ = BBox::from_value(bbox_val, At_Field("bbox", cx));
85
86 dist_fun_ = value_to_function(dist_val, At_Field("dist", cx));
87 dist_frame_ = Frame::make(dist_fun_->nslots_, sstate_, nullptr,
88 nullptr, nullptr);
89
90 colour_fun_ = value_to_function(colour_val, At_Field("colour", cx));
91 colour_frame_ = Frame::make(colour_fun_->nslots_, sstate_, nullptr,
92 nullptr, nullptr);
93
94 Value render_val = r->find_field(render_key, cx);
95 if (!render_val.is_missing()) {
96 At_Field rcx("render", cx);
97 auto render = render_val.to<Record>(rcx);
98 if (opts != nullptr) {
99 // update *opts from fields in the render record
100 opts->update_from_record(*render, rcx);
101 }
102 }
103
104 record_ = r;
105 return true;
106 }
107
Shape_Program(const Shape_Program & shape,Shared<Record> r,Viewed_Shape * vs)108 Shape_Program::Shape_Program(
109 const Shape_Program& shape,
110 Shared<Record> r,
111 Viewed_Shape* vs)
112 :
113 sstate_(shape.sstate_),
114 nub_(shape.nub_),
115 record_(r),
116 viewed_shape_(vs)
117 {
118 static Symbol_Ref dist_key = make_symbol("dist");
119 static Symbol_Ref colour_key = make_symbol("colour");
120
121 is_2d_ = shape.is_2d_;
122 is_3d_ = shape.is_3d_;
123 bbox_ = shape.bbox_;
124
125 At_Program cx(*this);
126
127 if (r->hasfield(dist_key))
128 dist_fun_ = value_to_function(r->getfield(dist_key, cx), cx);
129 else
130 throw Exception{cx, stringify(
131 "bad parametric shape: call result has no 'dist' field: ", r)};
132
133 if (r->hasfield(colour_key))
134 colour_fun_ = value_to_function(r->getfield(colour_key, cx), cx);
135 else
136 throw Exception{cx, stringify(
137 "bad parametric shape: call result has no 'colour' field: ", r)};
138 }
139
140 double
dist(double x,double y,double z,double t)141 Shape_Program::dist(double x, double y, double z, double t)
142 {
143 At_Program cx(*this);
144 Shared<List> point = List::make({Value{x}, Value{y}, Value{z}, Value{t}});
145 Value result = dist_fun_->call({point}, Fail::hard, *dist_frame_);
146 return result.to_num(cx);
147 }
148
149 Vec3
colour(double x,double y,double z,double t)150 Shape_Program::colour(double x, double y, double z, double t)
151 {
152 At_Program cx(*this);
153 Shared<List> point = List::make({Value{x}, Value{y}, Value{z}, Value{t}});
154 Value result = colour_fun_->call({point}, Fail::hard, *colour_frame_);
155 Shared<List> cval = result.to<List>(cx);
156 cval->assert_size(3, cx);
157 return Vec3{ cval->at(0).to_num(cx),
158 cval->at(1).to_num(cx),
159 cval->at(2).to_num(cx) };
160 }
161
162 } // namespace
163