53 : point(
p), label(
t), color(
c) {}
62 : point(
p), radius(
r), fill_color(fill) {}
76 items.emplace_back(item);
86 std::function<float (
float)>
px;
87 std::function<float (
float)>
py;
91 explicit Writer(
const std::string& path)
92 :
file(path.c_str()) {}
120 writer->
file <<
"<polyline points=\"";
124 auto write_point = [&writer](
const vec2f&
p)
126 writer->
file << writer->
px(
p.x) <<
","
130 for(
const auto p: poly->
points)
132 if(first) {first =
false;}
133 else {writer->
file <<
" ";}
141 write_point(poly->
points[0]);
145 writer->
file <<
"\" style=\"fill:";
147 writer->
file <<
";stroke:";
151 writer->
file <<
";stroke-width:0\"";
155 writer->
file <<
";stroke-width:1\"";
158 writer->
file <<
" stroke-dasharray=\"";
160 for(
const auto d: poly->
stroke)
162 if(first) { first =
false; }
163 else { writer->
file <<
","; }
166 writer->
file <<
"\"";
169 writer->
file <<
"/>\n";
174 writer->
file <<
"<text x=\"" << writer->
px(
text->point.x)
175 <<
"\" y=\"" << writer->
py(
text->point.y) <<
"\" fill=\"" <<
to_html_rgb(
text->color) <<
"\">" <<
text->label <<
"</text>\n";
181 <<
"<circle cx=\"" << writer->
px(circle->
point.
x) <<
"\" cy=\"" << writer->
py(circle->
point.
y)
189 const auto* poly =
as_poly(&item);
191 const auto* group =
as_group(&item);
197 else if(
text !=
nullptr)
201 else if(group !=
nullptr)
203 writer->
file <<
"<g>\n";
204 for(
const auto&
i: group->items)
208 writer->
file <<
"</g>\n";
210 else if(circle !=
nullptr)
216 DIE(
"unhandled type");
222 const auto* poly =
as_poly(&item);
224 const auto* group =
as_group(&item);
229 for(
const auto& point: poly->points)
234 else if(
text !=
nullptr)
238 else if(circle !=
nullptr)
240 mm->
include(circle->point, circle->radius);
242 else if(group !=
nullptr)
244 for(
const auto&
i: group->items)
251 DIE(
"unhandled type");
277 items.emplace_back(item);
286 for(
const auto& item:
items)
291 const auto&
min = minmax.
min;
292 const auto&
max = minmax.
max;
297 return {size, offset};
300 void Dumper::write(
const std::string& path,
int width,
int height,
int space)
const
311 const auto size =
r.first;
312 const auto offset =
r.second;
313 const auto scale =
std::min(
static_cast<float>(width-space*2) / size.x,
static_cast<float>(height-space*2) / size.y);
315 const auto dx = offset.x * scale;
316 const auto dy = offset.y * scale;
317 const auto px = [=](
float x) ->
float {
return static_cast<float>(space) + dx +
x * scale; };
318 const auto py = [=](
float y) ->
float {
return static_cast<float>(height) - (
static_cast<float>(space) + dy +
y * scale); };
323 writer.
scale = scale;
325 writer.
file <<
"<html style=\"height: 100%\">\n";
328 writer.
file <<
"<div style=\"display: grid; grid-template-columns: 1fr auto 1fr; grid-template-rows: 1fr auto 1fr; width:100%; height:100%\">\n";
329 writer.
file <<
"<div style=\"grid-row-start: 2; grid-column-start: 2;\">\n";
331 writer.
file <<
"<svg width=\"" << width <<
"\" height=\"" << height <<
"\">\n";
332 writer.
file <<
"<rect width=\"" << width <<
"\" height=\"" << height <<
"\""
335 for(
const auto& item:
items)
344 for(
const auto& item:
items)
346 const auto* poly =
as_poly(&item);
350 for(
const auto p: poly->points)
352 writer.
file <<
"<circle cx=\"" << px(
p.x) <<
"\" cy=\"" << py(
p.y) <<
"\" r=\"" <<
point_size <<
"\" stroke=\"none\" fill=\"" <<
to_html_rgb(
c) <<
"\""
355 << index <<
"(" <<
p.x <<
" " <<
p.y <<
")"
367 for(
const auto& item:
items)
369 const auto* poly =
as_poly(&item);
373 for(
const auto p: poly->points)
375 writer.
file <<
"<text x=\"" << px(
p.x) <<
"\" y=\"" << py(
p.y) <<
"\" fill=\"" <<
to_html_rgb(
c) <<
"\">"
376 << index <<
"(" <<
p.x <<
" " <<
p.y <<
")"
384 auto vline = [&](
float x,
const Rgbi&
c) { writer.
file <<
"<line x1=\"" << px(
x) <<
"\" y1=\"0\"" " x2=\"" << px(
x) <<
"\" y2=\"" << height <<
"\" style=\"stroke:" <<
to_html_rgb(
c) <<
";stroke-width:1\" />\n"; };
385 auto hline = [&](
float y,
const Rgbi&
c) { writer.
file <<
"<line x1=\"0\"" " y1=\"" << py(
y) <<
"\" x2=\"" << width <<
"\" y2=\"" << py(
y) <<
"\" style=\"stroke:" <<
to_html_rgb(
c) <<
";stroke-width:1\" />\n"; };
391 for(
auto x =
grid_x; px(
x) <
static_cast<float>(width);
x +=
grid_x) { vline(
x, grid_color); }
397 for(
auto y =
grid_y; px(
y) <
static_cast<float>(height);
y +=
grid_y) { hline(
y, grid_color); }
404 hline(0, axis_color);
405 vline(0, axis_color);
408 writer.
file <<
"</svg>\n";
410 writer.
file <<
"</div>\n";
411 writer.
file <<
"</div>\n";
413 writer.
file <<
"</body>\n";
414 writer.
file <<
"</html>\n";
427 <title>My first three.js app</title>
430 canvas { width: 100%; height: 100% }
434 <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r124/three.min.js"></script>
435 <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
438 var scene = new THREE.Scene();
439 scene.background = new THREE.Color( 0x0096ff );
440 var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
442 // https://threejs.org/examples/#webgl_shadowmesh
444 var light = new THREE.AmbientLight(0xffffff);
446 // scene.add( new THREE.AxisHelper( 20 ) );
448 var renderer = new THREE.WebGLRenderer();
449 renderer.setSize( window.innerWidth, window.innerHeight );
450 document.body.appendChild( renderer.domElement );
451 function onWindowResize() {
452 camera.aspect = window.innerWidth / window.innerHeight;
453 camera.updateProjectionMatrix();
454 renderer.setSize( window.innerWidth, window.innerHeight );
456 window.addEventListener( 'resize', onWindowResize, false );
458 var add_geom = function(geom, c)
460 var material = new THREE.MeshPhongMaterial( {
462 // shading: THREE.FlatShading,
464 polygonOffsetFactor: 1,
465 polygonOffsetUnits: 1
468 var mesh = new THREE.Mesh( geom, material );
473 var add_wire = function(geom, c)
475 mesh = add_geom(geom, c);
476 var geo = new THREE.EdgesGeometry( mesh.geometry ); // or WireframeGeometry
477 var mat = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 2 } );
478 var wireframe = new THREE.LineSegments( geo, mat );
479 mesh.add( wireframe );
482 // add_wire(new THREE.BoxGeometry( 1, 1, 1 ));
489 constexpr
auto s =
" ";
496 camera.position.z = 5;
497 new THREE.OrbitControls( camera, renderer.domElement );
499 var animate = function () {
500 requestAnimationFrame( animate );
501 renderer.render( scene, camera );
513 Dumper::add_sphere(
const vec3f& p,
float radius,
const Rgbi& color)
515 file <<
s <<
"add_geom(new THREE.SphereGeometry(" << radius <<
"), " <<
to_js_hex_color(color) <<
")\n"
516 <<
s <<
" .position.set("<<
p.x<<
", "<<
p.y<<
", "<<
p.z<<
");\n";
521 Dumper::add_lines(
const std::vector<vec3f>&
points,
const Rgbi& color)
524 <<
s <<
"(function() {\n"
525 <<
s <<
" var material = new THREE.LineBasicMaterial( { color: " <<
to_js_hex_color(color) <<
" } );\n"
526 <<
s <<
" var geometry = new THREE.Geometry();\n"
531 <<
s <<
" geometry.vertices.push(new THREE.Vector3( "
532 <<
p.x <<
", " <<
p.y <<
", " <<
p.z <<
") );\n"
536 <<
s <<
" var line = new THREE.Line( geometry, material );\n"
537 <<
s <<
" scene.add( line );\n"
544 to_three_vector_source(
const vec3f& v)
546 return fmt::format(
"new THREE.Vector3({}, {}, {})",
v.x,
v.y,
v.z);
551 Dumper::add_plane(
const Plane& plane,
const Rgbi& color)
553 constexpr
auto size = 5;
555 <<
s <<
"scene.add( new THREE.PlaneHelper( new THREE.Plane( "
556 << to_three_vector_source(plane.normal) <<
", " << plane.distance
563 Dumper::add_arrow(
const Ray3f& ray,
const Rgbi& color)
566 <<
s <<
"scene.add(new THREE.ArrowHelper("
567 << to_three_vector_source(ray.dir.get_normalized()) <<
", "
568 << to_three_vector_source(ray.from) <<
", "
569 << ray.dir.get_length() <<
", "
577 constexpr
auto size = 2;
579 <<
s <<
"scene.add(new THREE.AxesHelper("<< size <<
") );\n";
586 constexpr
auto size = 10;
587 constexpr
auto divisions = 10;
590 <<
s <<
"scene.add(new THREE.GridHelper("<< size <<
", " << divisions<<
") );\n";
void write_poly(Writer *writer, const Poly *poly)
void find_min_max(MinMaxer *mm, const Item &item)
void write_text(Writer *writer, const Text *text)
void write_circle(Writer *writer, const Circle *circle)
void write_item(Writer *writer, const Item &item)
std::vector< int > create_dash(int size)
std::string to_html_or_none_string(const std::optional< Rgbi > &c)
const Text * as_text(const Item *item)
const Poly * as_poly(const Item *item)
const Group * as_group(const Item *item)
const Circle * as_circle(const Item *item)
std::string to_html_rgb(const Rgbi &c)
size2f min(const size2f lhs, const size2f rhs)
std::string to_js_hex_color(const Rgbi &c)
size2f max(const size2f lhs, const size2f rhs)
std::optional< Rgbi > line_color
Circle(const vec2f &p, float r, std::optional< Rgbi > fill=std::nullopt)
Circle & set_line_color(const Rgbi &lc)
std::optional< Rgbi > fill_color
Dumper & add(const Item &item)
std::vector< Item > items
void write(const std::string &path, int width=1280, int height=1024, int space=6) const
bool add_axis_when_writing
Dumper & add_grid(float xy)
std::pair< vec2f, vec2f > calc_size_and_offset() const
calculate total area size and offset so that x+offset will never be lower than 0
Dumper & enable_points_rendering(int size=3)
Group & add(const Item &item)
std::vector< Item > items
std::shared_ptr< dump2d::Poly > poly
std::shared_ptr< dump2d::Group > group
std::shared_ptr< dump2d::Text > text
Item(const dump2d::Poly &p)
std::shared_ptr< dump2d::Circle > circle
std::optional< Rgbi > fill_color
std::vector< vec2f > points
Poly & fill(const Rgbi &fill_color)
Poly & set_stroke(const std::vector< int > &new_stroke)
std::vector< int > stroke
Text(const vec2f &p, const std::string &t, const Rgbi &c=NamedColor::black)
MinMaxer & include(const vec2f &point, float extra=0)
MinMaxer & operator<<(const vec2f &point)
std::function< float(float)> py
std::function< float(float)> px
Writer(const std::string &path)
Dumper(const std::string &path)