Doenet / DoenetTools

https://www.doenet.org/
15 stars 29 forks source link

feature request: add an actionName of "cloneObject" to callAction #1910

Open virginiaMae opened 1 year ago

virginiaMae commented 1 year ago

clone geometric objects using the action triggers in the callAction component

dqnykamp commented 1 year ago

So, if you had <graph name="g"><point name="P"></graph> you'd want to have a <callAction target="P" actionName="clone" /> If you did that, what should happen? Is the idea that we'd create a new point with all the properties of P duplicated, the parent of P (g in this case) would get a new point child (maybe inserted right before or after P). It seems like you might want to specify some additional attributes for the new point, though <callAction> doesn't currently support arbitrary attributes and assignNames like <copy> does.

It seems to me this should be a completely different component than <callAction> as it should have these different attributes like <copy> does. The clone names could be determined by the attribute assignNames="name1 name2 name3". But, maybe we'd want to access them using array notation, i.e., by name_of_new_component[3].

This is sounding awfully similar to how a <map> works. I wondering if it could be sugar a <map> with a <sequence> source and some actions attached.

Question: could all this be accomplished with a map/sequence/updateValue given our current setup? Naturally, that's ugly and not ergonomic for the author. But, if it could, then maybe this cloneObject could just be a map under the hood.

virginiaMae commented 1 year ago

I agree that this is probably very similar to a map in its mechanics, but should be simpler to create for the author. Your description of the desired behavior is accurate.

The user should be able to access the clones either individually, using the array notation mentioned above (i.e. should not be required to use assignNames), or as a group collectively.

Another feature of "clone" behavior not mentioned, (which may be already partially supported by a map) is that the author should also be able to assign logic that checks against any of the members of the cloned group, not just an individual member. For example, if the "number2" clones are used in a prime factorization activity, the author should be able to detect if any clones of "number2" meet a certain criteria (such as occurring in a given region of a graph), and also how many clones meet this criteria. As another illustration, if "number2" tokens are falling from the sky in an arithmetic activity, the author should be able to use a triggerWhenObjectsClicked attribute that checks whether any one of the cloned objects in the group have been clicked.

Clones need to have a shared accessible property defined by the author for the individual clone component for use in validation logic. The type of shared property could vary with the activity - maybe it is a text, the letter "A" on a block, or a math - an expression that simplifies to "2" on a token, or an image - a colored spherical ball representing a proton.

dqnykamp commented 1 year ago

What about something like this?

<cloneGenerator name="cg"">
  <point>(1,2)</point>
</cloneGenerator>

The above case would not produce anything by default unless you specified the nClones attribute, which would start it with that many clones. One could base nClones on another component, such as nClones="$n", in which case the number of clones would change with $n. One could also use <updateValue> to update $cg.nClones, whether or not the nClones attribute was specified. The third proposed way to change the number of clones would be via the actions addClone and deleteClone, which would simply increment or decrements nClones.

As nClones is changed, the <cloneGenerator> (or whatever it should be called) will behave like a map. One could access all clones with $cg, a single clone with $cg[2]. We have not yet introduced slice notation like $cg[2..5] to refer to a range of clones, but we can discuss if that is a desired feature. One could also add an assignNames attribute to name specific clones.

I think we should try to make the validation features you request work with any collection of components (such as coming from a map or collect), not just clones. We could create some generalization of a <boolean>, such as <validate> that you could give <validate>$cg.x > 0</validate> and it would return the number of cloned points with a positive x-coordinate (or another mode where it returns the fraction of the clones with a positive x-coordinate). Probably need a better name than <validate>.

I assume for this "shared accessible property", you mean something in additional to the public state variables we have built into components. Are you thinking of the case where one would be cloning author-defined objects more complicated than <point>? Maybe we need a mechanism for authors to define properties on those objects.

virginiaMae commented 1 year ago

This all seems excellent, especially the use of array notation to reference a single clone, and the addClone and deleteClone actions. For a "shared accessible property", it's about attaching values like math or text to an abstract geometric object. As an illustration, we have a bunch of floating bubbles. They have maths associated (perhaps with a composition, or in a group). They are cloned. If the user clicks any of the bubble clones with the "1" attached, that value can be copied to a math expression below the graph. Maybe its as simple as the idea that the children of a composition can be referenced using the dot notation, so it could be $bubble.math.

I think this <validate> sounds kind of similar to <count>, which we already have, so maybe we can recycle it or upgrade it somehow? I'm guessing this could be used in an application where the student has to make a certain number of changes, and then the final configuration (or the snapshot of the intermediate configuration) gets awarded credit somehow.

What I'm stuck on is an application where interactions are being recorded in some kind of timed flow or animation, where we might want to access the clone that was "recently clicked?" to make changes to just that clone, or copy properties of just that clone based on some other logical check. Here is an idea that has lots of holes and questions in it but hopefully gets the point across...(imagine I took the time to animate the bubble clones floating up from the bottom...)

<cloneGenerator name="bubble" nClones="10">
  <composition anchor="($x, $y)">
    <circle position="center" />
    <math relativePosition="(+0.25, -0.25)">
      <selectFromSequence from="1" to="10"/>
    </math>
  </composition>
</cloneGenerator>

<graph>
  $bubble
</graph>

<updateValue target="bubble.hide" triggerWhen="$bubble.clicked = true and $bubble.math = 1"/>

<copyClone source="bubble.clicked" prop="math" />

Some questions are: how do we do this updateValue where we only hide the bubble that was just clicked, (do we need some kind of "recentlyClicked?") if it happens to have an associated math equal to 1? How do we then copy that associated math?
Another question that came to me with the example above: can we assign multiple initial values to the anchors or associated maths or styleNumbers of the components defined inside a cloneGenerator, and have the default behavior be to simply assign individual randomized selections to the different clones? Perhaps if we had them reference a <selectFromSequence>? What would we do if we had multiple <selectFromSequence> instances nested inside a <cloneGenerator>?