4
$\begingroup$

I have a document with lots of numbered equations in their own cells (which I numbered through Format->Style->DisplayFormulaNumbered). I also have lots of paragraphs of text which refer to these formulas. Say I have a paragraph that reads

"We see from (formulanumber) that xyz."

where formulanumber is the number of the relevant formula cell. And I want to do this in a way that means that if I move the original formula around, or move the text paragraph, the numbers update automatically, so the reference and the cell it refers to are constantly linked.

Is this possible? How do I do it?

$\endgroup$
2
+100
$\begingroup$

Here is one approach (code at the end):

The idea is to tag the cells via CellTags, which allows us to easily find the cell again using Cells[CellTags->…]. Having the CellObject, we can obtain the number of the cell using CurrentValue[cell,{"CounterValue",counter}]. The rest of the code is just some book-keeping.

One potential issue of this approach is that CellTags are copied together with the cell. This means that copying a cell breaks the unique correspondence between links and cells. I try to handle this by regenerating the id tag if it occurs more than once in the current notebook. Another approach might be to set CreateCellID to True for the notebook, and then use that instead of tags.

enter image description here

Code

The code has quite a few comments which should explain everything. Please feel free to leave a comment if something is still unclear.

CreatePalette[
 Button["Copy cell reference",
  Module[
   {cell, tags, tag, createTag},
   (* get the first equation cell from the current selection *)
   cell = First[
     Cells[NotebookSelection[InputNotebook[]], 
      CellStyle -> "DisplayFormulaNumbered"],
     MessageDialog["Select an equation cell!"];
     Return[]
     ];
   (* extract the CellTags and make sure they are in a list *)
   tags = 
    Replace[CurrentValue[cell, CellTags], t : Except@_List :> {t}];
   (* evaluating this will create a new unique tag for the current cell *)
   createTag := With[
     {newTag = CreateUUID["eqnID-"]},
     SetOptions[
      cell,
      (* filter out all other id tags (if any) and append the new one *)
      CellTags -> 
       Append[Select[tags, Not@*StringMatchQ["eqnID-*"]], newTag]
      ];
     newTag
     ];
   (* get the first id tag from the cell, 
   and create a new one if there is none *)
   tag = SelectFirst[
     tags,
     StringMatchQ@"eqnID-*",
     createTag
     ];
   (* if the tag is not unique (happens if the cell was copied), 
   recreate it. This breaks all existing links to the cell, 
   but those point to another cell *)
   If[Length@Cells[InputNotebook[], CellTags -> tag] > 1,
    MessageDialog["Multiple cells with same tag found!
     Tag of current cell will be regenerated."];
    tag = createTag
    ];
   With[(* insert the tag into the Dynamic below *)
    {tag = tag},
    (* copy the resulting expression to the clipboard for easy pasting *)
    CopyToClipboard@Button[
      Row@{
        "(",
        (* 
        dynamically get the current number of the cell with the given id tag *)
        Dynamic@CurrentValue[
          First[Cells[CellTags -> tag], $Failed],
          {"CounterValue", "DisplayFormulaNumbered"}
          ],
        ")"
        },
      (* if the link is pressed, 
      select the cell with the matching tag *)
      NotebookLocate@tag,
      BaseStyle -> "Link",
      Appearance -> None
      ]
    ]
   ]
  ],
 Saveable -> False,
 WindowTitle -> "Equation linking"
 ]
$\endgroup$
  • $\begingroup$ Hi @Lukas, this looks great. I'm not clear how I actually implement this, though. Does the code go at the beginning of the notebook, and I evaluate it before I start work on the notebook? (I'm still basically a newb) $\endgroup$ – Richard Burke-Ward Apr 29 at 14:54
  • $\begingroup$ You can put the code in a same notebook, but a separate one will also work. Evaluating it once will create the palette window with the button that you can use to create the links. The palette is only needed to create the links, they should work fine without the palette or the code after that. If you plan to use it more often, the easiest is to install the palette: To do this, evaluate the code to get the palette. After that, go to Palettes > Install Palette. Select "Equation linking" as "Source", give it a name, and press "Ok". After that, you can always open the palette from the Palettes menu $\endgroup$ – Lukas Lang Apr 29 at 15:30
2
$\begingroup$

First, here's how the documentation expects you to create references for automatic numbering. Let's create some cells with automatic numbering, and give each cell a cell tag option:

SeedRandom[1]
CellPrint /@ Table[Cell[w, "DisplayFormulaNumbered", CellTags->w], {w, RandomWord[5]}];

enter image description here

Now, to create a reference to the "hurtle" formula, you would use the menu item Insert | Automatic Numbering ..., which opens up the following dialog:

enter image description here

Change the Counter to "DisplayFormulaNumbered" and select "hurtle", and a reference to the "hurtle" cell will be created.

Lucas provided a palette to streamline this, by making it easier to find the cell reference (and by automatically creating a randomized cell tag if needed). Instead, it is also possible to copy the cell reference by modifying the style sheet. For instance:

Module[{event, label},
    event = "MouseClicked" :> CopyToClipboard @ Cell @ TextData[{
        CounterBox[
            "DisplayFormulaNumbered",
            Replace[CurrentValue[ParentCell @ ParentCell @ EvaluationCell[], CellTags], l_List :> First@l]
        ]
    }];
    label = Cell[
        TextData[{
            "(",
            Cell @ BoxData @ TagBox[
                CounterBox["DisplayFormulaNumbered"],
                EventHandlerTag[{event}]
            ],
            ")"
        }],
        "DisplayFormulaEquationNumber"
    ];
    SetOptions[
        EvaluationNotebook[],
        StyleDefinitions -> Notebook[
            {
            Cell[StyleData[StyleDefinitions->"Default.nb"]],
            Cell[StyleData["DisplayFormulaNumbered"],
                CellFrameLabels -> {{None, label}, {None, None}}
            ]
            },
            StyleDefinitions->"PrivateStylesheetFormatting.nb"
        ]
    ]
]

The stylesheet works by including an event handler that copies the cell reference to the clipboard when clicked. Inside of a CellFrameLabels option, the EvaluationCell[] refers to the Cell @ BoxData @ TagBox[_CounterBox, _EventHandler] cell. The parent cell of that evaluation cell is the Cell[TextData[{"(", .., ")"}], "DisplayFormulaEquationNumber"] cell, and the parent cell of that cell is the main cell, which should have the CellTags option set. Here's a short animation of the stylesheet in action:

enter image description here

Now, suppose the formula to be referenced is far away, and you don't want to look for it in order to click on it? In that case, it would be convenient if there were a short cut to bring up the menu of relevant cell tags to use. Here is an input alias that does this:

CurrentValue[EvaluationNotebook[], {InputAliases, "dfn"}] = DynamicBox[
    ToBoxes[
        ActionMenu[
            "tags",
            With[{tags = CurrentValue[Cells[CellStyle->"DisplayFormulaNumbered"], CellTags]},
                #:>NotebookWrite[
                    ParentBox@EvaluationBox[],
                    CounterBox["DisplayFormulaNumbered", #]
                ]& /@ tags
            ]
        ]
    ]
];

Here is an animation of the input alias in action:

enter image description here

Finally, if the cell tags were randomized UUID strings (as in Lucas' answer), the input alias wouldn't be very useful, which is why I recommend assigning meaningful cell tags to your formula cells, as meaningful cell tags are needed when trying to select the right cell tag from a list of cell tags.

$\endgroup$
  • $\begingroup$ we like it as built-in functionality in Mathematica. $\endgroup$ – PureLine May 2 at 1:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.