174 lines
6 KiB
Org Mode
174 lines
6 KiB
Org Mode
#+title: d3 draggable object example #5 -- parametric + tangent line
|
|
#
|
|
# org-publish options
|
|
# H:2 controls section numbering.
|
|
# number top-level and second-level headings only
|
|
# ^:{} require a_{b} before assuming that b should be subscripted.
|
|
# without this option a_b will automatically subscript b.
|
|
#+options: ^:{}
|
|
#
|
|
# options used exclusively by emacs
|
|
#+startup: showall
|
|
#
|
|
# options used exclusively by the html exporter
|
|
#+language: en
|
|
#+infojs_opt: view:showall toc:nil ltoc:nil mouse:#ffc0c0 path:/web/ext/orginfo/org-info.js
|
|
#+html_head: <script type="text/javascript" src="/web/ext/d3/d3.v3.min.js"></script>
|
|
#+html_head: <script type="text/javascript" src="point.js"></script>
|
|
#+html_head: <script type="text/javascript" src="fx.js"></script>
|
|
#+html_head: <script type="text/javascript" src="fx_view.js"></script>
|
|
#+html_head: <script type="text/javascript" src="parametric-drag-example.js"></script>
|
|
#+html_head: <link rel="stylesheet" type="text/css" href="/web/css/notebook.css" />
|
|
#+html_link_home: ../../index.html
|
|
#+html_link_up: ../../index.html
|
|
|
|
* Introduction
|
|
|
|
This page extends example /#4/ (file:../drag4/index.org),
|
|
modifying the algorithm for associating mouse coordinates with a point on the function
|
|
so that we get smooth dragging behavior
|
|
|
|
- source :: org-mode source for this page is here: file:index-src.org
|
|
|
|
* Demo
|
|
The div element ~#frame~ will appear below this line:
|
|
|
|
#+begin_export html
|
|
<div id="frame"></div>
|
|
<script type="text/javascript">
|
|
window.onload = function() { ex.start(this); }
|
|
</script>
|
|
#+end_export
|
|
|
|
* Prerequisites
|
|
|
|
As for examples [[file:../drag1/index.org][/#1/]], [[file://drag2/index.org][/#2/]], [[file:~/proj/org-howto/d3/drag3/index.org][/#3/]], [[file:~/proj/org-howto/d3/drag4/index.org][/#4/]]
|
|
|
|
* Procedure
|
|
|
|
Start with example [[file:~/proj/org-howto/d3/drag4/index.org][/#4/]]: we will modify the files [[file:point.js]], [[file:fx.js]], [[file:fx_view.js]].
|
|
|
|
** Extend ~fx.js~
|
|
Add functions:
|
|
- ~fx.target_deriv_fn~
|
|
- ~fx.make_target_tangent_fn~
|
|
- ~fx.linear_inverse_fn~
|
|
|
|
#+begin_example
|
|
/* derivative f'(x) of fx.target_fn
|
|
* x :: number
|
|
* returns :: number
|
|
*/
|
|
fx.target_deriv_fn = function(x) {
|
|
return x * (3 * x - 1.2);
|
|
} /*target_deriv_fn*/
|
|
#+end_example
|
|
|
|
#+begin_example
|
|
/* the (linear) function of (x) that is
|
|
* tangent to fx.target_fn at x0
|
|
*/
|
|
fx.make_target_tangent_fn = function(x0) {
|
|
var fx0 = fx.target_fn(x0); /*f(x0)*/
|
|
var dfx0 = fx.target_deriv_fn(x0); /*f'(x0)*/
|
|
|
|
return function(x) { return fx0 + (x - x0) * dfx0; }
|
|
} /*make_target_tangent_fn*/
|
|
#+end_example
|
|
|
|
#+begin_example
|
|
/* inverse of fx.linear_fn:
|
|
* given scale k, {offset_x, offset_y}
|
|
* return function
|
|
* f({x,y}) = {(x - offset_x)/scale, (y - offset_y)/scale}
|
|
*/
|
|
fx.linear_inverse_fn = function(offset_pt, scale) {
|
|
/* p :: Point */
|
|
return function(p) {
|
|
return pt.scale_pt(1.0 / scale,
|
|
pt.sub_pt(p, offset_pt));
|
|
}
|
|
} /*linear_inverse_fn*/
|
|
#+end_example
|
|
|
|
** Upgrade ~fx_view.draw~
|
|
Extend ~fx_view.draw~ to draw tangent function:
|
|
|
|
#+begin_example
|
|
/* parent_id :: string. pass this to d3.select() to get selection for parent
|
|
* at which to attach svg box
|
|
* box_pt :: Point. size of svg bounding box
|
|
* fx2scr_fn :: Point -> Point
|
|
* scr2fx_fn :: Point -> Point
|
|
*/
|
|
fx_view.draw = function(parent_id, box_pt, target_pt_v,
|
|
fx2scr_fn, scr2fx_fn)
|
|
{
|
|
/* create an svg bounding box, to contain interactive drawing area */
|
|
fx_view.box = (d3.select(parent_id)
|
|
.append("svg")
|
|
.attr("class", "box")
|
|
.attr("width", box_pt.x)
|
|
.attr("height", box_pt.y));
|
|
|
|
/* border, so bounding box is visible */
|
|
fx_view.border = (fx_view.box.append("svg:rect")
|
|
.attr("class", "border")
|
|
.attr("x", 1)
|
|
.attr("y", 1)
|
|
.attr("width", box_pt.x - 2)
|
|
.attr("height", box_pt.y - 2)
|
|
.attr("stroke", "navy")
|
|
.attr("stroke-width", 3)
|
|
.style("fill", "none"));
|
|
|
|
/* create path representing our target function f(x) */
|
|
fx_view.fx_path = (fx_view.box.append("path")
|
|
.attr("d", fx_view.svg_line_fn(target_pt_v))
|
|
.attr("stroke", "navy")
|
|
.attr("stroke-width", 2)
|
|
.attr("fill", "none")
|
|
);
|
|
|
|
fx_view.fx_update_tangent_fn(scr2fx_fn(pt.scale_pt(0.5, box_pt)).x,
|
|
box_pt, fx2scr_fn, scr2fx_fn);
|
|
|
|
fx_view.fx_update_select_circle(pt.find_closest(pt.scale_pt(0.5, box_pt),
|
|
target_pt_v));
|
|
} /*draw*/
|
|
#+end_example
|
|
|
|
** Upgrade ~parametric-drag-example.js~
|
|
Extend ~ex.start~ to supply new conversion to ~fx.draw~
|
|
|
|
#+begin_example
|
|
ex.screen2pt = fx.linear_inverse_fn(pt.sub_pt(pt.scale_pt(0.5, ex.box_pt),
|
|
fx.eval_fn(0.0) /*ctr_fx*/),
|
|
200.0 /*scale_factor*/);
|
|
|
|
...
|
|
fx_view.draw("#frame", ex.box_pt, ex.target_pt_v,
|
|
ex.pt2screen, ex.screen2pt);
|
|
#+end_example
|
|
|
|
** Load ~.js_ files in html header
|
|
This step is identical to the similar step in example /#3/, example /#4/
|
|
At the top of the ~.org~ file:
|
|
#+begin_example
|
|
,#+html_head: <script type="text/javascript" src="/ext/d3/d3.js"></script>
|
|
,#+html_head: <script type="text/javascript" src="point.js"></script>
|
|
,#+html_head: <script type="text/javascript" src="fx.js"></script>
|
|
,#+html_head: <script type="text/javascript" src="fx_view.js"></script>
|
|
,#+html_head: <script type="text/javascript" src="parametric-drag-example.js"></script>
|
|
#+end_example
|
|
|
|
** Insert html fragment to invoke our interactive javascript code
|
|
This also follows the same model we used in example /#3/, example /#4/.
|
|
#+begin_example
|
|
,#+begin_html
|
|
<div id="frame"></div>
|
|
<script type="text/javascript">
|
|
window.onload = function() { ex.start(this); }
|
|
</script>
|
|
#+end_html
|
|
#+end_example
|