Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
X
xblock-vectordraw
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
xblock-vectordraw
Commits
9a9a3241
Commit
9a9a3241
authored
Oct 30, 2015
by
Tim Krones
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow to display and interact with exercises in the LMS.
parent
6cb67fc6
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
834 additions
and
24 deletions
+834
-24
vectordraw/static/css/vectordraw.css
+78
-3
vectordraw/static/html/vectordraw.html
+3
-3
vectordraw/static/js/src/vectordraw.js
+543
-10
vectordraw/vectordraw.py
+210
-8
No files found.
vectordraw/static/css/vectordraw.css
View file @
9a9a3241
/* CSS for VectorDrawXBlock */
/* CSS for VectorDrawXBlock */
.vectordraw_block
.count
{
.vectordraw_block
{
display
:
inline-block
;
}
.vectordraw_block
.jxgboard
{
float
:
left
;
border
:
2px
solid
#1f628d
;
}
.vectordraw_block
.jxgboard
.JXGtext
{
pointer-events
:
none
;
/* prevents cursor from turning into caret when over a label */
}
.vectordraw_block
.menu
{
float
:
left
;
width
:
160px
;
margin-left
:
15px
;
}
.vectordraw_block
.menu
.controls
{
margin-bottom
:
20px
;
font-size
:
0
;
}
.vectordraw_block
.menu
.controls
select
{
width
:
160px
;
margin-bottom
:
8px
;
font-size
:
18px
;
}
.vectordraw_block
.menu
.controls
button
{
display
:
inline-block
;
background-color
:
#3498db
;
border-radius
:
5px
;
box-shadow
:
0
1px
3px
#666
;
color
:
#fff
;
font-size
:
14px
;
padding
:
5px
10px
5px
10px
;
margin
:
4px
0
;
border
:
2px
solid
#1f628d
;
text-decoration
:
none
;
width
:
160px
;
}
.vectordraw_block
.menu
.controls
button
:hover
{
background
:
#3cb0fd
;
background-image
:
-webkit-linear-gradient
(
top
,
#3cb0fd
,
#3498db
);
background-image
:
-moz-linear-gradient
(
top
,
#3cb0fd
,
#3498db
);
background-image
:
-ms-linear-gradient
(
top
,
#3cb0fd
,
#3498db
);
background-image
:
-o-linear-gradient
(
top
,
#3cb0fd
,
#3498db
);
background-image
:
linear-gradient
(
to
bottom
,
#3cb0fd
,
#3498db
);
text-decoration
:
none
;
}
vectordraw_block
.menu
.controls
button
.undo
,
vectordraw_block
.menu
.controls
button
.redo
{
width
:
78px
;
}
.vectordraw_block
.menu
.controls
button
.undo
{
margin-right
:
4px
;
}
.vectordraw_block
.menu
.vector-properties
{
padding
:
10px
;
border
:
1px
solid
#1f628d
;
font-size
:
16px
;
line-height
:
1.25
;
}
.vectordraw_block
.menu
.vector-properties
h3
{
font-size
:
16px
;
margin
:
0
0
5px
;
}
.vectordraw_block
.menu
.vector-properties
.vector-prop-bold
{
font-weight
:
bold
;
font-weight
:
bold
;
}
}
.vectordraw_block
p
{
.vectordraw_block
.menu
.vector-prop-slope
{
cursor
:
pointer
;
display
:
none
;
}
}
vectordraw/static/html/vectordraw.html
View file @
9a9a3241
<div
class=
"vectordraw_block"
>
<div
class=
"vectordraw_block"
>
<p>
VectorDrawXBlock: count is now
<span
class=
'count'
>
{self.count}
</span>
(click me to increment).
<div
id=
"vectordraw"
/>
</p>
</div>
</div>
vectordraw/static/js/src/vectordraw.js
View file @
9a9a3241
/* Javascript for VectorDrawXBlock. */
/* Javascript for VectorDrawXBlock. */
function
VectorDrawXBlock
(
runtime
,
element
)
{
function
VectorDrawXBlock
(
runtime
,
element
,
init_args
)
{
'use strict'
;
function
updateCount
(
result
)
{
var
VectorDraw
=
function
(
element_id
,
settings
)
{
$
(
'.count'
,
element
).
text
(
result
.
count
);
this
.
board
=
null
;
this
.
dragged_vector
=
null
;
this
.
drawMode
=
false
;
this
.
history_stack
=
{
undo
:
[],
redo
:
[]};
this
.
settings
=
this
.
sanitizeSettings
(
settings
);
this
.
element
=
$
(
'#'
+
element_id
);
this
.
element
.
on
(
'click'
,
'.reset'
,
this
.
reset
.
bind
(
this
));
this
.
element
.
on
(
'click'
,
'.add-vector'
,
this
.
addElementFromList
.
bind
(
this
));
this
.
element
.
on
(
'click'
,
'.undo'
,
this
.
undo
.
bind
(
this
));
this
.
element
.
on
(
'click'
,
'.redo'
,
this
.
redo
.
bind
(
this
));
// Prevents default image drag and drop actions in some browsers.
this
.
element
.
on
(
'mousedown'
,
'.jxgboard image'
,
function
(
evt
)
{
evt
.
preventDefault
();
});
this
.
render
();
};
VectorDraw
.
prototype
.
sanitizeSettings
=
function
(
settings
)
{
// Fill in defaults at top level of settings.
var
default_settings
=
{
width
:
550
,
height
:
400
,
axis
:
false
,
background
:
null
,
bounding_box_size
:
10
,
show_navigation
:
false
,
show_vector_properties
:
true
,
add_vector_label
:
'Add Selected Force'
,
vector_properties_label
:
'Vector Properties'
,
vectors
:
[],
points
:
[],
expected_result
:
{},
custom_checks
:
[],
unit_vector_ratio
:
1
};
_
.
defaults
(
settings
,
default_settings
);
var
width_scale
=
settings
.
width
/
settings
.
height
,
box_size
=
settings
.
bounding_box_size
;
settings
.
bounding_box
=
[
-
box_size
*
width_scale
,
box_size
,
box_size
*
width_scale
,
-
box_size
];
// Fill in defaults for vectors.
var
default_vector
=
{
type
:
'vector'
,
render
:
false
,
length_factor
:
1
,
length_units
:
''
,
base_angle
:
0
,
style
:
{}
};
var
default_vector_style
=
{
pointSize
:
1
,
pointColor
:
'red'
,
width
:
4
,
color
:
"blue"
,
label
:
null
,
labelColor
:
'black'
};
settings
.
vectors
.
forEach
(
function
(
vector
)
{
_
.
defaults
(
vector
,
default_vector
);
_
.
defaults
(
vector
.
style
,
default_vector_style
);
});
// Fill in defaults for points.
var
default_point
=
{
fixed
:
true
,
// Default to true for backwards compatibility.
render
:
true
,
style
:
{}
};
var
default_point_style
=
{
size
:
1
,
withLabel
:
false
,
color
:
'pink'
,
showInfoBox
:
false
};
settings
.
points
.
forEach
(
function
(
point
)
{
_
.
defaults
(
point
,
default_point
);
_
.
defaults
(
point
.
style
,
default_point_style
);
point
.
style
.
name
=
point
.
name
;
point
.
style
.
fixed
=
point
.
fixed
;
point
.
style
.
strokeColor
=
point
.
style
.
color
;
point
.
style
.
fillColor
=
point
.
style
.
color
;
delete
point
.
style
.
color
;
});
return
settings
;
};
VectorDraw
.
prototype
.
template
=
_
.
template
([
'<div class="jxgboard" style="width:<%= width %>px; height:<%= height %>px;" />'
,
'<div class="menu">'
,
' <div class="controls">'
,
' <select>'
,
' <% vectors.forEach(function(vec, idx) { %>'
,
' <option value="vector-<%= idx %>"><%= vec.description %></option>'
,
' <% }) %>'
,
' <% points.forEach(function(point, idx) { if (!point.fixed) { %>'
,
' <option value="point-<%= idx %>"><%= point.description %></option>'
,
' <% }}) %>'
,
' </select>'
,
' <button class="add-vector"><%= add_vector_label %></button>'
,
' <button class="reset">Reset</button>'
,
' <button class="undo" title="Undo"><span class="fa fa-undo" /></button>'
,
' <button class="redo" title="redo"><span class="fa fa-repeat" /></button>'
,
' </div>'
,
' <% if (show_vector_properties) { %>'
,
' <div class="vector-properties">'
,
' <h3><%= vector_properties_label %></h3>'
,
' <div class="vector-prop-name">'
,
' name: <span class="value vector-prop-bold">-</span>'
,
' </div>'
,
' <div class="vector-prop-length">'
,
' length: <span class="value">-</span>'
,
' </div>'
,
' <div class="vector-prop-angle">'
,
' angle: <span class="value">-</span>°'
,
' </div>'
,
' <div class="vector-prop-slope">'
,
' slope: <span class="value">-</span>'
,
' </div>'
,
' </div>'
,
' <% } %>'
,
'</div>'
].
join
(
'
\
n'
));
VectorDraw
.
prototype
.
render
=
function
()
{
this
.
element
.
html
(
this
.
template
(
this
.
settings
));
// Assign the jxgboard element a random unique ID,
// because JXG.JSXGraph.initBoard needs it.
this
.
element
.
find
(
'.jxgboard'
).
prop
(
'id'
,
_
.
uniqueId
(
'jxgboard'
));
this
.
createBoard
();
};
VectorDraw
.
prototype
.
createBoard
=
function
()
{
var
id
=
this
.
element
.
find
(
'.jxgboard'
).
prop
(
'id'
),
self
=
this
;
this
.
board
=
JXG
.
JSXGraph
.
initBoard
(
id
,
{
keepaspectratio
:
true
,
boundingbox
:
this
.
settings
.
bounding_box
,
axis
:
this
.
settings
.
axis
,
showCopyright
:
false
,
showNavigation
:
this
.
settings
.
show_navigation
});
function
getImageRatio
(
bg
,
callback
)
{
$
(
'<img/>'
).
attr
(
'src'
,
bg
.
src
).
load
(
function
(){
//technically it's inverse of ratio, but we need it to calculate height
var
ratio
=
this
.
height
/
this
.
width
;
callback
(
bg
,
ratio
);
});
}
function
drawBackground
(
bg
,
ratio
)
{
var
height
=
(
bg
.
height
)
?
bg
.
height
:
bg
.
width
*
ratio
;
var
coords
=
(
bg
.
coords
)
?
bg
.
coords
:
[
-
bg
.
width
/
2
,
-
height
/
2
];
self
.
board
.
create
(
'image'
,
[
bg
.
src
,
coords
,
[
bg
.
width
,
height
]],
{
fixed
:
true
});
}
if
(
this
.
settings
.
background
)
{
if
(
this
.
settings
.
background
.
height
)
{
drawBackground
(
this
.
settings
.
background
);
}
else
{
getImageRatio
(
this
.
settings
.
background
,
drawBackground
);
}
}
this
.
settings
.
points
.
forEach
(
function
(
point
,
idx
)
{
if
(
point
.
render
)
{
this
.
renderPoint
(
idx
);
}
},
this
);
this
.
settings
.
vectors
.
forEach
(
function
(
vec
,
idx
)
{
if
(
vec
.
render
)
{
this
.
renderVector
(
idx
);
}
}
},
this
);
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
'increment_count'
);
this
.
board
.
on
(
'down'
,
this
.
onBoardDown
.
bind
(
this
));
this
.
board
.
on
(
'move'
,
this
.
onBoardMove
.
bind
(
this
));
this
.
board
.
on
(
'up'
,
this
.
onBoardUp
.
bind
(
this
));
};
$
(
'p'
,
element
).
click
(
function
(
eventObject
)
{
VectorDraw
.
prototype
.
renderPoint
=
function
(
idx
,
coords
)
{
$
.
ajax
({
var
point
=
this
.
settings
.
points
[
idx
];
type
:
"POST"
,
var
coords
=
coords
||
point
.
coords
;
url
:
handlerUrl
,
var
board_object
=
this
.
board
.
elementsByName
[
point
.
name
];
data
:
JSON
.
stringify
({
"hello"
:
"world"
}),
if
(
board_object
)
{
success
:
updateCount
// If the point is already rendered, only update its coordinates.
board_object
.
setPosition
(
JXG
.
COORDS_BY_USER
,
coords
);
return
;
}
this
.
board
.
create
(
'point'
,
coords
,
point
.
style
);
if
(
!
point
.
fixed
)
{
// Disable the <option> element corresponding to point.
var
option
=
this
.
getMenuOption
(
'point'
,
idx
);
option
.
prop
(
'disabled'
,
true
).
prop
(
'selected'
,
false
);
}
};
VectorDraw
.
prototype
.
removePoint
=
function
(
idx
)
{
var
point
=
this
.
settings
.
points
[
idx
];
var
object
=
this
.
board
.
elementsByName
[
point
.
name
];
if
(
object
)
{
this
.
board
.
removeAncestors
(
object
);
// Enable the <option> element corresponding to point.
var
option
=
this
.
getMenuOption
(
'point'
,
idx
);
option
.
prop
(
'disabled'
,
false
);
}
};
VectorDraw
.
prototype
.
getVectorCoordinates
=
function
(
vec
)
{
var
coords
=
vec
.
coords
;
if
(
!
coords
)
{
var
tail
=
vec
.
tail
||
[
0
,
0
];
var
length
=
'length'
in
vec
?
vec
.
length
:
5
;
var
angle
=
'angle'
in
vec
?
vec
.
angle
:
30
;
var
radians
=
angle
*
Math
.
PI
/
180
;
var
tip
=
[
tail
[
0
]
+
Math
.
cos
(
radians
)
*
length
,
tail
[
1
]
+
Math
.
sin
(
radians
)
*
length
];
coords
=
[
tail
,
tip
];
}
return
coords
;
};
VectorDraw
.
prototype
.
getVectorStyle
=
function
(
vec
)
{
//width, color, size of control point, label (which should be a JSXGraph option)
var
default_style
=
{
pointSize
:
1
,
pointColor
:
'red'
,
width
:
4
,
color
:
"blue"
,
label
:
null
,
labelColor
:
'black'
};
return
_
.
extend
(
default_style
,
vec
.
style
);
};
VectorDraw
.
prototype
.
renderVector
=
function
(
idx
,
coords
)
{
var
vec
=
this
.
settings
.
vectors
[
idx
];
coords
=
coords
||
this
.
getVectorCoordinates
(
vec
);
// If this vector is already rendered, only update its coordinates.
var
board_object
=
this
.
board
.
elementsByName
[
vec
.
name
];
if
(
board_object
)
{
board_object
.
point1
.
setPosition
(
JXG
.
COORDS_BY_USER
,
coords
[
0
]);
board_object
.
point2
.
setPosition
(
JXG
.
COORDS_BY_USER
,
coords
[
1
]);
return
;
}
var
style
=
vec
.
style
;
var
tail
=
this
.
board
.
create
(
'point'
,
coords
[
0
],
{
name
:
vec
.
name
,
size
:
style
.
pointSize
,
fillColor
:
style
.
pointColor
,
strokeColor
:
style
.
pointColor
,
withLabel
:
false
,
fixed
:
(
vec
.
type
===
'arrow'
|
vec
.
type
===
'vector'
),
showInfoBox
:
false
});
var
tip
=
this
.
board
.
create
(
'point'
,
coords
[
1
],
{
name
:
style
.
label
||
vec
.
name
,
size
:
style
.
pointSize
,
fillColor
:
style
.
pointColor
,
strokeColor
:
style
.
pointColor
,
withLabel
:
true
,
showInfoBox
:
false
});
});
// Not sure why, but including labelColor in attributes above doesn't work,
// it only works when set explicitly with setAttribute.
tip
.
setAttribute
({
labelColor
:
style
.
labelColor
});
var
line_type
=
(
vec
.
type
===
'vector'
)
?
'arrow'
:
vec
.
type
;
var
line
=
this
.
board
.
create
(
line_type
,
[
tail
,
tip
],
{
name
:
vec
.
name
,
strokeWidth
:
style
.
width
,
strokeColor
:
style
.
color
});
});
tip
.
label
.
setAttribute
({
fontsize
:
18
,
highlightStrokeColor
:
'black'
});
// Disable the <option> element corresponding to vector.
var
option
=
this
.
getMenuOption
(
'vector'
,
idx
);
option
.
prop
(
'disabled'
,
true
).
prop
(
'selected'
,
false
);
return
line
;
};
VectorDraw
.
prototype
.
removeVector
=
function
(
idx
)
{
var
vec
=
this
.
settings
.
vectors
[
idx
];
var
object
=
this
.
board
.
elementsByName
[
vec
.
name
];
if
(
object
)
{
this
.
board
.
removeAncestors
(
object
);
// Enable the <option> element corresponding to vector.
var
option
=
this
.
getMenuOption
(
'vector'
,
idx
);
option
.
prop
(
'disabled'
,
false
);
}
};
VectorDraw
.
prototype
.
getMenuOption
=
function
(
type
,
idx
)
{
return
this
.
element
.
find
(
'.menu option[value='
+
type
+
'-'
+
idx
+
']'
);
};
VectorDraw
.
prototype
.
getSelectedElement
=
function
()
{
var
selector
=
this
.
element
.
find
(
'.menu select'
).
val
();
if
(
selector
)
{
selector
=
selector
.
split
(
'-'
);
return
{
type
:
selector
[
0
],
idx
:
parseInt
(
selector
[
1
])
};
}
return
{};
};
VectorDraw
.
prototype
.
addElementFromList
=
function
()
{
this
.
pushHistory
();
var
selected
=
this
.
getSelectedElement
();
if
(
selected
.
type
===
'vector'
)
{
this
.
updateVectorProperties
(
this
.
renderVector
(
selected
.
idx
));
}
else
{
this
.
renderPoint
(
selected
.
idx
);
}
};
VectorDraw
.
prototype
.
reset
=
function
()
{
this
.
pushHistory
();
JXG
.
JSXGraph
.
freeBoard
(
this
.
board
);
this
.
render
();
};
VectorDraw
.
prototype
.
pushHistory
=
function
()
{
var
state
=
this
.
getState
();
var
previous_state
=
_
.
last
(
this
.
history_stack
.
undo
);
if
(
!
_
.
isEqual
(
state
,
previous_state
))
{
this
.
history_stack
.
undo
.
push
(
state
);
this
.
history_stack
.
redo
=
[];
}
};
VectorDraw
.
prototype
.
undo
=
function
()
{
var
curr_state
=
this
.
getState
();
var
undo_state
=
this
.
history_stack
.
undo
.
pop
();
if
(
undo_state
&&
!
_
.
isEqual
(
undo_state
,
curr_state
))
{
this
.
history_stack
.
redo
.
push
(
curr_state
);
this
.
setState
(
undo_state
);
}
};
VectorDraw
.
prototype
.
redo
=
function
()
{
var
state
=
this
.
history_stack
.
redo
.
pop
();
if
(
state
)
{
this
.
history_stack
.
undo
.
push
(
this
.
getState
());
this
.
setState
(
state
);
}
};
VectorDraw
.
prototype
.
getMouseCoords
=
function
(
evt
)
{
var
i
=
evt
[
JXG
.
touchProperty
]
?
0
:
undefined
;
var
c_pos
=
this
.
board
.
getCoordsTopLeftCorner
(
evt
,
i
);
var
abs_pos
=
JXG
.
getPosition
(
evt
,
i
);
var
dx
=
abs_pos
[
0
]
-
c_pos
[
0
];
var
dy
=
abs_pos
[
1
]
-
c_pos
[
1
];
return
new
JXG
.
Coords
(
JXG
.
COORDS_BY_SCREEN
,
[
dx
,
dy
],
this
.
board
);
};
VectorDraw
.
prototype
.
getVectorForObject
=
function
(
obj
)
{
if
(
obj
instanceof
JXG
.
Line
)
{
return
obj
;
}
if
(
obj
instanceof
JXG
.
Text
)
{
return
this
.
getVectorForObject
(
obj
.
element
);
}
if
(
obj
instanceof
JXG
.
Point
)
{
return
_
.
find
(
obj
.
descendants
,
function
(
d
)
{
return
(
d
instanceof
JXG
.
Line
);
});
}
return
null
;
};
VectorDraw
.
prototype
.
getVectorSettingsByName
=
function
(
name
)
{
return
_
.
find
(
this
.
settings
.
vectors
,
function
(
vec
)
{
return
vec
.
name
===
name
;
});
};
VectorDraw
.
prototype
.
updateVectorProperties
=
function
(
vector
)
{
var
vec_settings
=
this
.
getVectorSettingsByName
(
vector
.
name
);
var
x1
=
vector
.
point1
.
X
(),
y1
=
vector
.
point1
.
Y
(),
x2
=
vector
.
point2
.
X
(),
y2
=
vector
.
point2
.
Y
();
var
length
=
vec_settings
.
length_factor
*
Math
.
sqrt
(
Math
.
pow
(
x2
-
x1
,
2
)
+
Math
.
pow
(
y2
-
y1
,
2
));
var
slope
=
(
y2
-
y1
)
/
(
x2
-
x1
);
var
angle
=
((
Math
.
atan2
(
y2
-
y1
,
x2
-
x1
)
/
Math
.
PI
*
180
)
-
vec_settings
.
base_angle
)
%
360
;
if
(
angle
<
0
)
{
angle
+=
360
;
}
$
(
'.vector-prop-name .value'
,
this
.
element
).
html
(
vector
.
point2
.
name
);
// labels are stored as point2 names
$
(
'.vector-prop-angle .value'
,
this
.
element
).
html
(
angle
.
toFixed
(
2
));
if
(
vector
.
elType
!==
"line"
)
{
$
(
'.vector-prop-length'
,
this
.
element
).
show
();
$
(
'.vector-prop-length .value'
,
this
.
element
).
html
(
length
.
toFixed
(
2
)
+
' '
+
vec_settings
.
length_units
);
$
(
'.vector-prop-slope'
,
this
.
element
).
hide
();
}
else
{
$
(
'.vector-prop-length'
,
this
.
element
).
hide
();
if
(
this
.
settings
.
show_slope_for_lines
)
{
$
(
'.vector-prop-slope'
,
this
.
element
).
show
();
$
(
'.vector-prop-slope .value'
,
this
.
element
).
html
(
slope
.
toFixed
(
2
));
}
}
};
VectorDraw
.
prototype
.
isVectorTailDraggable
=
function
(
vector
)
{
return
vector
.
elType
!==
'arrow'
;
};
VectorDraw
.
prototype
.
canCreateVectorOnTopOf
=
function
(
el
)
{
// If the user is trying to drag the arrow of an existing vector, we should not create a new vector.
if
(
el
instanceof
JXG
.
Line
)
{
return
false
;
}
// If this is tip/tail of a vector, it's going to have a descendant Line - we should not create a new vector
// when over the tip. Creating on top of the tail is allowed for plain vectors but not for segments.
// If it doesn't have a descendant Line, it's a point from settings.points - creating a new vector is allowed.
if
(
el
instanceof
JXG
.
Point
)
{
var
vector
=
this
.
getVectorForObject
(
el
);
if
(
!
vector
)
{
return
el
.
getProperty
(
'fixed'
);
}
else
if
(
el
===
vector
.
point1
&&
!
this
.
isVectorTailDraggable
(
vector
))
{
return
true
;
}
else
{
return
false
;
}
}
return
true
;
};
VectorDraw
.
prototype
.
objectsUnderMouse
=
function
(
coords
)
{
var
filter
=
function
(
el
)
{
return
!
(
el
instanceof
JXG
.
Image
)
&&
el
.
hasPoint
(
coords
.
scrCoords
[
1
],
coords
.
scrCoords
[
2
]);
};
return
_
.
filter
(
_
.
values
(
this
.
board
.
objects
),
filter
);
};
VectorDraw
.
prototype
.
onBoardDown
=
function
(
evt
)
{
this
.
pushHistory
();
// Can't create a vector if none is selected from the list.
var
selected
=
this
.
getSelectedElement
();
var
coords
=
this
.
getMouseCoords
(
evt
);
var
targetObjects
=
this
.
objectsUnderMouse
(
coords
);
if
(
selected
.
idx
&&
(
!
targetObjects
||
_
.
all
(
targetObjects
,
this
.
canCreateVectorOnTopOf
.
bind
(
this
))))
{
var
point_coords
=
[
coords
.
usrCoords
[
1
],
coords
.
usrCoords
[
2
]];
if
(
selected
.
type
===
'vector'
)
{
this
.
drawMode
=
true
;
this
.
dragged_vector
=
this
.
renderVector
(
selected
.
idx
,
[
point_coords
,
point_coords
]);
}
else
{
this
.
renderPoint
(
selected
.
idx
,
point_coords
);
}
}
else
{
this
.
drawMode
=
false
;
var
vectorPoint
=
_
.
find
(
targetObjects
,
this
.
getVectorForObject
.
bind
(
this
));
if
(
vectorPoint
)
{
this
.
dragged_vector
=
this
.
getVectorForObject
(
vectorPoint
);
this
.
dragged_vector
.
point1
.
setProperty
({
fixed
:
false
});
this
.
updateVectorProperties
(
this
.
dragged_vector
);
}
}
};
VectorDraw
.
prototype
.
onBoardMove
=
function
(
evt
)
{
if
(
this
.
drawMode
)
{
var
coords
=
this
.
getMouseCoords
(
evt
);
this
.
dragged_vector
.
point2
.
moveTo
(
coords
.
usrCoords
);
}
if
(
this
.
dragged_vector
)
{
this
.
updateVectorProperties
(
this
.
dragged_vector
);
}
};
VectorDraw
.
prototype
.
onBoardUp
=
function
(
evt
)
{
this
.
drawMode
=
false
;
if
(
this
.
dragged_vector
&&
!
this
.
isVectorTailDraggable
(
this
.
dragged_vector
))
{
this
.
dragged_vector
.
point1
.
setProperty
({
fixed
:
true
});
}
this
.
dragged_vector
=
null
;
};
VectorDraw
.
prototype
.
getVectorCoords
=
function
(
name
)
{
var
object
=
this
.
board
.
elementsByName
[
name
];
if
(
object
)
{
return
{
tail
:
[
object
.
point1
.
X
(),
object
.
point1
.
Y
()],
tip
:
[
object
.
point2
.
X
(),
object
.
point2
.
Y
()]
};
}
};
VectorDraw
.
prototype
.
getState
=
function
()
{
var
vectors
=
{},
points
=
{};
this
.
settings
.
vectors
.
forEach
(
function
(
vec
)
{
var
coords
=
this
.
getVectorCoords
(
vec
.
name
);
if
(
coords
)
{
vectors
[
vec
.
name
]
=
coords
;
}
},
this
);
this
.
settings
.
points
.
forEach
(
function
(
point
)
{
var
obj
=
this
.
board
.
elementsByName
[
point
.
name
];
if
(
obj
)
{
points
[
point
.
name
]
=
[
obj
.
X
(),
obj
.
Y
()];
}
},
this
);
return
{
vectors
:
vectors
,
points
:
points
};
};
VectorDraw
.
prototype
.
setState
=
function
(
state
)
{
this
.
settings
.
vectors
.
forEach
(
function
(
vec
,
idx
)
{
var
vec_state
=
state
.
vectors
[
vec
.
name
];
if
(
vec_state
)
{
this
.
renderVector
(
idx
,
[
vec_state
.
tail
,
vec_state
.
tip
]);
}
else
{
this
.
removeVector
(
idx
);
}
},
this
);
this
.
settings
.
points
.
forEach
(
function
(
point
,
idx
)
{
var
point_state
=
state
.
points
[
point
.
name
];
if
(
point_state
)
{
this
.
renderPoint
(
idx
,
point_state
);
}
else
{
this
.
removePoint
(
idx
);
}
},
this
);
this
.
board
.
update
();
};
$
(
function
(
$
)
{
$
(
function
(
$
)
{
/* Here's where you'd do things on page load. */
/* Here's where you'd do things on page load. */
var
vectordraw
=
new
VectorDraw
(
'vectordraw'
,
init_args
);
});
});
}
}
vectordraw/vectordraw.py
View file @
9a9a3241
"""TO-DO: Write a description of what this XBlock is."""
"""TO-DO: Write a description of what this XBlock is."""
import
json
import
pkg_resources
import
pkg_resources
from
xblock.core
import
XBlock
from
xblock.core
import
XBlock
from
xblock.fields
import
Scope
,
Integer
from
xblock.fields
import
Scope
,
Boolean
,
Integer
,
String
from
xblock.fragment
import
Fragment
from
xblock.fragment
import
Fragment
from
xblockutils.resources
import
ResourceLoader
from
xblockutils.studio_editable
import
StudioEditableXBlockMixin
class
VectorDrawXBlock
(
XBlock
):
loader
=
ResourceLoader
(
__name__
)
class
VectorDrawXBlock
(
StudioEditableXBlockMixin
,
XBlock
):
"""
"""
TO-DO: document what your XBlock does.
TO-DO: document what your XBlock does.
"""
"""
...
@@ -21,6 +27,185 @@ class VectorDrawXBlock(XBlock):
...
@@ -21,6 +27,185 @@ class VectorDrawXBlock(XBlock):
help
=
"A simple counter, to show something happening"
,
help
=
"A simple counter, to show something happening"
,
)
)
# Content
display_name
=
String
(
display_name
=
"Title (display name)"
,
help
=
"Title to display"
,
default
=
"Vector Drawing"
,
scope
=
Scope
.
content
)
width
=
Integer
(
display_name
=
"Width"
,
help
=
"The width of the board in pixels"
,
default
=
550
,
scope
=
Scope
.
content
)
height
=
Integer
(
display_name
=
""
,
help
=
"The height of the board in pixels"
,
default
=
400
,
scope
=
Scope
.
content
)
bounding_box_size
=
Integer
(
display_name
=
""
,
help
=
(
"Defines the bounding box height of the graph area. "
"The bounding box width is calculated from the width/height ratio."
),
default
=
10
,
scope
=
Scope
.
content
)
axis
=
Boolean
(
display_name
=
"Show axis"
,
help
=
"Show the graph axis"
,
default
=
False
,
scope
=
Scope
.
content
)
show_navigation
=
Boolean
(
display_name
=
"Show navigation"
,
help
=
"Show navigation arrows and zoom controls"
,
default
=
False
,
scope
=
Scope
.
content
)
show_vector_properties
=
Boolean
(
display_name
=
"Show vector properties"
,
help
=
"Show box detailing vector properties"
,
default
=
True
,
scope
=
Scope
.
content
)
show_slope_for_lines
=
Boolean
(
display_name
=
"Show slope for lines"
,
help
=
"If True, slope will be shown for line objects."
,
default
=
False
,
scope
=
Scope
.
content
)
add_vector_label
=
String
(
display_name
=
"Add vector label"
,
help
=
"Label for button that allows to add vectors to the board"
,
default
=
"Add Selected Force"
,
scope
=
Scope
.
content
)
vector_properties_label
=
String
(
display_name
=
"Vector properties label"
,
help
=
"Label for box that displays vector properties"
,
default
=
"Vector Properties"
,
scope
=
Scope
.
content
)
background_url
=
String
(
display_name
=
"Background URL"
,
help
=
"URL for background image"
,
default
=
""
,
scope
=
Scope
.
content
)
background_width
=
Integer
(
display_name
=
"Background width"
,
help
=
"Width of background image"
,
default
=
0
,
scope
=
Scope
.
content
)
background_height
=
Integer
(
display_name
=
"Background height"
,
help
=
"Height of background image"
,
default
=
0
,
scope
=
Scope
.
content
)
vectors
=
String
(
display_name
=
"Vectors"
,
help
=
(
"List of vectors to use for the exercise. "
"You must specify it as an array of entries where each entry represents an individual vector."
),
default
=
"[]"
,
multiline_editor
=
True
,
resettable_editor
=
False
,
scope
=
Scope
.
content
)
points
=
String
(
display_name
=
"Points"
,
help
=
(
"List of points to be drawn on the board for reference, or to be placed by the student."
"You must specify it as an array of entries where each entry represents an individual point."
),
default
=
"[]"
,
multiline_editor
=
True
,
resettable_editor
=
False
,
scope
=
Scope
.
content
)
expected_result
=
String
(
display_name
=
"Expected result"
,
help
=
(
"Defines vector properties for grading. "
"Vectors omitted from this setting are ignored when grading."
),
default
=
"{}"
,
multiline_editor
=
True
,
resettable_editor
=
False
,
scope
=
Scope
.
content
)
custom_checks
=
String
(
display_name
=
"Custom checks"
,
help
=
(
'List of custom checks to use for grading. '
'This is needed when grading is more complex and cannot be defined in terms of "Expected results" only.'
),
default
=
"[]"
,
multiline_editor
=
True
,
resettable_editor
=
False
,
scope
=
Scope
.
content
)
editable_fields
=
(
'display_name'
,
'width'
,
'height'
,
'bounding_box_size'
,
'axis'
,
'show_navigation'
,
'show_vector_properties'
,
'show_slope_for_lines'
,
'add_vector_label'
,
'vector_properties_label'
,
'background_url'
,
'background_width'
,
'background_height'
,
'vectors'
,
'points'
,
'expected_result'
,
'custom_checks'
)
@property
def
background
(
self
):
return
{
'src'
:
self
.
background_url
,
'width'
:
self
.
background_width
,
'height'
:
self
.
background_height
,
}
@property
def
vectors_json
(
self
):
return
json
.
loads
(
self
.
vectors
)
@property
def
points_json
(
self
):
return
json
.
loads
(
self
.
points
)
def
resource_string
(
self
,
path
):
def
resource_string
(
self
,
path
):
"""Handy helper for getting resources from our kit."""
"""Handy helper for getting resources from our kit."""
data
=
pkg_resources
.
resource_string
(
__name__
,
path
)
data
=
pkg_resources
.
resource_string
(
__name__
,
path
)
...
@@ -32,12 +217,29 @@ class VectorDrawXBlock(XBlock):
...
@@ -32,12 +217,29 @@ class VectorDrawXBlock(XBlock):
The primary view of the VectorDrawXBlock, shown to students
The primary view of the VectorDrawXBlock, shown to students
when viewing courses.
when viewing courses.
"""
"""
html
=
self
.
resource_string
(
"static/html/vectordraw.html"
)
fragment
=
Fragment
()
frag
=
Fragment
(
html
.
format
(
self
=
self
))
fragment
.
add_content
(
loader
.
render_template
(
'static/html/vectordraw.html'
,
context
))
frag
.
add_css
(
self
.
resource_string
(
"static/css/vectordraw.css"
))
fragment
.
add_css
(
self
.
resource_string
(
'static/css/vectordraw.css'
))
frag
.
add_javascript
(
self
.
resource_string
(
"static/js/src/vectordraw.js"
))
fragment
.
add_javascript_url
(
"//cdnjs.cloudflare.com/ajax/libs/jsxgraph/0.98/jsxgraphcore.js"
)
frag
.
initialize_js
(
'VectorDrawXBlock'
)
fragment
.
add_javascript
(
self
.
resource_string
(
"static/js/src/vectordraw.js"
))
return
frag
fragment
.
initialize_js
(
'VectorDrawXBlock'
,
{
'width'
:
self
.
width
,
'height'
:
self
.
height
,
'bounding_box_size'
:
self
.
bounding_box_size
,
'axis'
:
self
.
axis
,
'show_navigation'
:
self
.
show_navigation
,
'show_vector_properties'
:
self
.
show_vector_properties
,
'show_slope_for_lines'
:
self
.
show_slope_for_lines
,
'add_vector_label'
:
self
.
add_vector_label
,
'vector_properties_label'
:
self
.
vector_properties_label
,
'background'
:
self
.
background
,
'vectors'
:
self
.
vectors_json
,
'points'
:
self
.
points_json
,
}
)
return
fragment
# TO-DO: change this handler to perform your own actions. You may need more
# TO-DO: change this handler to perform your own actions. You may need more
# than one handler, or you may not need any handlers at all.
# than one handler, or you may not need any handlers at all.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment