@doenet/doenetml 0.6.0-alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (549) hide show
  1. package/.prettierrc +3 -0
  2. package/LICENSE +661 -0
  3. package/README.md +146 -0
  4. package/cypress/e2e/ActivityViewer/activityVariants.cy.js +1770 -0
  5. package/cypress/e2e/ActivityViewer/compiledActivity.cy.js +83 -0
  6. package/cypress/e2e/ActivityViewer/relationshipsAmongPages.cy.js +697 -0
  7. package/cypress/e2e/answerValidation/errorinnumbers.cy.js +2125 -0
  8. package/cypress/e2e/answerValidation/factoring.cy.js +1945 -0
  9. package/cypress/e2e/answerValidation/factoringOldAlgorithm.cy.js +892 -0
  10. package/cypress/e2e/answerValidation/functionanswers.cy.js +314 -0
  11. package/cypress/e2e/answerValidation/matchingpatterns.cy.js +287 -0
  12. package/cypress/e2e/answerValidation/matchpartial.cy.js +6711 -0
  13. package/cypress/e2e/answerValidation/pointlocation.cy.js +3989 -0
  14. package/cypress/e2e/answerValidation/symbolicequality.cy.js +1893 -0
  15. package/cypress/e2e/answerValidation/videoProgress.cy.js +210 -0
  16. package/cypress/e2e/assignNames/basiccopy.cy.js +2376 -0
  17. package/cypress/e2e/assignNames/collections.cy.js +9247 -0
  18. package/cypress/e2e/assignNames/selects.cy.js +105 -0
  19. package/cypress/e2e/assignNames/sequences.cy.js +1964 -0
  20. package/cypress/e2e/baseComponent/basecomponentproperties.cy.js +999 -0
  21. package/cypress/e2e/baseComponent/doenetMLtext.cy.js +427 -0
  22. package/cypress/e2e/chemistry/atom.cy.js +201 -0
  23. package/cypress/e2e/chemistry/ion.cy.js +608 -0
  24. package/cypress/e2e/chemistry/ioniccompound.cy.js +133 -0
  25. package/cypress/e2e/dynamicalsystem/cobwebpolyline.cy.js +2653 -0
  26. package/cypress/e2e/dynamicalsystem/equilibriumcurve.cy.js +311 -0
  27. package/cypress/e2e/dynamicalsystem/equilibriumline.cy.js +279 -0
  28. package/cypress/e2e/dynamicalsystem/equilibriumpoint.cy.js +283 -0
  29. package/cypress/e2e/dynamicalsystem/odesystem.cy.js +1834 -0
  30. package/cypress/e2e/equality/mathexpressions.cy.js +948 -0
  31. package/cypress/e2e/graphing/graphreferences.cy.js +978 -0
  32. package/cypress/e2e/graphing/graphreferences2.cy.js +615 -0
  33. package/cypress/e2e/linearAlgebra/eigenDecomposition.cy.js +401 -0
  34. package/cypress/e2e/tagSpecific/angle.cy.js +3898 -0
  35. package/cypress/e2e/tagSpecific/animatefromsequence.cy.js +2306 -0
  36. package/cypress/e2e/tagSpecific/answer.cy.js +31647 -0
  37. package/cypress/e2e/tagSpecific/bestfitline.cy.js +612 -0
  38. package/cypress/e2e/tagSpecific/blockquote.cy.js +30 -0
  39. package/cypress/e2e/tagSpecific/boolean.cy.js +742 -0
  40. package/cypress/e2e/tagSpecific/booleaninput.cy.js +1283 -0
  41. package/cypress/e2e/tagSpecific/booleanlist.cy.js +588 -0
  42. package/cypress/e2e/tagSpecific/booleanoperators.cy.js +596 -0
  43. package/cypress/e2e/tagSpecific/booleanoperatorsonmath.cy.js +498 -0
  44. package/cypress/e2e/tagSpecific/callaction.cy.js +2835 -0
  45. package/cypress/e2e/tagSpecific/choiceinput.cy.js +3205 -0
  46. package/cypress/e2e/tagSpecific/circle.cy.js +22036 -0
  47. package/cypress/e2e/tagSpecific/codeeditor.cy.js +1995 -0
  48. package/cypress/e2e/tagSpecific/collect.cy.js +5035 -0
  49. package/cypress/e2e/tagSpecific/componentsize.cy.js +502 -0
  50. package/cypress/e2e/tagSpecific/conditionalcontent.cy.js +3495 -0
  51. package/cypress/e2e/tagSpecific/contentBrowser.cy.js +335 -0
  52. package/cypress/e2e/tagSpecific/contentpicker.cy.js +261 -0
  53. package/cypress/e2e/tagSpecific/copy.cy.js +12627 -0
  54. package/cypress/e2e/tagSpecific/copy2.cy.js +5698 -0
  55. package/cypress/e2e/tagSpecific/curve.bezier.cy.js +12440 -0
  56. package/cypress/e2e/tagSpecific/curve.cy.js +1716 -0
  57. package/cypress/e2e/tagSpecific/curve.function.cy.js +1471 -0
  58. package/cypress/e2e/tagSpecific/curve.parametrized.cy.js +920 -0
  59. package/cypress/e2e/tagSpecific/document.cy.js +234 -0
  60. package/cypress/e2e/tagSpecific/endpoint.cy.js +197 -0
  61. package/cypress/e2e/tagSpecific/evaluate.cy.js +8895 -0
  62. package/cypress/e2e/tagSpecific/extract.cy.js +2282 -0
  63. package/cypress/e2e/tagSpecific/feedback.cy.js +2941 -0
  64. package/cypress/e2e/tagSpecific/function.cy.js +9450 -0
  65. package/cypress/e2e/tagSpecific/functioniterates.cy.js +1178 -0
  66. package/cypress/e2e/tagSpecific/functionoperators.cy.js +4047 -0
  67. package/cypress/e2e/tagSpecific/graph.cy.js +2491 -0
  68. package/cypress/e2e/tagSpecific/group.cy.js +683 -0
  69. package/cypress/e2e/tagSpecific/hint.cy.js +204 -0
  70. package/cypress/e2e/tagSpecific/image.cy.js +770 -0
  71. package/cypress/e2e/tagSpecific/integer.cy.js +206 -0
  72. package/cypress/e2e/tagSpecific/label.cy.js +800 -0
  73. package/cypress/e2e/tagSpecific/legend.cy.js +1001 -0
  74. package/cypress/e2e/tagSpecific/line.cy.js +12167 -0
  75. package/cypress/e2e/tagSpecific/linesegment.cy.js +4749 -0
  76. package/cypress/e2e/tagSpecific/lorem.cy.js +289 -0
  77. package/cypress/e2e/tagSpecific/map.cy.js +4476 -0
  78. package/cypress/e2e/tagSpecific/matchespattern.cy.js +693 -0
  79. package/cypress/e2e/tagSpecific/math.cy.js +10990 -0
  80. package/cypress/e2e/tagSpecific/mathdisplay.cy.js +2689 -0
  81. package/cypress/e2e/tagSpecific/mathinput.cy.js +15628 -0
  82. package/cypress/e2e/tagSpecific/mathinputgraph.cy.js +566 -0
  83. package/cypress/e2e/tagSpecific/mathlist.cy.js +4073 -0
  84. package/cypress/e2e/tagSpecific/mathoperators.cy.js +13851 -0
  85. package/cypress/e2e/tagSpecific/matrix.cy.js +8825 -0
  86. package/cypress/e2e/tagSpecific/matrixinput.cy.js +16277 -0
  87. package/cypress/e2e/tagSpecific/module.cy.js +1771 -0
  88. package/cypress/e2e/tagSpecific/number.cy.js +2221 -0
  89. package/cypress/e2e/tagSpecific/numberlist.cy.js +1285 -0
  90. package/cypress/e2e/tagSpecific/p.cy.js +72 -0
  91. package/cypress/e2e/tagSpecific/paginator.cy.js +2983 -0
  92. package/cypress/e2e/tagSpecific/parabola.cy.js +14331 -0
  93. package/cypress/e2e/tagSpecific/paragraphmarkup.cy.js +104 -0
  94. package/cypress/e2e/tagSpecific/periodicset.cy.js +1439 -0
  95. package/cypress/e2e/tagSpecific/piecewisefunction.cy.js +1055 -0
  96. package/cypress/e2e/tagSpecific/pluralize.cy.js +274 -0
  97. package/cypress/e2e/tagSpecific/point.cy.js +8895 -0
  98. package/cypress/e2e/tagSpecific/point2.cy.js +10259 -0
  99. package/cypress/e2e/tagSpecific/polygon.cy.js +5039 -0
  100. package/cypress/e2e/tagSpecific/polyline.cy.js +4704 -0
  101. package/cypress/e2e/tagSpecific/problem.cy.js +2768 -0
  102. package/cypress/e2e/tagSpecific/ray.cy.js +10770 -0
  103. package/cypress/e2e/tagSpecific/rectangle.cy.js +2143 -0
  104. package/cypress/e2e/tagSpecific/ref.cy.js +420 -0
  105. package/cypress/e2e/tagSpecific/regularPolygon.cy.js +1006 -0
  106. package/cypress/e2e/tagSpecific/regularPolygon2.cy.js +100 -0
  107. package/cypress/e2e/tagSpecific/regularPolygon3.cy.js +777 -0
  108. package/cypress/e2e/tagSpecific/samplerandomnumbers.cy.js +3619 -0
  109. package/cypress/e2e/tagSpecific/sectioning.cy.js +3530 -0
  110. package/cypress/e2e/tagSpecific/select.cy.js +5376 -0
  111. package/cypress/e2e/tagSpecific/selectfromsequence.cy.js +3846 -0
  112. package/cypress/e2e/tagSpecific/selectrandomnumbers.cy.js +2914 -0
  113. package/cypress/e2e/tagSpecific/sequence.cy.js +2093 -0
  114. package/cypress/e2e/tagSpecific/shuffle.cy.js +490 -0
  115. package/cypress/e2e/tagSpecific/sidebyside.cy.js +8057 -0
  116. package/cypress/e2e/tagSpecific/singlecharactercomponents.cy.js +72 -0
  117. package/cypress/e2e/tagSpecific/slider.cy.js +914 -0
  118. package/cypress/e2e/tagSpecific/solution.cy.js +109 -0
  119. package/cypress/e2e/tagSpecific/solveequations.cy.js +1026 -0
  120. package/cypress/e2e/tagSpecific/sort.cy.js +1685 -0
  121. package/cypress/e2e/tagSpecific/spreadsheet.cy.js +5971 -0
  122. package/cypress/e2e/tagSpecific/subsetofreals.cy.js +2725 -0
  123. package/cypress/e2e/tagSpecific/substitute.cy.js +2646 -0
  124. package/cypress/e2e/tagSpecific/tabular.cy.js +36 -0
  125. package/cypress/e2e/tagSpecific/text.cy.js +975 -0
  126. package/cypress/e2e/tagSpecific/textinput.cy.js +2177 -0
  127. package/cypress/e2e/tagSpecific/textlist.cy.js +369 -0
  128. package/cypress/e2e/tagSpecific/triangle.cy.js +1936 -0
  129. package/cypress/e2e/tagSpecific/triggerset.cy.js +2023 -0
  130. package/cypress/e2e/tagSpecific/updatevalue.cy.js +3288 -0
  131. package/cypress/e2e/tagSpecific/vector.cy.js +20183 -0
  132. package/cypress/e2e/tagSpecific/video.cy.js +612 -0
  133. package/cypress/e2e/tagSpecific/when.cy.js +202 -0
  134. package/cypress/e2e/variants/specifysinglevariant.cy.js +6726 -0
  135. package/cypress/e2e/variants/uniquevariants.cy.js +4846 -0
  136. package/cypress/fixtures/example.json +5 -0
  137. package/cypress/support/commands.js +32 -0
  138. package/cypress/support/e2e.js +31 -0
  139. package/cypress.config.js +18 -0
  140. package/docs/codeSnippet.jsx +11 -0
  141. package/docs/index.html +133 -0
  142. package/docs/index.jsx +138 -0
  143. package/docs/prism.css +3 -0
  144. package/index.html +14 -0
  145. package/index.js +21 -0
  146. package/media/answer_example.png +0 -0
  147. package/media/graph_example.png +0 -0
  148. package/media/graph_markup_example.png +0 -0
  149. package/package.json +83 -0
  150. package/public/favicon.ico +0 -0
  151. package/public/fonts/files/open-sans-v18-latin-700.woff +0 -0
  152. package/public/fonts/files/open-sans-v18-latin-700.woff2 +0 -0
  153. package/public/fonts/files/open-sans-v18-latin-700italic.woff +0 -0
  154. package/public/fonts/files/open-sans-v18-latin-700italic.woff2 +0 -0
  155. package/public/fonts/files/open-sans-v18-latin-italic.woff +0 -0
  156. package/public/fonts/files/open-sans-v18-latin-italic.woff2 +0 -0
  157. package/public/fonts/files/open-sans-v18-latin-light-italic.woff +0 -0
  158. package/public/fonts/files/open-sans-v18-latin-light-italic.woff2 +0 -0
  159. package/public/fonts/files/open-sans-v18-latin-light.woff +0 -0
  160. package/public/fonts/files/open-sans-v18-latin-light.woff2 +0 -0
  161. package/public/fonts/files/open-sans-v18-latin-regular.woff +0 -0
  162. package/public/fonts/files/open-sans-v18-latin-regular.woff2 +0 -0
  163. package/src/Core/ComponentTypes.js +426 -0
  164. package/src/Core/Core.js +11852 -0
  165. package/src/Core/CoreWorker.js +127 -0
  166. package/src/Core/Dependencies.js +8226 -0
  167. package/src/Core/Numerics.js +473 -0
  168. package/src/Core/ParameterStack.js +36 -0
  169. package/src/Core/ReadOnlyProxyHandler.js +41 -0
  170. package/src/Core/StateProxyHandler.js +88 -0
  171. package/src/Core/components/Aliases.js +67 -0
  172. package/src/Core/components/Angle.js +758 -0
  173. package/src/Core/components/AnimateFromSequence.js +922 -0
  174. package/src/Core/components/Answer.js +2087 -0
  175. package/src/Core/components/AsList.js +83 -0
  176. package/src/Core/components/AttractTo.js +245 -0
  177. package/src/Core/components/AttractToAngles.js +262 -0
  178. package/src/Core/components/AttractToConstraint.js +104 -0
  179. package/src/Core/components/AttractToGrid.js +315 -0
  180. package/src/Core/components/Award.js +906 -0
  181. package/src/Core/components/BestFitLine.js +318 -0
  182. package/src/Core/components/BezierControls.js +719 -0
  183. package/src/Core/components/BlockQuote.js +35 -0
  184. package/src/Core/components/Boolean.js +500 -0
  185. package/src/Core/components/BooleanInput.js +330 -0
  186. package/src/Core/components/BooleanList.js +396 -0
  187. package/src/Core/components/BooleanOperators.js +35 -0
  188. package/src/Core/components/BooleanOperatorsOfMath.js +148 -0
  189. package/src/Core/components/CallAction.js +261 -0
  190. package/src/Core/components/Caption.js +73 -0
  191. package/src/Core/components/Case.js +56 -0
  192. package/src/Core/components/Cell.js +439 -0
  193. package/src/Core/components/CellBlock.js +64 -0
  194. package/src/Core/components/Chart.js +795 -0
  195. package/src/Core/components/Choice.js +266 -0
  196. package/src/Core/components/ChoiceInput.js +1407 -0
  197. package/src/Core/components/Circle.js +2884 -0
  198. package/src/Core/components/CodeEditor.js +647 -0
  199. package/src/Core/components/CodeViewer.js +294 -0
  200. package/src/Core/components/CollaborateGroupSetup.js +46 -0
  201. package/src/Core/components/CollaborateGroups.js +119 -0
  202. package/src/Core/components/Collect.js +850 -0
  203. package/src/Core/components/Column.js +608 -0
  204. package/src/Core/components/ConditionalContent.js +468 -0
  205. package/src/Core/components/ConsiderAsResponses.js +49 -0
  206. package/src/Core/components/ConstrainTo.js +161 -0
  207. package/src/Core/components/ConstrainToAngles.js +244 -0
  208. package/src/Core/components/ConstrainToGraph.js +142 -0
  209. package/src/Core/components/ConstrainToGrid.js +175 -0
  210. package/src/Core/components/ConstraintUnion.js +119 -0
  211. package/src/Core/components/Constraints.js +497 -0
  212. package/src/Core/components/ContentBrowser.js +441 -0
  213. package/src/Core/components/ContentPicker.js +263 -0
  214. package/src/Core/components/ControlVectors.js +25 -0
  215. package/src/Core/components/Coords.js +63 -0
  216. package/src/Core/components/Copy.js +3412 -0
  217. package/src/Core/components/Curve.js +4130 -0
  218. package/src/Core/components/CustomAttribute.js +175 -0
  219. package/src/Core/components/DataFrame.js +357 -0
  220. package/src/Core/components/DiscreteSimulationResultList.js +342 -0
  221. package/src/Core/components/DiscreteSimulationResultPolyline.js +581 -0
  222. package/src/Core/components/Divisions.js +55 -0
  223. package/src/Core/components/Document.js +888 -0
  224. package/src/Core/components/Embed.js +65 -0
  225. package/src/Core/components/Endpoint.js +62 -0
  226. package/src/Core/components/Evaluate.js +321 -0
  227. package/src/Core/components/Extract.js +656 -0
  228. package/src/Core/components/Extrema.js +556 -0
  229. package/src/Core/components/Feedback.js +200 -0
  230. package/src/Core/components/FeedbackDefinitions.js +97 -0
  231. package/src/Core/components/Figure.js +148 -0
  232. package/src/Core/components/Footnote.js +73 -0
  233. package/src/Core/components/Function.js +5344 -0
  234. package/src/Core/components/FunctionIterates.js +306 -0
  235. package/src/Core/components/FunctionOperators.js +702 -0
  236. package/src/Core/components/Graph.js +1679 -0
  237. package/src/Core/components/Group.js +7 -0
  238. package/src/Core/components/HasSameFactoring.js +407 -0
  239. package/src/Core/components/Hint.js +241 -0
  240. package/src/Core/components/Image.js +524 -0
  241. package/src/Core/components/Indexing.js +79 -0
  242. package/src/Core/components/IntComma.js +64 -0
  243. package/src/Core/components/Integer.js +81 -0
  244. package/src/Core/components/Intersection.js +328 -0
  245. package/src/Core/components/Interval.js +29 -0
  246. package/src/Core/components/Label.js +492 -0
  247. package/src/Core/components/Latex.js +104 -0
  248. package/src/Core/components/Legend.js +329 -0
  249. package/src/Core/components/Line.js +2040 -0
  250. package/src/Core/components/LineSegment.js +882 -0
  251. package/src/Core/components/Lists.js +180 -0
  252. package/src/Core/components/Lorem.js +249 -0
  253. package/src/Core/components/MMeMen.js +377 -0
  254. package/src/Core/components/Map.js +873 -0
  255. package/src/Core/components/Markers.js +101 -0
  256. package/src/Core/components/MatchesPattern.js +339 -0
  257. package/src/Core/components/Math.js +2552 -0
  258. package/src/Core/components/MathInput.js +948 -0
  259. package/src/Core/components/MathList.js +828 -0
  260. package/src/Core/components/MathOperators.js +1286 -0
  261. package/src/Core/components/Matrix.js +497 -0
  262. package/src/Core/components/MatrixInput.js +3157 -0
  263. package/src/Core/components/MdMdnMrow.js +394 -0
  264. package/src/Core/components/Module.js +16 -0
  265. package/src/Core/components/Number.js +1031 -0
  266. package/src/Core/components/NumberList.js +550 -0
  267. package/src/Core/components/Option.js +24 -0
  268. package/src/Core/components/P.js +71 -0
  269. package/src/Core/components/Paginator.js +338 -0
  270. package/src/Core/components/Panel.js +126 -0
  271. package/src/Core/components/Parabola.js +1561 -0
  272. package/src/Core/components/ParagraphMarkup.js +59 -0
  273. package/src/Core/components/Pegboard.js +43 -0
  274. package/src/Core/components/PeriodicSet.js +291 -0
  275. package/src/Core/components/PiecewiseFunction.js +832 -0
  276. package/src/Core/components/Pluralize.js +198 -0
  277. package/src/Core/components/Point.js +1295 -0
  278. package/src/Core/components/Polygon.js +408 -0
  279. package/src/Core/components/Polyline.js +841 -0
  280. package/src/Core/components/RandomizedTextList.js +225 -0
  281. package/src/Core/components/Ray.js +1737 -0
  282. package/src/Core/components/Rectangle.js +1535 -0
  283. package/src/Core/components/Ref.js +350 -0
  284. package/src/Core/components/RegionBetweenCurveXAxis.js +124 -0
  285. package/src/Core/components/RegionHalfPlane.js +107 -0
  286. package/src/Core/components/RegularPolygon.js +2118 -0
  287. package/src/Core/components/RenderDoenetML.js +181 -0
  288. package/src/Core/components/Row.js +780 -0
  289. package/src/Core/components/SampleRandomNumbers.js +653 -0
  290. package/src/Core/components/Sectioning.js +303 -0
  291. package/src/Core/components/Select.js +947 -0
  292. package/src/Core/components/SelectFromSequence.js +1242 -0
  293. package/src/Core/components/SelectRandomNumbers.js +225 -0
  294. package/src/Core/components/Sequence.js +444 -0
  295. package/src/Core/components/Setup.js +53 -0
  296. package/src/Core/components/Shuffle.js +470 -0
  297. package/src/Core/components/SideBySide.js +2130 -0
  298. package/src/Core/components/SingleCharacterComponents.js +41 -0
  299. package/src/Core/components/Slider.js +819 -0
  300. package/src/Core/components/SolutionContainer.js +67 -0
  301. package/src/Core/components/Solutions.js +334 -0
  302. package/src/Core/components/SolveEquations.js +568 -0
  303. package/src/Core/components/Sort.js +398 -0
  304. package/src/Core/components/Sources.js +108 -0
  305. package/src/Core/components/Split.js +205 -0
  306. package/src/Core/components/Spreadsheet.js +1507 -0
  307. package/src/Core/components/StyleDefinitions.js +111 -0
  308. package/src/Core/components/SubsetOfReals.js +348 -0
  309. package/src/Core/components/SubsetOfRealsInput.js +1474 -0
  310. package/src/Core/components/Substitute.js +496 -0
  311. package/src/Core/components/SummaryStatistics.js +652 -0
  312. package/src/Core/components/Table.js +145 -0
  313. package/src/Core/components/Tabular.js +384 -0
  314. package/src/Core/components/Template.js +360 -0
  315. package/src/Core/components/Text.js +341 -0
  316. package/src/Core/components/TextInput.js +566 -0
  317. package/src/Core/components/TextList.js +442 -0
  318. package/src/Core/components/TextListFromString.js +137 -0
  319. package/src/Core/components/TextOperatorsOfMath.js +21 -0
  320. package/src/Core/components/Triangle.js +280 -0
  321. package/src/Core/components/TriggerSet.js +189 -0
  322. package/src/Core/components/TupleList.js +43 -0
  323. package/src/Core/components/UpdateValue.js +435 -0
  324. package/src/Core/components/VariantControl.js +36 -0
  325. package/src/Core/components/Vector.js +2478 -0
  326. package/src/Core/components/Verbatim.js +125 -0
  327. package/src/Core/components/Video.js +673 -0
  328. package/src/Core/components/When.js +198 -0
  329. package/src/Core/components/abstract/AngleListComponent.js +140 -0
  330. package/src/Core/components/abstract/BaseComponent.js +1496 -0
  331. package/src/Core/components/abstract/BlockComponent.js +5 -0
  332. package/src/Core/components/abstract/BooleanBaseOperator.js +88 -0
  333. package/src/Core/components/abstract/BooleanBaseOperatorOfMath.js +100 -0
  334. package/src/Core/components/abstract/BooleanBaseOperatorOneInput.js +44 -0
  335. package/src/Core/components/abstract/ComponentSize.js +789 -0
  336. package/src/Core/components/abstract/ComponentWithSelectableType.js +537 -0
  337. package/src/Core/components/abstract/CompositeComponent.js +142 -0
  338. package/src/Core/components/abstract/ConstraintComponent.js +19 -0
  339. package/src/Core/components/abstract/FunctionBaseOperator.js +680 -0
  340. package/src/Core/components/abstract/GraphicalComponent.js +56 -0
  341. package/src/Core/components/abstract/InlineComponent.js +5 -0
  342. package/src/Core/components/abstract/InlineRenderInlineChildren.js +63 -0
  343. package/src/Core/components/abstract/Input.js +192 -0
  344. package/src/Core/components/abstract/IntervalListComponent.js +218 -0
  345. package/src/Core/components/abstract/LineListComponent.js +114 -0
  346. package/src/Core/components/abstract/MathBaseOperator.js +631 -0
  347. package/src/Core/components/abstract/MathBaseOperatorOneInput.js +112 -0
  348. package/src/Core/components/abstract/PointListComponent.js +238 -0
  349. package/src/Core/components/abstract/SectioningComponent.js +1262 -0
  350. package/src/Core/components/abstract/SingleCharacterInline.js +23 -0
  351. package/src/Core/components/abstract/TextBaseOperatorOfMath.js +47 -0
  352. package/src/Core/components/abstract/TextOrInline.js +66 -0
  353. package/src/Core/components/abstract/VariableName.js +31 -0
  354. package/src/Core/components/abstract/VariableNameList.js +83 -0
  355. package/src/Core/components/abstract/VectorListComponent.js +235 -0
  356. package/src/Core/components/chemistry/Atom.js +910 -0
  357. package/src/Core/components/chemistry/ElectronConfiguration.js +36 -0
  358. package/src/Core/components/chemistry/Ion.js +684 -0
  359. package/src/Core/components/chemistry/IonicCompound.js +189 -0
  360. package/src/Core/components/chemistry/OrbitalDiagram.js +175 -0
  361. package/src/Core/components/chemistry/OrbitalDiagramInput.js +753 -0
  362. package/src/Core/components/chemistry/index.js +6 -0
  363. package/src/Core/components/commonsugar/breakstrings.js +627 -0
  364. package/src/Core/components/commonsugar/lists.js +177 -0
  365. package/src/Core/components/dynamicalSystems/CobwebPolyline.js +913 -0
  366. package/src/Core/components/dynamicalSystems/EquilibriumCurve.js +95 -0
  367. package/src/Core/components/dynamicalSystems/EquilibriumLine.js +93 -0
  368. package/src/Core/components/dynamicalSystems/EquilibriumPoint.js +93 -0
  369. package/src/Core/components/dynamicalSystems/ODESystem.js +943 -0
  370. package/src/Core/components/dynamicalSystems/index.js +5 -0
  371. package/src/Core/components/linearAlgebra/EigenDecomposition.js +294 -0
  372. package/src/Core/utils/array.js +30 -0
  373. package/src/Core/utils/booleanLogic.js +965 -0
  374. package/src/Core/utils/checkEquality.js +818 -0
  375. package/src/Core/utils/cid.js +29 -0
  376. package/src/Core/utils/componentInfoObjects.js +100 -0
  377. package/src/Core/utils/constraints.js +23 -0
  378. package/src/Core/utils/copy.js +572 -0
  379. package/src/Core/utils/deepFunctions.js +173 -0
  380. package/src/Core/utils/descendants.js +252 -0
  381. package/src/Core/utils/enumeration.js +234 -0
  382. package/src/Core/utils/feedback.js +84 -0
  383. package/src/Core/utils/function.js +1343 -0
  384. package/src/Core/utils/graphical.js +196 -0
  385. package/src/Core/utils/label.js +396 -0
  386. package/src/Core/utils/math.js +1056 -0
  387. package/src/Core/utils/naming.js +45 -0
  388. package/src/Core/utils/periodicSetEquality.js +403 -0
  389. package/src/Core/utils/randomNumbers.js +70 -0
  390. package/src/Core/utils/retrieveMedia.js +98 -0
  391. package/src/Core/utils/retrieveTextFile.js +140 -0
  392. package/src/Core/utils/returnAllPossibleVariants.js +73 -0
  393. package/src/Core/utils/rounding.js +316 -0
  394. package/src/Core/utils/sequence.js +754 -0
  395. package/src/Core/utils/serializedStateProcessing.js +4049 -0
  396. package/src/Core/utils/size.js +22 -0
  397. package/src/Core/utils/stateVariables.js +138 -0
  398. package/src/Core/utils/style.js +535 -0
  399. package/src/Core/utils/subset-of-reals.js +796 -0
  400. package/src/Core/utils/table.js +41 -0
  401. package/src/Core/utils/text.js +16 -0
  402. package/src/Core/utils/triggering.js +167 -0
  403. package/src/Core/utils/variants.js +477 -0
  404. package/src/DoenetML.css +308 -0
  405. package/src/DoenetML.jsx +201 -0
  406. package/src/Parser/doenet.grammar +90 -0
  407. package/src/Parser/doenet.js +33 -0
  408. package/src/Parser/doenet.terms.js +20 -0
  409. package/src/Parser/parser.js +266 -0
  410. package/src/Parser/tokens.js +129 -0
  411. package/src/Tools/CodeMirror.jsx +440 -0
  412. package/src/Tools/DarkmodeController.jsx +21 -0
  413. package/src/Tools/Footers/MathInputSelector.jsx +34 -0
  414. package/src/Tools/Footers/VirtualKeyboard.jsx +751 -0
  415. package/src/Tools/cypressTest/CypressTest.jsx +341 -0
  416. package/src/Tools/cypressTest/index.html +102 -0
  417. package/src/Tools/cypressTest/index.jsx +40 -0
  418. package/src/Viewer/ActivityViewer.jsx +1461 -0
  419. package/src/Viewer/PageViewer.jsx +1329 -0
  420. package/src/Viewer/renderers/alert.jsx +17 -0
  421. package/src/Viewer/renderers/angle.jsx +209 -0
  422. package/src/Viewer/renderers/answer.jsx +206 -0
  423. package/src/Viewer/renderers/asList.jsx +25 -0
  424. package/src/Viewer/renderers/blockQuote.jsx +41 -0
  425. package/src/Viewer/renderers/boolean.jsx +17 -0
  426. package/src/Viewer/renderers/booleanInput.css +105 -0
  427. package/src/Viewer/renderers/booleanInput.jsx +636 -0
  428. package/src/Viewer/renderers/button.jsx +369 -0
  429. package/src/Viewer/renderers/c.jsx +17 -0
  430. package/src/Viewer/renderers/callAction.jsx +18 -0
  431. package/src/Viewer/renderers/cell.jsx +59 -0
  432. package/src/Viewer/renderers/chart.jsx +83 -0
  433. package/src/Viewer/renderers/choiceInput.css +223 -0
  434. package/src/Viewer/renderers/choiceInput.jsx +535 -0
  435. package/src/Viewer/renderers/circle.jsx +990 -0
  436. package/src/Viewer/renderers/cobwebPolyline.jsx +442 -0
  437. package/src/Viewer/renderers/codeEditor.jsx +248 -0
  438. package/src/Viewer/renderers/codeViewer.jsx +105 -0
  439. package/src/Viewer/renderers/containerBlock.jsx +41 -0
  440. package/src/Viewer/renderers/containerInline.jsx +17 -0
  441. package/src/Viewer/renderers/contentBrowser.jsx +159 -0
  442. package/src/Viewer/renderers/contentPicker.jsx +160 -0
  443. package/src/Viewer/renderers/curve.jsx +1072 -0
  444. package/src/Viewer/renderers/ellipsis.jsx +17 -0
  445. package/src/Viewer/renderers/em.jsx +17 -0
  446. package/src/Viewer/renderers/embed.jsx +110 -0
  447. package/src/Viewer/renderers/feedback.jsx +74 -0
  448. package/src/Viewer/renderers/figure.jsx +131 -0
  449. package/src/Viewer/renderers/footnote.jsx +52 -0
  450. package/src/Viewer/renderers/graph.jsx +925 -0
  451. package/src/Viewer/renderers/hint.jsx +142 -0
  452. package/src/Viewer/renderers/image.jsx +581 -0
  453. package/src/Viewer/renderers/jsxgraph-distrib/jsxgraphcore.mjs +2 -0
  454. package/src/Viewer/renderers/jsxgraph-distrib/jsxgraphcore.mjs.map +1 -0
  455. package/src/Viewer/renderers/label.jsx +470 -0
  456. package/src/Viewer/renderers/legend.jsx +306 -0
  457. package/src/Viewer/renderers/line.jsx +511 -0
  458. package/src/Viewer/renderers/lineSegment.jsx +754 -0
  459. package/src/Viewer/renderers/list.jsx +111 -0
  460. package/src/Viewer/renderers/lq.jsx +12 -0
  461. package/src/Viewer/renderers/lsq.jsx +12 -0
  462. package/src/Viewer/renderers/math.jsx +582 -0
  463. package/src/Viewer/renderers/mathInput.css +10 -0
  464. package/src/Viewer/renderers/mathInput.jsx +425 -0
  465. package/src/Viewer/renderers/mathInputog.jsx +534 -0
  466. package/src/Viewer/renderers/mathList.jsx +39 -0
  467. package/src/Viewer/renderers/matrixInput.jsx +317 -0
  468. package/src/Viewer/renderers/mdash.jsx +12 -0
  469. package/src/Viewer/renderers/nbsp.jsx +12 -0
  470. package/src/Viewer/renderers/ndash.jsx +12 -0
  471. package/src/Viewer/renderers/number.jsx +454 -0
  472. package/src/Viewer/renderers/numberList.jsx +35 -0
  473. package/src/Viewer/renderers/orbitalDiagram.jsx +247 -0
  474. package/src/Viewer/renderers/orbitalDiagramInput.jsx +450 -0
  475. package/src/Viewer/renderers/p.jsx +38 -0
  476. package/src/Viewer/renderers/paginatorControls.jsx +41 -0
  477. package/src/Viewer/renderers/pegboard.jsx +239 -0
  478. package/src/Viewer/renderers/point.jsx +649 -0
  479. package/src/Viewer/renderers/polygon.jsx +612 -0
  480. package/src/Viewer/renderers/polyline.jsx +608 -0
  481. package/src/Viewer/renderers/pre.jsx +34 -0
  482. package/src/Viewer/renderers/q.jsx +17 -0
  483. package/src/Viewer/renderers/ray.jsx +410 -0
  484. package/src/Viewer/renderers/ref.jsx +149 -0
  485. package/src/Viewer/renderers/regionBetweenCurveXAxis.jsx +182 -0
  486. package/src/Viewer/renderers/renderDoenetML.jsx +56 -0
  487. package/src/Viewer/renderers/row.jsx +31 -0
  488. package/src/Viewer/renderers/rq.jsx +12 -0
  489. package/src/Viewer/renderers/rsq.jsx +12 -0
  490. package/src/Viewer/renderers/section.jsx +427 -0
  491. package/src/Viewer/renderers/sideBySide.jsx +80 -0
  492. package/src/Viewer/renderers/slider.jsx +800 -0
  493. package/src/Viewer/renderers/solution.jsx +134 -0
  494. package/src/Viewer/renderers/spreadsheet.jsx +83 -0
  495. package/src/Viewer/renderers/sq.jsx +17 -0
  496. package/src/Viewer/renderers/styles/global.css +14 -0
  497. package/src/Viewer/renderers/subsetOfRealsInput.jsx +392 -0
  498. package/src/Viewer/renderers/summaryStatistics.jsx +83 -0
  499. package/src/Viewer/renderers/table.jsx +78 -0
  500. package/src/Viewer/renderers/tabular.jsx +58 -0
  501. package/src/Viewer/renderers/tag.jsx +26 -0
  502. package/src/Viewer/renderers/text.jsx +439 -0
  503. package/src/Viewer/renderers/textInput.jsx +774 -0
  504. package/src/Viewer/renderers/textList.jsx +30 -0
  505. package/src/Viewer/renderers/triggerSet.jsx +52 -0
  506. package/src/Viewer/renderers/updateValue.jsx +30 -0
  507. package/src/Viewer/renderers/utils/css.js +13 -0
  508. package/src/Viewer/renderers/utils/graph.js +159 -0
  509. package/src/Viewer/renderers/utils/offGraphIndicators.js +91 -0
  510. package/src/Viewer/renderers/vector.jsx +678 -0
  511. package/src/Viewer/renderers/video.jsx +494 -0
  512. package/src/Viewer/useDoenetRenderer.jsx +128 -0
  513. package/src/main.jsx +16 -0
  514. package/src/media/fonts/files/open-sans-v18-latin-700.woff +0 -0
  515. package/src/media/fonts/files/open-sans-v18-latin-700.woff2 +0 -0
  516. package/src/media/fonts/files/open-sans-v18-latin-700italic.woff +0 -0
  517. package/src/media/fonts/files/open-sans-v18-latin-700italic.woff2 +0 -0
  518. package/src/media/fonts/files/open-sans-v18-latin-italic.woff +0 -0
  519. package/src/media/fonts/files/open-sans-v18-latin-italic.woff2 +0 -0
  520. package/src/media/fonts/files/open-sans-v18-latin-light-italic.woff +0 -0
  521. package/src/media/fonts/files/open-sans-v18-latin-light-italic.woff2 +0 -0
  522. package/src/media/fonts/files/open-sans-v18-latin-light.woff +0 -0
  523. package/src/media/fonts/files/open-sans-v18-latin-light.woff2 +0 -0
  524. package/src/media/fonts/files/open-sans-v18-latin-regular.woff +0 -0
  525. package/src/media/fonts/files/open-sans-v18-latin-regular.woff2 +0 -0
  526. package/src/test/testCode.doenet +26 -0
  527. package/src/test/testViewer.jsx +158 -0
  528. package/src/uiComponents/ActionButton.jsx +157 -0
  529. package/src/uiComponents/ActionButtonGroup.jsx +93 -0
  530. package/src/uiComponents/Button.jsx +160 -0
  531. package/src/uiComponents/ButtonGroup.jsx +56 -0
  532. package/src/uiComponents/ToggleButton.jsx +194 -0
  533. package/src/uiComponents/ToggleButtonGroup.jsx +77 -0
  534. package/src/utils/activityUtils.js +713 -0
  535. package/src/utils/array.js +17 -0
  536. package/src/utils/cid.js +34 -0
  537. package/src/utils/componentInfoObjects.js +89 -0
  538. package/src/utils/deepFunctions.js +165 -0
  539. package/src/utils/enumeration.js +226 -0
  540. package/src/utils/math.js +624 -0
  541. package/src/utils/naming.js +44 -0
  542. package/src/utils/retrieveTextFile.js +156 -0
  543. package/src/utils/returnAllPossibleVariants.js +81 -0
  544. package/src/utils/sequence.js +715 -0
  545. package/src/utils/serialize.js +29 -0
  546. package/src/utils/serializedStateProcessing.js +2587 -0
  547. package/src/utils/subset-of-reals.js +783 -0
  548. package/src/utils/url.js +19 -0
  549. package/vite.config.js +14 -0
@@ -0,0 +1,2884 @@
1
+ import {
2
+ returnRoundingAttributeComponentShadowing,
3
+ returnRoundingStateVariableDefinitions,
4
+ } from "../utils/rounding";
5
+ import Curve from "./Curve";
6
+ import GraphicalComponent from "./abstract/GraphicalComponent";
7
+
8
+ import me from "math-expressions";
9
+
10
+ export default class Circle extends Curve {
11
+ constructor(args) {
12
+ super(args);
13
+
14
+ Object.assign(this.actions, {
15
+ moveCircle: this.moveCircle.bind(this),
16
+ circleClicked: this.circleClicked.bind(this),
17
+ circleFocused: this.circleFocused.bind(this),
18
+ });
19
+ }
20
+ static componentType = "circle";
21
+ static rendererType = "circle";
22
+ static representsClosedPath = true;
23
+
24
+ static createAttributesObject() {
25
+ let attributes = super.createAttributesObject();
26
+
27
+ attributes.through = {
28
+ createComponentOfType: "_pointListComponent",
29
+ };
30
+ attributes.center = {
31
+ createComponentOfType: "point",
32
+ };
33
+ attributes.radius = {
34
+ createComponentOfType: "math",
35
+ };
36
+
37
+ attributes.filled = {
38
+ createComponentOfType: "boolean",
39
+ createStateVariable: "filled",
40
+ defaultValue: false,
41
+ public: true,
42
+ forRenderer: true,
43
+ };
44
+
45
+ attributes.hideOffGraphIndicator = {
46
+ createComponentOfType: "boolean",
47
+ };
48
+
49
+ delete attributes.parMin;
50
+ delete attributes.parMax;
51
+ delete attributes.variable;
52
+
53
+ return attributes;
54
+ }
55
+
56
+ static returnChildGroups() {
57
+ return GraphicalComponent.returnChildGroups();
58
+ }
59
+
60
+ static returnStateVariableDefinitions(args) {
61
+ let stateVariableDefinitions =
62
+ GraphicalComponent.returnStateVariableDefinitions(args);
63
+
64
+ Object.assign(
65
+ stateVariableDefinitions,
66
+ returnRoundingStateVariableDefinitions(),
67
+ );
68
+
69
+ stateVariableDefinitions.styleDescription = {
70
+ public: true,
71
+ shadowingInstructions: {
72
+ createComponentOfType: "text",
73
+ },
74
+ returnDependencies: () => ({
75
+ selectedStyle: {
76
+ dependencyType: "stateVariable",
77
+ variableName: "selectedStyle",
78
+ },
79
+ filled: {
80
+ dependencyType: "stateVariable",
81
+ variableName: "filled",
82
+ },
83
+ document: {
84
+ dependencyType: "ancestor",
85
+ componentType: "document",
86
+ variableNames: ["theme"],
87
+ },
88
+ }),
89
+ definition: function ({ dependencyValues }) {
90
+ let lineColorWord;
91
+ if (dependencyValues.document?.stateValues.theme === "dark") {
92
+ lineColorWord = dependencyValues.selectedStyle.lineColorWordDarkMode;
93
+ } else {
94
+ lineColorWord = dependencyValues.selectedStyle.lineColorWord;
95
+ }
96
+
97
+ let borderDescription = dependencyValues.selectedStyle.lineWidthWord;
98
+ if (dependencyValues.selectedStyle.lineStyleWord) {
99
+ if (borderDescription) {
100
+ borderDescription += " ";
101
+ }
102
+ borderDescription += dependencyValues.selectedStyle.lineStyleWord;
103
+ }
104
+ if (borderDescription) {
105
+ borderDescription += " ";
106
+ }
107
+
108
+ let styleDescription;
109
+ if (!dependencyValues.filled) {
110
+ styleDescription = borderDescription + lineColorWord;
111
+ } else {
112
+ let fillColorWord;
113
+ if (dependencyValues.document?.stateValues.theme === "dark") {
114
+ fillColorWord =
115
+ dependencyValues.selectedStyle.fillColorWordDarkMode;
116
+ } else {
117
+ fillColorWord = dependencyValues.selectedStyle.fillColorWord;
118
+ }
119
+
120
+ if (fillColorWord === lineColorWord) {
121
+ styleDescription = "filled " + fillColorWord;
122
+ if (borderDescription) {
123
+ styleDescription += " with " + borderDescription + "border";
124
+ }
125
+ } else {
126
+ styleDescription =
127
+ "filled " +
128
+ fillColorWord +
129
+ " with " +
130
+ borderDescription +
131
+ lineColorWord +
132
+ " border";
133
+ }
134
+ }
135
+
136
+ return { setValue: { styleDescription } };
137
+ },
138
+ };
139
+
140
+ stateVariableDefinitions.styleDescriptionWithNoun = {
141
+ public: true,
142
+ shadowingInstructions: {
143
+ createComponentOfType: "text",
144
+ },
145
+ returnDependencies: () => ({
146
+ selectedStyle: {
147
+ dependencyType: "stateVariable",
148
+ variableName: "selectedStyle",
149
+ },
150
+ filled: {
151
+ dependencyType: "stateVariable",
152
+ variableName: "filled",
153
+ },
154
+ document: {
155
+ dependencyType: "ancestor",
156
+ componentType: "document",
157
+ variableNames: ["theme"],
158
+ },
159
+ }),
160
+ definition: function ({ dependencyValues }) {
161
+ let lineColorWord;
162
+ if (dependencyValues.document?.stateValues.theme === "dark") {
163
+ lineColorWord = dependencyValues.selectedStyle.lineColorWordDarkMode;
164
+ } else {
165
+ lineColorWord = dependencyValues.selectedStyle.lineColorWord;
166
+ }
167
+
168
+ let borderDescription = dependencyValues.selectedStyle.lineWidthWord;
169
+ if (dependencyValues.selectedStyle.lineStyleWord) {
170
+ if (borderDescription) {
171
+ borderDescription += " ";
172
+ }
173
+ borderDescription += dependencyValues.selectedStyle.lineStyleWord;
174
+ }
175
+ if (borderDescription) {
176
+ borderDescription += " ";
177
+ }
178
+
179
+ let styleDescriptionWithNoun;
180
+ if (!dependencyValues.filled) {
181
+ styleDescriptionWithNoun =
182
+ borderDescription + lineColorWord + " circle";
183
+ } else {
184
+ let fillColorWord;
185
+ if (dependencyValues.document?.stateValues.theme === "dark") {
186
+ fillColorWord =
187
+ dependencyValues.selectedStyle.fillColorWordDarkMode;
188
+ } else {
189
+ fillColorWord = dependencyValues.selectedStyle.fillColorWord;
190
+ }
191
+
192
+ if (fillColorWord === lineColorWord) {
193
+ styleDescriptionWithNoun = "filled " + fillColorWord + " circle";
194
+ if (borderDescription) {
195
+ styleDescriptionWithNoun +=
196
+ " with a " + borderDescription + "border";
197
+ }
198
+ } else {
199
+ styleDescriptionWithNoun =
200
+ "filled " +
201
+ fillColorWord +
202
+ " circle with a " +
203
+ borderDescription +
204
+ lineColorWord +
205
+ " border";
206
+ }
207
+ }
208
+
209
+ return { setValue: { styleDescriptionWithNoun } };
210
+ },
211
+ };
212
+
213
+ stateVariableDefinitions.borderStyleDescription = {
214
+ public: true,
215
+ shadowingInstructions: {
216
+ createComponentOfType: "text",
217
+ },
218
+ returnDependencies: () => ({
219
+ selectedStyle: {
220
+ dependencyType: "stateVariable",
221
+ variableName: "selectedStyle",
222
+ },
223
+ document: {
224
+ dependencyType: "ancestor",
225
+ componentType: "document",
226
+ variableNames: ["theme"],
227
+ },
228
+ }),
229
+ definition: function ({ dependencyValues }) {
230
+ let lineColorWord;
231
+ if (dependencyValues.document?.stateValues.theme === "dark") {
232
+ lineColorWord = dependencyValues.selectedStyle.lineColorWordDarkMode;
233
+ } else {
234
+ lineColorWord = dependencyValues.selectedStyle.lineColorWord;
235
+ }
236
+
237
+ let borderStyleDescription =
238
+ dependencyValues.selectedStyle.lineWidthWord;
239
+ if (dependencyValues.selectedStyle.lineStyleWord) {
240
+ if (borderStyleDescription) {
241
+ borderStyleDescription += " ";
242
+ }
243
+ borderStyleDescription +=
244
+ dependencyValues.selectedStyle.lineStyleWord;
245
+ }
246
+
247
+ if (borderStyleDescription) {
248
+ borderStyleDescription += " ";
249
+ }
250
+
251
+ borderStyleDescription += lineColorWord;
252
+
253
+ return { setValue: { borderStyleDescription } };
254
+ },
255
+ };
256
+
257
+ stateVariableDefinitions.fillStyleDescription = {
258
+ public: true,
259
+ shadowingInstructions: {
260
+ createComponentOfType: "text",
261
+ },
262
+ returnDependencies: () => ({
263
+ selectedStyle: {
264
+ dependencyType: "stateVariable",
265
+ variableName: "selectedStyle",
266
+ },
267
+ filled: {
268
+ dependencyType: "stateVariable",
269
+ variableName: "filled",
270
+ },
271
+ document: {
272
+ dependencyType: "ancestor",
273
+ componentType: "document",
274
+ variableNames: ["theme"],
275
+ },
276
+ }),
277
+ definition: function ({ dependencyValues }) {
278
+ let fillColorWord;
279
+ if (dependencyValues.document?.stateValues.theme === "dark") {
280
+ fillColorWord = dependencyValues.selectedStyle.fillColorWordDarkMode;
281
+ } else {
282
+ fillColorWord = dependencyValues.selectedStyle.fillColorWord;
283
+ }
284
+
285
+ let fillStyleDescription;
286
+ if (!dependencyValues.filled) {
287
+ fillStyleDescription = "unfilled";
288
+ } else {
289
+ fillStyleDescription = fillColorWord;
290
+ }
291
+
292
+ return { setValue: { fillStyleDescription } };
293
+ },
294
+ };
295
+
296
+ stateVariableDefinitions.hideOffGraphIndicator = {
297
+ public: true,
298
+ forRenderer: true,
299
+ shadowingInstructions: {
300
+ createComponentOfType: "boolean",
301
+ },
302
+ returnDependencies: () => ({
303
+ hideOffGraphIndicatorAttr: {
304
+ dependencyType: "attributeComponent",
305
+ attributeName: "hideOffGraphIndicator",
306
+ variableNames: ["value"],
307
+ },
308
+ graphAncestor: {
309
+ dependencyType: "ancestor",
310
+ componentType: "graph",
311
+ variableNames: ["hideOffGraphIndicators"],
312
+ },
313
+ }),
314
+ definition({ dependencyValues }) {
315
+ if (dependencyValues.hideOffGraphIndicatorAttr) {
316
+ return {
317
+ setValue: {
318
+ hideOffGraphIndicator:
319
+ dependencyValues.hideOffGraphIndicatorAttr.stateValues.value,
320
+ },
321
+ };
322
+ } else if (dependencyValues.graphAncestor) {
323
+ return {
324
+ setValue: {
325
+ hideOffGraphIndicator:
326
+ dependencyValues.graphAncestor.stateValues
327
+ .hideOffGraphIndicators,
328
+ },
329
+ };
330
+ } else {
331
+ return {
332
+ setValue: { hideOffGraphIndicator: false },
333
+ };
334
+ }
335
+ },
336
+ };
337
+
338
+ stateVariableDefinitions.curveType = {
339
+ forRenderer: true,
340
+ returnDependencies: () => ({}),
341
+ definition: () => ({ setValue: { curveType: "circle" } }),
342
+ };
343
+
344
+ stateVariableDefinitions.parMax = {
345
+ public: true,
346
+ shadowingInstructions: {
347
+ createComponentOfType: "number",
348
+ },
349
+ forRenderer: true,
350
+ returnDependencies: () => ({}),
351
+ definition: () => ({ setValue: { parMax: NaN } }),
352
+ };
353
+
354
+ stateVariableDefinitions.parMin = {
355
+ public: true,
356
+ shadowingInstructions: {
357
+ createComponentOfType: "number",
358
+ },
359
+ forRenderer: true,
360
+ returnDependencies: () => ({}),
361
+ definition: () => ({ setValue: { parMin: NaN } }),
362
+ };
363
+
364
+ stateVariableDefinitions.fs = {
365
+ forRenderer: true,
366
+ isArray: true,
367
+ entryPrefixes: ["f"],
368
+ returnArraySizeDependencies: () => ({}),
369
+ returnArraySize: () => [0],
370
+ returnArrayDependenciesByKey: () => ({}),
371
+ arrayDefinitionByKey: () => ({ setValue: { fs: {} } }),
372
+ };
373
+
374
+ stateVariableDefinitions.numThroughPoints = {
375
+ returnDependencies: () => ({
376
+ throughAttr: {
377
+ dependencyType: "attributeComponent",
378
+ attributeName: "through",
379
+ variableNames: ["numPoints"],
380
+ },
381
+ }),
382
+ definition: function ({ dependencyValues }) {
383
+ if (dependencyValues.throughAttr !== null) {
384
+ return {
385
+ setValue: {
386
+ numThroughPoints:
387
+ dependencyValues.throughAttr.stateValues.numPoints,
388
+ },
389
+ };
390
+ } else {
391
+ return { setValue: { numThroughPoints: 0 } };
392
+ }
393
+ },
394
+ };
395
+
396
+ stateVariableDefinitions.throughPoints = {
397
+ public: true,
398
+ isLocation: true,
399
+ shadowingInstructions: {
400
+ createComponentOfType: "math",
401
+ addAttributeComponentsShadowingStateVariables:
402
+ returnRoundingAttributeComponentShadowing(),
403
+ returnWrappingComponents(prefix) {
404
+ if (prefix === "throughPointX") {
405
+ return [];
406
+ } else {
407
+ // point or entire array
408
+ // wrap inner dimension by both <point> and <xs>
409
+ // don't wrap outer dimension (for entire array)
410
+ return [
411
+ ["point", { componentType: "mathList", isAttribute: "xs" }],
412
+ ];
413
+ }
414
+ },
415
+ },
416
+ isArray: true,
417
+ numDimensions: 2,
418
+ entryPrefixes: ["throughPointX", "throughPoint"],
419
+ getArrayKeysFromVarName({ arrayEntryPrefix, varEnding, arraySize }) {
420
+ if (arrayEntryPrefix === "throughPointX") {
421
+ // throughPointX1_2 is the 2nd component of the first point
422
+ let indices = varEnding.split("_").map((x) => Number(x) - 1);
423
+ if (
424
+ indices.length === 2 &&
425
+ indices.every((x, i) => Number.isInteger(x) && x >= 0)
426
+ ) {
427
+ if (arraySize) {
428
+ if (indices.every((x, i) => x < arraySize[i])) {
429
+ return [String(indices)];
430
+ } else {
431
+ return [];
432
+ }
433
+ } else {
434
+ // If not given the array size,
435
+ // then return the array keys assuming the array is large enough.
436
+ // Must do this as it is used to determine potential array entries.
437
+ return [String(indices)];
438
+ }
439
+ } else {
440
+ return [];
441
+ }
442
+ } else {
443
+ // throughPoint3 is all components of the third vertex
444
+
445
+ let pointInd = Number(varEnding) - 1;
446
+ if (!(Number.isInteger(pointInd) && pointInd >= 0)) {
447
+ return [];
448
+ }
449
+
450
+ if (!arraySize) {
451
+ // If don't have array size, we just need to determine if it is a potential entry.
452
+ // Return the first entry assuming array is large enough
453
+ return [pointInd + ",0"];
454
+ }
455
+ if (pointInd < arraySize[0]) {
456
+ // array of "pointInd,i", where i=0, ..., arraySize[1]-1
457
+ return Array.from(
458
+ Array(arraySize[1]),
459
+ (_, i) => pointInd + "," + i,
460
+ );
461
+ } else {
462
+ return [];
463
+ }
464
+ }
465
+ },
466
+ arrayVarNameFromPropIndex(propIndex, varName) {
467
+ if (varName === "throughPoints") {
468
+ if (propIndex.length === 1) {
469
+ return "throughPoint" + propIndex[0];
470
+ } else {
471
+ // if propIndex has additional entries, ignore them
472
+ return `throughPointX${propIndex[0]}_${propIndex[1]}`;
473
+ }
474
+ }
475
+ if (varName.slice(0, 12) === "throughPoint") {
476
+ // could be throughPoint or throughPointX
477
+ let throughPointNum = Number(varName.slice(12));
478
+ if (Number.isInteger(throughPointNum) && throughPointNum > 0) {
479
+ // if propIndex has additional entries, ignore them
480
+ return `throughPointX${throughPointNum}_${propIndex[0]}`;
481
+ }
482
+ }
483
+ return null;
484
+ },
485
+ returnArraySizeDependencies: () => ({
486
+ numThroughPoints: {
487
+ dependencyType: "stateVariable",
488
+ variableName: "numThroughPoints",
489
+ },
490
+ }),
491
+ returnArraySize({ dependencyValues }) {
492
+ return [dependencyValues.numThroughPoints, 2];
493
+ },
494
+ returnArrayDependenciesByKey({ arrayKeys }) {
495
+ let dependenciesByKey = {};
496
+ for (let arrayKey of arrayKeys) {
497
+ let [pointInd, dim] = arrayKey.split(",");
498
+ let varEnding = Number(pointInd) + 1 + "_" + (Number(dim) + 1);
499
+
500
+ dependenciesByKey[arrayKey] = {
501
+ throughAttr: {
502
+ dependencyType: "attributeComponent",
503
+ attributeName: "through",
504
+ variableNames: ["pointX" + varEnding],
505
+ },
506
+ };
507
+ }
508
+ return { dependenciesByKey };
509
+ },
510
+ arrayDefinitionByKey({ dependencyValuesByKey, arrayKeys }) {
511
+ // console.log('definition of circle throughPoints');
512
+ // console.log(dependencyValuesByKey)
513
+ // console.log(arrayKeys);
514
+
515
+ let throughPoints = {};
516
+
517
+ for (let arrayKey of arrayKeys) {
518
+ let [pointInd, dim] = arrayKey.split(",");
519
+ let varEnding = Number(pointInd) + 1 + "_" + (Number(dim) + 1);
520
+
521
+ let throughAttr = dependencyValuesByKey[arrayKey].throughAttr;
522
+ if (
523
+ throughAttr !== null &&
524
+ throughAttr.stateValues["pointX" + varEnding]
525
+ ) {
526
+ throughPoints[arrayKey] =
527
+ throughAttr.stateValues["pointX" + varEnding];
528
+ } else {
529
+ throughPoints[arrayKey] = me.fromAst("\uff3f");
530
+ }
531
+ }
532
+
533
+ return { setValue: { throughPoints } };
534
+ },
535
+
536
+ async inverseArrayDefinitionByKey({
537
+ desiredStateVariableValues,
538
+ dependencyValuesByKey,
539
+ dependencyNamesByKey,
540
+ initialChange,
541
+ stateValues,
542
+ }) {
543
+ // console.log(`inverseDefinition of throughPoints of circle`);
544
+ // console.log(desiredStateVariableValues)
545
+ // console.log(JSON.parse(JSON.stringify(stateValues)))
546
+ // console.log(dependencyValuesByKey);
547
+
548
+ // if not draggable, then disallow initial change
549
+ if (initialChange && !(await stateValues.draggable)) {
550
+ return { success: false };
551
+ }
552
+
553
+ let instructions = [];
554
+ for (let arrayKey in desiredStateVariableValues.throughPoints) {
555
+ let [pointInd, dim] = arrayKey.split(",");
556
+ let varEnding = Number(pointInd) + 1 + "_" + (Number(dim) + 1);
557
+
558
+ if (
559
+ dependencyValuesByKey[arrayKey].throughAttr !== null &&
560
+ dependencyValuesByKey[arrayKey].throughAttr.stateValues[
561
+ "pointX" + varEnding
562
+ ]
563
+ ) {
564
+ instructions.push({
565
+ setDependency: dependencyNamesByKey[arrayKey].throughAttr,
566
+ desiredValue: desiredStateVariableValues.throughPoints[arrayKey],
567
+ variableIndex: 0,
568
+ });
569
+ } else {
570
+ return { success: false };
571
+ }
572
+ }
573
+
574
+ return {
575
+ success: true,
576
+ instructions,
577
+ };
578
+ },
579
+ };
580
+
581
+ stateVariableDefinitions.havePrescribedCenter = {
582
+ returnDependencies: () => ({
583
+ centerAttr: {
584
+ dependencyType: "attributeComponent",
585
+ attributeName: "center",
586
+ },
587
+ }),
588
+ definition: ({ dependencyValues }) => ({
589
+ setValue: {
590
+ havePrescribedCenter: dependencyValues.centerAttr !== null,
591
+ },
592
+ checkForActualChange: { havePrescribedCenter: true },
593
+ }),
594
+ };
595
+
596
+ stateVariableDefinitions.prescribedCenter = {
597
+ isArray: true,
598
+ isLocation: true,
599
+ entryPrefixes: ["prescribedCenterX"],
600
+ returnArraySizeDependencies: () => ({
601
+ havePrescribedCenter: {
602
+ dependencyType: "stateVariable",
603
+ variableName: "havePrescribedCenter",
604
+ },
605
+ }),
606
+ returnArraySize({ dependencyValues }) {
607
+ return [dependencyValues.havePrescribedCenter ? 2 : 0];
608
+ },
609
+ returnArrayDependenciesByKey({ arrayKeys }) {
610
+ let dependenciesByKey = {};
611
+
612
+ for (let arrayKey of arrayKeys) {
613
+ let varEnding = Number(arrayKey) + 1;
614
+ dependenciesByKey[arrayKey] = {
615
+ centerAttr: {
616
+ dependencyType: "attributeComponent",
617
+ attributeName: "center",
618
+ variableNames: ["x" + varEnding],
619
+ },
620
+ };
621
+ }
622
+
623
+ return { dependenciesByKey };
624
+ },
625
+ arrayDefinitionByKey: function ({
626
+ globalDependencyValues,
627
+ dependencyValuesByKey,
628
+ arrayKeys,
629
+ }) {
630
+ let prescribedCenter = {};
631
+
632
+ for (let arrayKey of arrayKeys) {
633
+ let varEnding = Number(arrayKey) + 1;
634
+
635
+ if (dependencyValuesByKey[arrayKey].centerAttr !== null) {
636
+ prescribedCenter[arrayKey] =
637
+ dependencyValuesByKey[arrayKey].centerAttr.stateValues[
638
+ "x" + varEnding
639
+ ];
640
+ if (!prescribedCenter[arrayKey]) {
641
+ prescribedCenter[arrayKey] = me.fromAst("\uff3f");
642
+ }
643
+ }
644
+ }
645
+
646
+ return { setValue: { prescribedCenter } };
647
+ },
648
+ inverseArrayDefinitionByKey({
649
+ desiredStateVariableValues,
650
+ globalDependencyValues,
651
+ dependencyValuesByKey,
652
+ dependencyNamesByKey,
653
+ arraySize,
654
+ }) {
655
+ // console.log(`inverse definition of prescribed center of circle`)
656
+ // console.log(desiredStateVariableValues)
657
+ // console.log(globalDependencyValues)
658
+ // console.log(dependencyValuesByKey)
659
+
660
+ let instructions = [];
661
+
662
+ // process instructions in reverse order
663
+ // so that the x-coordinates is processed last and takes precedence
664
+ for (let arrayKey of Object.keys(
665
+ desiredStateVariableValues.prescribedCenter,
666
+ ).reverse()) {
667
+ if (dependencyValuesByKey[arrayKey].centerAttr !== null) {
668
+ instructions.push({
669
+ setDependency: dependencyNamesByKey[arrayKey].centerAttr,
670
+ desiredValue:
671
+ desiredStateVariableValues.prescribedCenter[arrayKey],
672
+ variableIndex: 0,
673
+ });
674
+ }
675
+ }
676
+
677
+ return {
678
+ success: true,
679
+ instructions,
680
+ };
681
+ },
682
+ };
683
+
684
+ stateVariableDefinitions.prescribedRadius = {
685
+ defaultValue: null,
686
+ isLocation: true,
687
+ returnDependencies: () => ({
688
+ radiusAttr: {
689
+ dependencyType: "attributeComponent",
690
+ attributeName: "radius",
691
+ variableNames: ["value"],
692
+ },
693
+ }),
694
+ definition: function ({ dependencyValues }) {
695
+ if (dependencyValues.radiusAttr !== null) {
696
+ return {
697
+ setValue: {
698
+ prescribedRadius: dependencyValues.radiusAttr.stateValues.value,
699
+ },
700
+ };
701
+ } else {
702
+ return {
703
+ setValue: {
704
+ prescribedRadius: null,
705
+ },
706
+ };
707
+ }
708
+ },
709
+ inverseDefinition: function ({
710
+ desiredStateVariableValues,
711
+ dependencyValues,
712
+ }) {
713
+ if (dependencyValues.radiusAttr !== null) {
714
+ return {
715
+ success: true,
716
+ instructions: [
717
+ {
718
+ setDependency: "radiusAttr",
719
+ desiredValue: desiredStateVariableValues.prescribedRadius,
720
+ childIndex: 0,
721
+ variableIndex: 0,
722
+ },
723
+ ],
724
+ };
725
+ } else {
726
+ return { success: false };
727
+ }
728
+ },
729
+ };
730
+
731
+ stateVariableDefinitions.havePrescribedRadius = {
732
+ returnDependencies: () => ({
733
+ radiusAttr: {
734
+ dependencyType: "attributeComponent",
735
+ attributeName: "radius",
736
+ },
737
+ }),
738
+ definition: ({ dependencyValues }) => ({
739
+ setValue: {
740
+ havePrescribedRadius: dependencyValues.radiusAttr !== null,
741
+ },
742
+ checkForActualChange: { havePrescribedRadius: true },
743
+ }),
744
+ };
745
+
746
+ stateVariableDefinitions.numericalPrescribedRadius = {
747
+ returnDependencies: () => ({
748
+ prescribedRadius: {
749
+ dependencyType: "stateVariable",
750
+ variableName: "prescribedRadius",
751
+ },
752
+ }),
753
+ additionalStateVariablesDefined: ["haveNonNumericalPrescribedRadius"],
754
+ definition: function ({ dependencyValues }) {
755
+ let haveNonNumericalPrescribedRadius = false;
756
+ let numericalPrescribedRadius;
757
+
758
+ if (dependencyValues.prescribedRadius === null) {
759
+ numericalPrescribedRadius = null;
760
+ } else {
761
+ numericalPrescribedRadius =
762
+ dependencyValues.prescribedRadius.evaluate_to_constant();
763
+ if (!Number.isFinite(numericalPrescribedRadius)) {
764
+ numericalPrescribedRadius = NaN;
765
+ haveNonNumericalPrescribedRadius = true;
766
+ }
767
+ }
768
+
769
+ return {
770
+ setValue: {
771
+ haveNonNumericalPrescribedRadius,
772
+ numericalPrescribedRadius,
773
+ },
774
+ checkForActualChange: {
775
+ haveNonNumericalEntriesNumericalRadius: true,
776
+ },
777
+ };
778
+ },
779
+ inverseDefinition: async function ({
780
+ desiredStateVariableValues,
781
+ dependencyValues,
782
+ stateValues,
783
+ }) {
784
+ // console.log('inverse definition of numerical prescribed radius of circle')
785
+ // console.log(desiredStateVariableValues)
786
+ // console.log(dependencyValues)
787
+
788
+ if (await stateValues.haveNonNumericalPrescribedRadius) {
789
+ return { success: false };
790
+ }
791
+
792
+ if (
793
+ desiredStateVariableValues.numericalPrescribedRadius !== undefined
794
+ ) {
795
+ if (dependencyValues.prescribedRadius === null) {
796
+ return { success: false };
797
+ }
798
+
799
+ return {
800
+ success: true,
801
+ instructions: [
802
+ {
803
+ setDependency: "prescribedRadius",
804
+ desiredValue: me.fromAst(
805
+ desiredStateVariableValues.numericalPrescribedRadius,
806
+ ),
807
+ },
808
+ ],
809
+ };
810
+ }
811
+
812
+ return { success: false };
813
+ },
814
+ };
815
+
816
+ stateVariableDefinitions.numericalPrescribedCenter = {
817
+ isArray: true,
818
+ entryPrefixes: ["numericalPrescribedCenterX"],
819
+ returnArraySizeDependencies: () => ({
820
+ havePrescribedCenter: {
821
+ dependencyType: "stateVariable",
822
+ variableName: "havePrescribedCenter",
823
+ },
824
+ }),
825
+ returnArraySize({ dependencyValues }) {
826
+ return [dependencyValues.havePrescribedCenter ? 2 : 0];
827
+ },
828
+ returnArrayDependenciesByKey({ arrayKeys }) {
829
+ let dependenciesByKey = {};
830
+
831
+ for (let arrayKey of arrayKeys) {
832
+ let varEnding = Number(arrayKey) + 1;
833
+ dependenciesByKey[arrayKey] = {
834
+ prescribedCenterX: {
835
+ dependencyType: "stateVariable",
836
+ variableName: "prescribedCenterX" + varEnding,
837
+ },
838
+ };
839
+ }
840
+
841
+ return { dependenciesByKey };
842
+ },
843
+ arrayDefinitionByKey: function ({ dependencyValuesByKey, arrayKeys }) {
844
+ let numericalPrescribedCenter = {};
845
+ for (let arrayKey of arrayKeys) {
846
+ let prescribedX = dependencyValuesByKey[arrayKey].prescribedCenterX;
847
+ if (prescribedX) {
848
+ numericalPrescribedCenter[arrayKey] =
849
+ prescribedX.evaluate_to_constant();
850
+ } else {
851
+ numericalPrescribedCenter[arrayKey] = NaN;
852
+ }
853
+ }
854
+
855
+ return { setValue: { numericalPrescribedCenter } };
856
+ },
857
+ inverseArrayDefinitionByKey({
858
+ desiredStateVariableValues,
859
+ dependencyNamesByKey,
860
+ }) {
861
+ // console.log('inverse definition of numerical prescribed center of circle')
862
+ // console.log(desiredStateVariableValues)
863
+ // console.log(dependencyValuesByKey)
864
+
865
+ let instructions = [];
866
+
867
+ for (let arrayKey in desiredStateVariableValues.numericalPrescribedCenter) {
868
+ instructions.push({
869
+ setDependency: dependencyNamesByKey[arrayKey].prescribedCenterX,
870
+ desiredValue: me.fromAst(
871
+ desiredStateVariableValues.numericalPrescribedCenter[arrayKey],
872
+ ),
873
+ });
874
+ }
875
+
876
+ return {
877
+ success: true,
878
+ instructions,
879
+ };
880
+ },
881
+ };
882
+
883
+ stateVariableDefinitions.haveNonNumericalPrescribedCenter = {
884
+ returnDependencies: () => ({
885
+ numericalPrescribedCenter: {
886
+ dependencyType: "stateVariable",
887
+ variableName: "numericalPrescribedCenter",
888
+ },
889
+ }),
890
+ definition({ dependencyValues }) {
891
+ let haveNonNumericalPrescribedCenter =
892
+ dependencyValues.numericalPrescribedCenter.some(
893
+ (x) => !Number.isFinite(x),
894
+ );
895
+
896
+ return {
897
+ setValue: {
898
+ haveNonNumericalPrescribedCenter,
899
+ },
900
+ checkForActualChange: { haveNonNumericalPrescribedCenter: true },
901
+ };
902
+ },
903
+ };
904
+
905
+ stateVariableDefinitions.numericalThroughPoints = {
906
+ returnDependencies: () => ({
907
+ throughPoints: {
908
+ dependencyType: "stateVariable",
909
+ variableName: "throughPoints",
910
+ },
911
+ }),
912
+ additionalStateVariablesDefined: ["haveNonNumericalThroughPoints"],
913
+ definition: function ({ dependencyValues }) {
914
+ let haveNonNumericalThroughPoints = false;
915
+ let numericalThroughPoints = [];
916
+
917
+ for (let point of dependencyValues.throughPoints) {
918
+ let numericalPoint = [];
919
+ for (let dim = 0; dim < 2; dim++) {
920
+ let numericalValue;
921
+ try {
922
+ numericalValue = point[dim].evaluate_to_constant();
923
+ } catch (e) {
924
+ console.warn("Invalid point of circle");
925
+ haveNonNumericalThroughPoints = true;
926
+ numericalPoint = [];
927
+ break;
928
+ }
929
+ if (Number.isFinite(numericalValue)) {
930
+ numericalPoint.push(numericalValue);
931
+ } else {
932
+ haveNonNumericalThroughPoints = true;
933
+ numericalPoint = [];
934
+ break;
935
+ }
936
+ }
937
+ if (numericalPoint.length > 0) {
938
+ numericalThroughPoints.push(numericalPoint);
939
+ } else {
940
+ numericalThroughPoints = [];
941
+ break;
942
+ }
943
+ }
944
+
945
+ return {
946
+ setValue: {
947
+ haveNonNumericalThroughPoints,
948
+ numericalThroughPoints,
949
+ },
950
+ checkForActualChange: { haveNonNumericalThroughPoints: true },
951
+ };
952
+ },
953
+ inverseDefinition: async function ({
954
+ desiredStateVariableValues,
955
+ dependencyValues,
956
+ stateValues,
957
+ }) {
958
+ // console.log('inverse definition of numerical throughpoints of circle')
959
+ // console.log(desiredStateVariableValues)
960
+ // console.log(dependencyValues)
961
+
962
+ if (await stateValues.haveNonNumericalThroughPoints) {
963
+ return { success: false };
964
+ }
965
+
966
+ if (desiredStateVariableValues.numericalThroughPoints !== undefined) {
967
+ let desiredThroughPoints = {};
968
+ for (let [
969
+ ind,
970
+ pt,
971
+ ] of desiredStateVariableValues.numericalThroughPoints.entries()) {
972
+ desiredThroughPoints[ind + ",0"] = me.fromAst(pt[0]);
973
+ desiredThroughPoints[ind + ",1"] = me.fromAst(pt[1]);
974
+ }
975
+ return {
976
+ success: true,
977
+ instructions: [
978
+ {
979
+ setDependency: "throughPoints",
980
+ desiredValue: desiredThroughPoints,
981
+ },
982
+ ],
983
+ };
984
+ }
985
+
986
+ return { success: false };
987
+ },
988
+ };
989
+
990
+ stateVariableDefinitions.haveNumericalEntries = {
991
+ returnDependencies: () => ({
992
+ haveNonNumericalPrescribedCenter: {
993
+ dependencyType: "stateVariable",
994
+ variableName: "haveNonNumericalPrescribedCenter",
995
+ },
996
+ haveNonNumericalPrescribedRadius: {
997
+ dependencyType: "stateVariable",
998
+ variableName: "haveNonNumericalPrescribedRadius",
999
+ },
1000
+ haveNonNumericalThroughPoints: {
1001
+ dependencyType: "stateVariable",
1002
+ variableName: "haveNonNumericalThroughPoints",
1003
+ },
1004
+ }),
1005
+ definition: ({ dependencyValues }) => ({
1006
+ setValue: {
1007
+ haveNumericalEntries: !(
1008
+ dependencyValues.haveNonNumericalPrescribedCenter ||
1009
+ dependencyValues.haveNonNumericalPrescribedRadius ||
1010
+ dependencyValues.haveNonNumericalThroughPoints
1011
+ ),
1012
+ },
1013
+ }),
1014
+ };
1015
+
1016
+ // When circle is based on two or three points
1017
+ // radius and center are more efficently calculated simultaneously.
1018
+ // This state variable doesn't check that radius and center
1019
+ // are not prescribed, but it is used in just those cases
1020
+ stateVariableDefinitions.numericalRadiusCalculatedWithCenter = {
1021
+ additionalStateVariablesDefined: ["numericalCenterCalculatedWithRadius"],
1022
+ returnDependencies: () => ({
1023
+ numThroughPoints: {
1024
+ dependencyType: "stateVariable",
1025
+ variableName: "numThroughPoints",
1026
+ },
1027
+ numericalThroughPoints: {
1028
+ dependencyType: "stateVariable",
1029
+ variableName: "numericalThroughPoints",
1030
+ },
1031
+ haveNumericalEntries: {
1032
+ dependencyType: "stateVariable",
1033
+ variableName: "haveNonNumericalThroughPoints",
1034
+ },
1035
+ }),
1036
+ definition: function ({ dependencyValues }) {
1037
+ if (dependencyValues.haveNonNumericalThroughPoints) {
1038
+ let message =
1039
+ "Haven't implemented circle through " +
1040
+ dependencyValues.numThroughPoints +
1041
+ " points";
1042
+ message += " in case where don't have numerical values.";
1043
+ console.warn(message);
1044
+ return {
1045
+ setValue: {
1046
+ numericalRadiusCalculatedWithCenter: null,
1047
+ numericalCenterCalculatedWithRadius: null,
1048
+ },
1049
+ };
1050
+ }
1051
+
1052
+ if (dependencyValues.numThroughPoints === 2) {
1053
+ let { numericalCenter, numericalRadius } =
1054
+ circleFromTwoNumericalPoints({
1055
+ point1: dependencyValues.numericalThroughPoints[0],
1056
+ point2: dependencyValues.numericalThroughPoints[1],
1057
+ });
1058
+
1059
+ return {
1060
+ setValue: {
1061
+ numericalCenterCalculatedWithRadius: numericalCenter,
1062
+ numericalRadiusCalculatedWithCenter: numericalRadius,
1063
+ },
1064
+ };
1065
+ } else if (dependencyValues.numThroughPoints === 3) {
1066
+ let x1 = dependencyValues.numericalThroughPoints[0][0];
1067
+ let x2 = dependencyValues.numericalThroughPoints[1][0];
1068
+ let x3 = dependencyValues.numericalThroughPoints[2][0];
1069
+ let y1 = dependencyValues.numericalThroughPoints[0][1];
1070
+ let y2 = dependencyValues.numericalThroughPoints[1][1];
1071
+ let y3 = dependencyValues.numericalThroughPoints[2][1];
1072
+
1073
+ let numericalCenter, numericalRadius;
1074
+
1075
+ if (x1 === x2 && y1 === y2) {
1076
+ if (x1 === x3 && y1 === y3) {
1077
+ // if all points are identical, it's a circle with radius zero
1078
+ numericalCenter = [x1, y1];
1079
+ numericalRadius = 0;
1080
+ } else {
1081
+ // if point 1 and 2 are identical, treat it as circle through two points
1082
+ let result = circleFromTwoNumericalPoints({
1083
+ point1: [x1, y1],
1084
+ point2: [x3, y3],
1085
+ });
1086
+ numericalCenter = result.numericalCenter;
1087
+ numericalRadius = result.numericalRadius;
1088
+ }
1089
+ } else if ((x1 === x3 && y1 === y3) || (x2 === x3 && y2 === y3)) {
1090
+ // if point 1 and 3 are identical,
1091
+ // or if point 2 and 3 are identical,
1092
+ // treat it as circle through two points
1093
+ let result = circleFromTwoNumericalPoints({
1094
+ point1: [x1, y1],
1095
+ point2: [x2, y2],
1096
+ });
1097
+ numericalCenter = result.numericalCenter;
1098
+ numericalRadius = result.numericalRadius;
1099
+ } else {
1100
+ // all distinct points
1101
+
1102
+ let mag1 = x1 * x1 + y1 * y1;
1103
+ let mag2 = x2 * x2 + y2 * y2;
1104
+ let mag3 = x3 * x3 + y3 * y3;
1105
+
1106
+ let A = x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2;
1107
+ let B = mag1 * (y3 - y2) + mag2 * (y1 - y3) + mag3 * (y2 - y1);
1108
+ let C = mag1 * (x2 - x3) + mag2 * (x3 - x1) + mag3 * (x1 - x2);
1109
+ let D =
1110
+ mag1 * (x3 * y2 - x2 * y3) +
1111
+ mag2 * (x1 * y3 - x3 * y1) +
1112
+ mag3 * (x2 * y1 - x1 * y2);
1113
+
1114
+ if (A !== 0) {
1115
+ numericalCenter = [-B / (2 * A), -C / (2 * A)];
1116
+ numericalRadius = Math.sqrt(
1117
+ (B * B + C * C - 4 * A * D) / (4 * A * A),
1118
+ );
1119
+ } else {
1120
+ // collinear non-identical points, can't make a circle
1121
+ numericalCenter = [NaN, NaN];
1122
+ numericalRadius = NaN;
1123
+ }
1124
+ }
1125
+
1126
+ return {
1127
+ setValue: {
1128
+ numericalCenterCalculatedWithRadius: numericalCenter,
1129
+ numericalRadiusCalculatedWithCenter: numericalRadius,
1130
+ },
1131
+ };
1132
+ } else if (dependencyValues.numThroughPoints > 3) {
1133
+ console.warn("Can't calculate circle through more than 3 points");
1134
+ return {
1135
+ setValue: {
1136
+ numericalRadiusCalculatedWithCenter: null,
1137
+ numericalCenterCalculatedWithRadius: null,
1138
+ },
1139
+ };
1140
+ } else {
1141
+ // these variables aren't used with fewer than 2 points
1142
+ return {
1143
+ setValue: {
1144
+ numericalRadiusCalculatedWithCenter: null,
1145
+ numericalCenterCalculatedWithRadius: null,
1146
+ },
1147
+ };
1148
+ }
1149
+ },
1150
+ };
1151
+
1152
+ stateVariableDefinitions.essentialRadius = {
1153
+ defaultValue: 1,
1154
+ hasEssential: true,
1155
+ isLocation: true,
1156
+ returnDependencies: () => ({}),
1157
+ definition: () => ({
1158
+ useEssentialOrDefaultValue: {
1159
+ essentialRadius: true,
1160
+ },
1161
+ }),
1162
+ inverseDefinition: function ({ desiredStateVariableValues }) {
1163
+ return {
1164
+ success: true,
1165
+ instructions: [
1166
+ {
1167
+ setEssentialValue: "essentialRadius",
1168
+ value: desiredStateVariableValues.essentialRadius,
1169
+ },
1170
+ ],
1171
+ };
1172
+ },
1173
+ };
1174
+
1175
+ stateVariableDefinitions.numericalRadius = {
1176
+ forRenderer: true,
1177
+ stateVariablesDeterminingDependencies: [
1178
+ "numThroughPoints",
1179
+ "havePrescribedCenter",
1180
+ "havePrescribedRadius",
1181
+ ],
1182
+ returnDependencies: function ({ stateValues }) {
1183
+ let dependencies = {
1184
+ haveNonNumericalPrescribedRadius: {
1185
+ dependencyType: "stateVariable",
1186
+ variableName: "haveNonNumericalPrescribedRadius",
1187
+ },
1188
+ numThroughPoints: {
1189
+ dependencyType: "stateVariable",
1190
+ variableName: "numThroughPoints",
1191
+ },
1192
+ essentialRadius: {
1193
+ dependencyType: "stateVariable",
1194
+ variableName: "essentialRadius",
1195
+ },
1196
+ };
1197
+
1198
+ if (stateValues.havePrescribedRadius) {
1199
+ dependencies.numericalPrescribedRadius = {
1200
+ dependencyType: "stateVariable",
1201
+ variableName: "numericalPrescribedRadius",
1202
+ };
1203
+ if (
1204
+ stateValues.havePrescribedCenter &&
1205
+ stateValues.numThroughPoints > 0
1206
+ ) {
1207
+ dependencies.haveCenterRadiusPoints = {
1208
+ dependencyType: "value",
1209
+ value: true,
1210
+ };
1211
+ }
1212
+ } else {
1213
+ if (stateValues.havePrescribedCenter) {
1214
+ if (stateValues.numThroughPoints === 1) {
1215
+ dependencies.haveNonNumericalPrescribedCenter = {
1216
+ dependencyType: "stateVariable",
1217
+ variableName: "haveNonNumericalPrescribedCenter",
1218
+ };
1219
+ dependencies.numericalPrescribedCenter = {
1220
+ dependencyType: "stateVariable",
1221
+ variableName: "numericalPrescribedCenter",
1222
+ };
1223
+ dependencies.haveNonNumericalThroughPoints = {
1224
+ dependencyType: "stateVariable",
1225
+ variableName: "haveNonNumericalThroughPoints",
1226
+ };
1227
+ dependencies.numericalThroughPoints = {
1228
+ dependencyType: "stateVariable",
1229
+ variableName: "numericalThroughPoints",
1230
+ };
1231
+ }
1232
+ } else if (stateValues.numThroughPoints > 1) {
1233
+ dependencies.numericalRadiusCalculatedWithCenter = {
1234
+ dependencyType: "stateVariable",
1235
+ variableName: "numericalRadiusCalculatedWithCenter",
1236
+ };
1237
+ // need numericalThroughPoints for inverse definition
1238
+ // as skip numericalRadiusCalculatedWithCenter
1239
+ dependencies.numericalThroughPoints = {
1240
+ dependencyType: "stateVariable",
1241
+ variableName: "numericalThroughPoints",
1242
+ };
1243
+ }
1244
+ }
1245
+
1246
+ return dependencies;
1247
+ },
1248
+ definition: function ({ dependencyValues }) {
1249
+ // console.log(`definition of numericalRadius of circle`);
1250
+ // console.log(dependencyValues);
1251
+
1252
+ if (
1253
+ dependencyValues.haveNonNumericalPrescribedRadius ||
1254
+ dependencyValues.haveNonNumericalPrescribedCenter ||
1255
+ dependencyValues.haveNonNumericalThroughPoints
1256
+ ) {
1257
+ return {
1258
+ setValue: {
1259
+ numericalRadius: NaN,
1260
+ },
1261
+ };
1262
+ }
1263
+
1264
+ if (dependencyValues.numericalPrescribedRadius !== undefined) {
1265
+ if (dependencyValues.haveCenterRadiusPoints) {
1266
+ console.warn(
1267
+ "Can't calculate circle with specified radius and center and through points",
1268
+ );
1269
+ return { setValue: { numericalRadius: NaN } };
1270
+ }
1271
+
1272
+ return {
1273
+ setValue: {
1274
+ numericalRadius: Math.max(
1275
+ 0,
1276
+ dependencyValues.numericalPrescribedRadius,
1277
+ ),
1278
+ },
1279
+ };
1280
+ }
1281
+
1282
+ if (dependencyValues.numericalPrescribedCenter !== undefined) {
1283
+ if (dependencyValues.numThroughPoints === 0) {
1284
+ let r = dependencyValues.essentialRadius;
1285
+ if (r instanceof me.class) {
1286
+ r = r.evaluate_to_constant();
1287
+ }
1288
+ return {
1289
+ setValue: { numericalRadius: r },
1290
+ };
1291
+ } else if (dependencyValues.numThroughPoints === 1) {
1292
+ // center and one point specified.
1293
+ // Radius is distance from center to point.
1294
+ let pt = dependencyValues.numericalThroughPoints[0];
1295
+ if (pt === undefined) {
1296
+ // if dependencies haven't been updated,
1297
+ // it is possible to temporarility have fewer numericalThroughPoints
1298
+ // than numThroughPoints
1299
+ return { setValue: { numericalRadius: NaN } };
1300
+ }
1301
+ let numericalRadius = Math.sqrt(
1302
+ Math.pow(
1303
+ pt[0] - dependencyValues.numericalPrescribedCenter[0],
1304
+ 2,
1305
+ ) +
1306
+ Math.pow(
1307
+ pt[1] - dependencyValues.numericalPrescribedCenter[1],
1308
+ 2,
1309
+ ),
1310
+ );
1311
+ return { setValue: { numericalRadius } };
1312
+ } else {
1313
+ console.warn(
1314
+ "Can't calculate circle with specified center through more than 1 point",
1315
+ );
1316
+ return { setValue: { numericalRadius: NaN } };
1317
+ }
1318
+ }
1319
+
1320
+ // don't have prescribed center
1321
+ if (dependencyValues.numThroughPoints < 2) {
1322
+ let r = dependencyValues.essentialRadius;
1323
+ if (r instanceof me.class) {
1324
+ r = r.evaluate_to_constant();
1325
+ }
1326
+ return {
1327
+ setValue: { numericalRadius: r },
1328
+ };
1329
+ } else {
1330
+ // having two or three through points
1331
+ // with no prescribed radius or center
1332
+ // is case where calculated radius and center together.
1333
+ return {
1334
+ setValue: {
1335
+ numericalRadius:
1336
+ dependencyValues.numericalRadiusCalculatedWithCenter,
1337
+ },
1338
+ };
1339
+ }
1340
+ },
1341
+ inverseDefinition: async function ({
1342
+ desiredStateVariableValues,
1343
+ dependencyValues,
1344
+ stateValues,
1345
+ }) {
1346
+ // console.log('inverse definition of numericalRadius of circle')
1347
+ // console.log(desiredStateVariableValues)
1348
+ // console.log(dependencyValues)
1349
+
1350
+ if (dependencyValues.numericalPrescribedRadius !== undefined) {
1351
+ return {
1352
+ success: true,
1353
+ instructions: [
1354
+ {
1355
+ setDependency: "numericalPrescribedRadius",
1356
+ desiredValue: Math.max(
1357
+ 0,
1358
+ desiredStateVariableValues.numericalRadius,
1359
+ ),
1360
+ },
1361
+ ],
1362
+ };
1363
+ }
1364
+
1365
+ if (dependencyValues.numericalPrescribedCenter !== undefined) {
1366
+ if (dependencyValues.numThroughPoints === 0) {
1367
+ return {
1368
+ success: true,
1369
+ instructions: [
1370
+ {
1371
+ setDependency: "essentialRadius",
1372
+ desiredValue: Math.max(
1373
+ 0,
1374
+ desiredStateVariableValues.numericalRadius,
1375
+ ),
1376
+ },
1377
+ ],
1378
+ };
1379
+ } else if (dependencyValues.numThroughPoints === 1) {
1380
+ let numericalRadius = Math.max(
1381
+ 0,
1382
+ desiredStateVariableValues.numericalRadius,
1383
+ );
1384
+
1385
+ let theta = (await stateValues.throughAngles)[0];
1386
+ if (!Number.isFinite(theta)) {
1387
+ return { success: false };
1388
+ }
1389
+ let numericalCenter = await stateValues.numericalCenter;
1390
+ let pt = [
1391
+ numericalCenter[0] + numericalRadius * Math.cos(theta),
1392
+ numericalCenter[1] + numericalRadius * Math.sin(theta),
1393
+ ];
1394
+
1395
+ return {
1396
+ success: true,
1397
+ instructions: [
1398
+ {
1399
+ setDependency: "numericalThroughPoints",
1400
+ desiredValue: [pt],
1401
+ },
1402
+ ],
1403
+ };
1404
+ } else {
1405
+ return { success: false };
1406
+ }
1407
+ }
1408
+
1409
+ // don't have prescribed center
1410
+ if (dependencyValues.numThroughPoints < 2) {
1411
+ return {
1412
+ success: true,
1413
+ instructions: [
1414
+ {
1415
+ setDependency: "essentialRadius",
1416
+ desiredValue: Math.max(
1417
+ 0,
1418
+ desiredStateVariableValues.numericalRadius,
1419
+ ),
1420
+ },
1421
+ ],
1422
+ };
1423
+ } else {
1424
+ let newThroughPoints = [];
1425
+
1426
+ let numericalRadius = Math.max(
1427
+ 0,
1428
+ desiredStateVariableValues.numericalRadius,
1429
+ );
1430
+
1431
+ for (let i = 0; i < dependencyValues.numThroughPoints; i++) {
1432
+ let theta = (await stateValues.throughAngles)[i];
1433
+ if (!Number.isFinite(theta)) {
1434
+ return { success: false };
1435
+ }
1436
+ let numericalCenter = await stateValues.numericalCenter;
1437
+ let pt = [
1438
+ numericalCenter[0] + numericalRadius * Math.cos(theta),
1439
+ numericalCenter[1] + numericalRadius * Math.sin(theta),
1440
+ ];
1441
+
1442
+ newThroughPoints.push(pt);
1443
+ }
1444
+
1445
+ return {
1446
+ success: true,
1447
+ instructions: [
1448
+ {
1449
+ setDependency: "numericalThroughPoints",
1450
+ desiredValue: newThroughPoints,
1451
+ },
1452
+ ],
1453
+ };
1454
+ }
1455
+ },
1456
+ };
1457
+
1458
+ stateVariableDefinitions.essentialCenter = {
1459
+ isLocation: true,
1460
+ isArray: true,
1461
+ entryPrefixes: ["essentialCenterX"],
1462
+ hasEssential: true,
1463
+ defaultValueByArrayKey: () => 0,
1464
+ returnArraySizeDependencies: () => ({}),
1465
+ returnArraySize() {
1466
+ return [2];
1467
+ },
1468
+ returnArrayDependenciesByKey: () => ({}),
1469
+ arrayDefinitionByKey: function ({ arrayKeys }) {
1470
+ let essentialCenter = {};
1471
+ for (let arrayKey of arrayKeys) {
1472
+ essentialCenter[arrayKey] = true;
1473
+ }
1474
+ return {
1475
+ useEssentialOrDefaultValue: { essentialCenter },
1476
+ };
1477
+ },
1478
+ async inverseArrayDefinitionByKey({
1479
+ desiredStateVariableValues,
1480
+ stateValues,
1481
+ workspace,
1482
+ }) {
1483
+ let instructions = [];
1484
+ for (let arrayKey in desiredStateVariableValues.essentialCenter) {
1485
+ instructions.push({
1486
+ setEssentialValue: "essentialCenter",
1487
+ value: {
1488
+ [arrayKey]: desiredStateVariableValues.essentialCenter[arrayKey],
1489
+ },
1490
+ });
1491
+ }
1492
+ return {
1493
+ success: true,
1494
+ instructions,
1495
+ };
1496
+ },
1497
+ };
1498
+
1499
+ stateVariableDefinitions.numericalCenter = {
1500
+ forRenderer: true,
1501
+ isArray: true,
1502
+ entryPrefixes: ["numericalCenterX"],
1503
+ stateVariablesDeterminingDependencies: [
1504
+ "numThroughPoints",
1505
+ "havePrescribedCenter",
1506
+ "havePrescribedRadius",
1507
+ ],
1508
+ returnArraySizeDependencies: () => ({}),
1509
+ returnArraySize() {
1510
+ return [2];
1511
+ },
1512
+ returnArrayDependenciesByKey({ arrayKeys, stateValues }) {
1513
+ let globalDependencies = {};
1514
+
1515
+ let dependenciesByKey = {};
1516
+
1517
+ if (stateValues.havePrescribedCenter) {
1518
+ for (let arrayKey of arrayKeys) {
1519
+ let varEnding = Number(arrayKey) + 1;
1520
+ dependenciesByKey[arrayKey] = {
1521
+ numericalPrescribedCenterX: {
1522
+ dependencyType: "stateVariable",
1523
+ variableName: "numericalPrescribedCenterX" + varEnding,
1524
+ },
1525
+ };
1526
+ }
1527
+ if (
1528
+ stateValues.havePrescribedRadius &&
1529
+ stateValues.numThroughPoints > 0
1530
+ ) {
1531
+ globalDependencies.haveCenterRadiusPoints = {
1532
+ dependencyType: "value",
1533
+ value: true,
1534
+ };
1535
+ }
1536
+ } else {
1537
+ for (let arrayKey of arrayKeys) {
1538
+ let varEnding = Number(arrayKey) + 1;
1539
+ dependenciesByKey[arrayKey] = {
1540
+ essentialCenterX: {
1541
+ dependencyType: "stateVariable",
1542
+ variableName: "essentialCenterX" + varEnding,
1543
+ },
1544
+ };
1545
+ }
1546
+ globalDependencies.numThroughPoints = {
1547
+ dependencyType: "stateVariable",
1548
+ variableName: "numThroughPoints",
1549
+ };
1550
+ globalDependencies.numericalThroughPoints = {
1551
+ dependencyType: "stateVariable",
1552
+ variableName: "numericalThroughPoints",
1553
+ };
1554
+ globalDependencies.haveNonNumericalThroughPoints = {
1555
+ dependencyType: "stateVariable",
1556
+ variableName: "haveNonNumericalThroughPoints",
1557
+ };
1558
+
1559
+ if (stateValues.havePrescribedRadius) {
1560
+ // still used numericalRadius, rather than numericalPrescribedRadius
1561
+ // as numericalRadius becomes zero if have negative numericalPrescribedRadius
1562
+ globalDependencies.numericalRadius = {
1563
+ dependencyType: "stateVariable",
1564
+ variableName: "numericalRadius",
1565
+ };
1566
+ } else if (stateValues.numThroughPoints == 1) {
1567
+ // if didn't have prescribed radius but just one point
1568
+ // we treat the radius calculated above as prescribed
1569
+ globalDependencies.numericalRadius = {
1570
+ dependencyType: "stateVariable",
1571
+ variableName: "numericalRadius",
1572
+ };
1573
+ } else if (stateValues.numThroughPoints > 1) {
1574
+ globalDependencies.numericalCenterCalculatedWithRadius = {
1575
+ dependencyType: "stateVariable",
1576
+ variableName: "numericalCenterCalculatedWithRadius",
1577
+ };
1578
+ }
1579
+ }
1580
+
1581
+ return { globalDependencies, dependenciesByKey };
1582
+ },
1583
+ arrayDefinitionByKey: function ({
1584
+ globalDependencyValues,
1585
+ dependencyValuesByKey,
1586
+ arrayKeys,
1587
+ }) {
1588
+ // console.log(`definition of numericalCenter of circle`);
1589
+ // console.log(globalDependencyValues);
1590
+ // console.log(dependencyValuesByKey);
1591
+ // console.log(arrayKeys)
1592
+
1593
+ let numericalCenter = {};
1594
+
1595
+ for (let arrayKey of arrayKeys) {
1596
+ if (
1597
+ dependencyValuesByKey[arrayKey].numericalPrescribedCenterX !==
1598
+ undefined
1599
+ ) {
1600
+ if (globalDependencyValues.haveCenterRadiusPoints) {
1601
+ console.warn(
1602
+ "Can't calculate circle with specified radius and center and through points",
1603
+ );
1604
+ return { setValue: { numericalCenter: [NaN, NaN] } };
1605
+ }
1606
+ numericalCenter[arrayKey] =
1607
+ dependencyValuesByKey[arrayKey].numericalPrescribedCenterX;
1608
+ }
1609
+ }
1610
+ if (Object.keys(numericalCenter).length > 0) {
1611
+ return { setValue: { numericalCenter } };
1612
+ }
1613
+
1614
+ if (
1615
+ globalDependencyValues.haveNonNumericalPrescribedRadius ||
1616
+ globalDependencyValues.haveNonNumericalThroughPoints
1617
+ ) {
1618
+ return {
1619
+ setValue: {
1620
+ numericalCenter: [NaN, NaN],
1621
+ },
1622
+ };
1623
+ }
1624
+
1625
+ if (globalDependencyValues.numericalRadius !== undefined) {
1626
+ // have a radius defined and no center
1627
+ if (globalDependencyValues.numThroughPoints === 0) {
1628
+ // only radius specified. use essential center
1629
+
1630
+ for (let arrayKey of arrayKeys) {
1631
+ let value = dependencyValuesByKey[arrayKey].essentialCenterX;
1632
+
1633
+ if (value instanceof me.class) {
1634
+ value = value.evaluate_to_constant();
1635
+ }
1636
+ numericalCenter[arrayKey] = value;
1637
+ }
1638
+
1639
+ return { setValue: { numericalCenter } };
1640
+ } else if (globalDependencyValues.numThroughPoints === 1) {
1641
+ // radius and one through point
1642
+ // create a circle with top being the point
1643
+
1644
+ if (globalDependencyValues.numericalThroughPoints.length < 1) {
1645
+ // if dependencies haven't been updated,
1646
+ // it is possible to temporarility have fewer numericalThroughPoints
1647
+ // than numThroughPoints
1648
+ return { setValue: { numericalCenter: [NaN, NaN] } };
1649
+ }
1650
+
1651
+ let numericalCenter = [
1652
+ globalDependencyValues.numericalThroughPoints[0][0],
1653
+ globalDependencyValues.numericalThroughPoints[0][1] -
1654
+ globalDependencyValues.numericalRadius,
1655
+ ];
1656
+
1657
+ return { setValue: { numericalCenter } };
1658
+ } else if (globalDependencyValues.numThroughPoints === 2) {
1659
+ if (globalDependencyValues.numericalThroughPoints.length < 2) {
1660
+ // if dependencies haven't been updated,
1661
+ // it is possible to temporarility have fewer numericalThroughPoints
1662
+ // than numThroughPoints
1663
+ return { setValue: { numericalCenter: [NaN, NaN] } };
1664
+ }
1665
+
1666
+ // find circle through two points with given radius
1667
+ let r = globalDependencyValues.numericalRadius;
1668
+
1669
+ let x1 = globalDependencyValues.numericalThroughPoints[0][0];
1670
+ let x2 = globalDependencyValues.numericalThroughPoints[1][0];
1671
+ let y1 = globalDependencyValues.numericalThroughPoints[0][1];
1672
+ let y2 = globalDependencyValues.numericalThroughPoints[1][1];
1673
+
1674
+ let dist2 = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
1675
+ let r2 = r * r;
1676
+
1677
+ if (r < 0 || 4 * r2 < dist2) {
1678
+ console.warn(
1679
+ "Can't find circle through given radius and two points",
1680
+ );
1681
+ return { setValue: { numericalCenter: [NaN, NaN] } };
1682
+ }
1683
+
1684
+ if (dist2 === 0) {
1685
+ // points are equal to each other, treat as through single point
1686
+ return { setValue: { numericalCenter: [x1, y1 - r] } };
1687
+ }
1688
+
1689
+ let centerx =
1690
+ (0.5 *
1691
+ (dist2 * (x1 + x2) +
1692
+ (y1 - y2) * Math.sqrt((4 * r2 - dist2) * dist2))) /
1693
+ dist2;
1694
+
1695
+ let centery =
1696
+ (0.5 *
1697
+ (dist2 * (y1 + y2) +
1698
+ (x2 - x1) * Math.sqrt((4 * r2 - dist2) * dist2))) /
1699
+ dist2;
1700
+
1701
+ return { setValue: { numericalCenter: [centerx, centery] } };
1702
+ } else {
1703
+ console.warn(
1704
+ "Can't create circle through more than two points with given radius",
1705
+ );
1706
+ return { setValue: { numericalCenter: [NaN, NaN] } };
1707
+ }
1708
+ }
1709
+
1710
+ // don't have prescribed radius
1711
+ if (globalDependencyValues.numThroughPoints === 0) {
1712
+ for (let arrayKey of arrayKeys) {
1713
+ let value = dependencyValuesByKey[arrayKey].essentialCenterX;
1714
+
1715
+ if (value instanceof me.class) {
1716
+ value = value.evaluate_to_constant();
1717
+ }
1718
+ numericalCenter[arrayKey] = value;
1719
+ }
1720
+
1721
+ return { setValue: { numericalCenter } };
1722
+ } else {
1723
+ // Must have at least two points, as case with one through point
1724
+ // used calculated radius
1725
+
1726
+ // having two or three through points
1727
+ // with no prescribed radius or center
1728
+ // is case where calculated radius and center together.
1729
+
1730
+ if (
1731
+ globalDependencyValues.numericalCenterCalculatedWithRadius ===
1732
+ undefined
1733
+ ) {
1734
+ // if numThroughPoints changed, but dependencies haven't been recalculated yet
1735
+ // could get to here where don't have numericalCenterCalculatedWithRadius
1736
+ return {
1737
+ setValue: {
1738
+ numericalCenter: [NaN, NaN],
1739
+ },
1740
+ };
1741
+ } else {
1742
+ return {
1743
+ setValue: {
1744
+ numericalCenter:
1745
+ globalDependencyValues.numericalCenterCalculatedWithRadius,
1746
+ },
1747
+ };
1748
+ }
1749
+ }
1750
+ },
1751
+ async inverseArrayDefinitionByKey({
1752
+ desiredStateVariableValues,
1753
+ globalDependencyValues,
1754
+ dependencyValuesByKey,
1755
+ dependencyNamesByKey,
1756
+ stateValues,
1757
+ workspace,
1758
+ }) {
1759
+ // console.log('inverse definition of numericalCenter of circle')
1760
+ // console.log(desiredStateVariableValues)
1761
+ // console.log(globalDependencyValues)
1762
+ // console.log(dependencyValuesByKey)
1763
+
1764
+ let instructions = [];
1765
+
1766
+ for (let arrayKey in desiredStateVariableValues.numericalCenter) {
1767
+ if (
1768
+ dependencyValuesByKey[arrayKey].numericalPrescribedCenterX !==
1769
+ undefined
1770
+ ) {
1771
+ instructions.push({
1772
+ setDependency:
1773
+ dependencyNamesByKey[arrayKey].numericalPrescribedCenterX,
1774
+ desiredValue:
1775
+ desiredStateVariableValues.numericalCenter[arrayKey],
1776
+ });
1777
+ }
1778
+ }
1779
+
1780
+ if (instructions.length > 0) {
1781
+ return {
1782
+ success: true,
1783
+ instructions,
1784
+ };
1785
+ }
1786
+
1787
+ if (globalDependencyValues.numThroughPoints === 0) {
1788
+ // just change value of essentialCenter
1789
+ for (let arrayKey in desiredStateVariableValues.numericalCenter) {
1790
+ instructions.push({
1791
+ setDependency: dependencyNamesByKey[arrayKey].essentialCenterX,
1792
+ desiredValue:
1793
+ desiredStateVariableValues.numericalCenter[arrayKey],
1794
+ });
1795
+ }
1796
+ return {
1797
+ success: true,
1798
+ instructions,
1799
+ };
1800
+ } else {
1801
+ // in case just one key specified, merge with previous values
1802
+ if (!workspace.desiredCenter) {
1803
+ workspace.desiredCenter = {};
1804
+ }
1805
+ for (let key = 0; key < 2; key++) {
1806
+ if (desiredStateVariableValues.numericalCenter[key] !== undefined) {
1807
+ workspace.desiredCenter[key] =
1808
+ desiredStateVariableValues.numericalCenter[key];
1809
+ } else if (workspace.desiredCenter[key] === undefined) {
1810
+ workspace.desiredCenter[key] = stateValues.numericalCenter[key];
1811
+ }
1812
+ }
1813
+
1814
+ let newThroughPoints = [];
1815
+
1816
+ let throughAngles = await stateValues.throughAngles;
1817
+ let numericalRadius = await stateValues.numericalRadius;
1818
+
1819
+ for (let i = 0; i < globalDependencyValues.numThroughPoints; i++) {
1820
+ let theta = throughAngles[i];
1821
+ if (!Number.isFinite(theta)) {
1822
+ return { success: false };
1823
+ }
1824
+ let pt = [
1825
+ workspace.desiredCenter[0] + numericalRadius * Math.cos(theta),
1826
+ workspace.desiredCenter[1] + numericalRadius * Math.sin(theta),
1827
+ ];
1828
+
1829
+ newThroughPoints.push(pt);
1830
+ }
1831
+
1832
+ return {
1833
+ success: true,
1834
+ instructions: [
1835
+ {
1836
+ setDependency: "numericalThroughPoints",
1837
+ desiredValue: newThroughPoints,
1838
+ },
1839
+ ],
1840
+ };
1841
+ }
1842
+ },
1843
+ };
1844
+
1845
+ stateVariableDefinitions.throughAngles = {
1846
+ // send to renderer just so it can try to preserve original angles
1847
+ // when dragging circle based on points with constraints
1848
+ forRenderer: true,
1849
+ isLocation: true,
1850
+ defaultValue: [],
1851
+ hasEssential: true,
1852
+ doNotShadowEssential: true,
1853
+ returnDependencies: () => ({
1854
+ haveNumericalEntries: {
1855
+ dependencyType: "stateVariable",
1856
+ variableName: "haveNumericalEntries",
1857
+ },
1858
+ numThroughPoints: {
1859
+ dependencyType: "stateVariable",
1860
+ variableName: "numThroughPoints",
1861
+ },
1862
+ numericalThroughPoints: {
1863
+ dependencyType: "stateVariable",
1864
+ variableName: "numericalThroughPoints",
1865
+ },
1866
+ numericalRadius: {
1867
+ dependencyType: "stateVariable",
1868
+ variableName: "numericalRadius",
1869
+ },
1870
+ numericalCenter: {
1871
+ dependencyType: "stateVariable",
1872
+ variableName: "numericalCenter",
1873
+ },
1874
+ }),
1875
+ definition: function ({ dependencyValues }) {
1876
+ if (
1877
+ !(
1878
+ dependencyValues.haveNumericalEntries &&
1879
+ dependencyValues.numThroughPoints > 0 &&
1880
+ dependencyValues.numericalRadius > 0 &&
1881
+ dependencyValues.numericalCenter.every((x) => Number.isFinite(x))
1882
+ )
1883
+ ) {
1884
+ return {
1885
+ useEssentialOrDefaultValue: {
1886
+ throughAngles: true,
1887
+ },
1888
+ };
1889
+ }
1890
+
1891
+ // if have through points, numeric entries and positive radius
1892
+ // calculate angles
1893
+ let throughAngles = [];
1894
+ for (let pt of dependencyValues.numericalThroughPoints) {
1895
+ throughAngles.push(
1896
+ Math.atan2(
1897
+ pt[1] - dependencyValues.numericalCenter[1],
1898
+ pt[0] - dependencyValues.numericalCenter[0],
1899
+ ),
1900
+ );
1901
+ }
1902
+
1903
+ // make throughAngles essential so that can save their values
1904
+ // even if values become invalid (such as radius becoming zero)
1905
+ return {
1906
+ setValue: { throughAngles },
1907
+ setEssentialValue: { throughAngles },
1908
+ };
1909
+ },
1910
+ };
1911
+
1912
+ stateVariableDefinitions.radius = {
1913
+ public: true,
1914
+ isLocation: true,
1915
+ shadowingInstructions: {
1916
+ createComponentOfType: "math",
1917
+ addAttributeComponentsShadowingStateVariables:
1918
+ returnRoundingAttributeComponentShadowing(),
1919
+ },
1920
+ stateVariablesDeterminingDependencies: [
1921
+ "numThroughPoints",
1922
+ "havePrescribedCenter",
1923
+ "havePrescribedRadius",
1924
+ ],
1925
+ returnDependencies: function ({ stateValues }) {
1926
+ let dependencies = {
1927
+ numericalRadius: {
1928
+ dependencyType: "stateVariable",
1929
+ variableName: "numericalRadius",
1930
+ },
1931
+ numThroughPoints: {
1932
+ dependencyType: "stateVariable",
1933
+ variableName: "numThroughPoints",
1934
+ },
1935
+ essentialRadius: {
1936
+ dependencyType: "stateVariable",
1937
+ variableName: "essentialRadius",
1938
+ },
1939
+ };
1940
+
1941
+ if (stateValues.havePrescribedRadius) {
1942
+ dependencies.prescribedRadius = {
1943
+ dependencyType: "stateVariable",
1944
+ variableName: "prescribedRadius",
1945
+ };
1946
+ if (
1947
+ stateValues.havePrescribedCenter &&
1948
+ stateValues.numThroughPoints > 0
1949
+ ) {
1950
+ dependencies.haveCenterRadiusPoints = {
1951
+ dependencyType: "value",
1952
+ value: true,
1953
+ };
1954
+ }
1955
+ } else {
1956
+ if (
1957
+ stateValues.havePrescribedCenter &&
1958
+ stateValues.numThroughPoints === 1
1959
+ ) {
1960
+ dependencies.prescribedCenter = {
1961
+ dependencyType: "stateVariable",
1962
+ variableName: "prescribedCenter",
1963
+ };
1964
+ dependencies.throughPoints = {
1965
+ dependencyType: "stateVariable",
1966
+ variableName: "throughPoints",
1967
+ };
1968
+ }
1969
+ }
1970
+ return dependencies;
1971
+ },
1972
+ definition: function ({ dependencyValues }) {
1973
+ // console.log(`definition of radius of circle`);
1974
+ // console.log(dependencyValues);
1975
+
1976
+ if (Number.isFinite(dependencyValues.numericalRadius)) {
1977
+ return {
1978
+ setValue: {
1979
+ radius: me.fromAst(dependencyValues.numericalRadius),
1980
+ },
1981
+ };
1982
+ }
1983
+
1984
+ if (dependencyValues.prescribedRadius !== undefined) {
1985
+ if (dependencyValues.haveCenterRadiusPoints) {
1986
+ console.warn(
1987
+ "Can't calculate circle with specified radius and center and through points",
1988
+ );
1989
+ return { setValue: { radius: me.fromAst("\uff3f") } };
1990
+ }
1991
+ return {
1992
+ setValue: {
1993
+ radius: dependencyValues.prescribedRadius,
1994
+ },
1995
+ };
1996
+ }
1997
+
1998
+ if (dependencyValues.prescribedCenter !== undefined) {
1999
+ if (dependencyValues.numThroughPoints === 0) {
2000
+ let r = dependencyValues.essentialRadius;
2001
+ if (!(r instanceof me.class)) {
2002
+ if (Number.isFinite(r)) {
2003
+ r = me.fromAst(r);
2004
+ } else {
2005
+ r = me.fromAst("\uff3f");
2006
+ }
2007
+ }
2008
+ return {
2009
+ setValue: { radius: r },
2010
+ };
2011
+ } else if (dependencyValues.numThroughPoints === 1) {
2012
+ // center and one point specified.
2013
+ // Radius is distance from center to point.
2014
+
2015
+ try {
2016
+ let pt = dependencyValues.throughPoints[0];
2017
+ let ptx = pt[0];
2018
+ let pty = pt[1];
2019
+ let ctx = dependencyValues.prescribedCenter[0];
2020
+ let cty = dependencyValues.prescribedCenter[1];
2021
+
2022
+ let radius = ptx
2023
+ .subtract(ctx)
2024
+ .pow(2)
2025
+ .add(pty.subtract(cty).pow(2))
2026
+ .pow(0.5)
2027
+ .simplify();
2028
+
2029
+ return { setValue: { radius } };
2030
+ } catch (e) {
2031
+ console.warn("Invalid center or through points of circle");
2032
+ return { setValue: { radius: me.fromAst("\uff3f") } };
2033
+ }
2034
+ } else {
2035
+ console.warn(
2036
+ "Can't calculate circle with specified center through more than 1 point",
2037
+ );
2038
+ return { setValue: { radius: me.fromAst("\uff3f") } };
2039
+ }
2040
+ }
2041
+
2042
+ // don't have prescribed center
2043
+ if (dependencyValues.numThroughPoints < 2) {
2044
+ let r = dependencyValues.essentialRadius;
2045
+ if (!(r instanceof me.class)) {
2046
+ if (Number.isFinite(r)) {
2047
+ r = me.fromAst(r);
2048
+ } else {
2049
+ r = me.fromAst("\uff3f");
2050
+ }
2051
+ }
2052
+ return {
2053
+ setValue: { radius: r },
2054
+ };
2055
+ } else {
2056
+ // having two or three through points
2057
+ // with no prescribed radius or center
2058
+
2059
+ console.warn(
2060
+ `Have not implemented circle through ${dependencyValues.numThroughPoints} points when non-numerical values`,
2061
+ );
2062
+ return { setValue: { radius: me.fromAst("\uff3f") } };
2063
+ }
2064
+ },
2065
+ inverseDefinition: function ({
2066
+ desiredStateVariableValues,
2067
+ dependencyValues,
2068
+ }) {
2069
+ // console.log('inverse definition of radius of circle')
2070
+ // console.log(desiredStateVariableValues)
2071
+ // console.log(dependencyValues)
2072
+
2073
+ let numericalRadius =
2074
+ desiredStateVariableValues.radius.evaluate_to_constant();
2075
+
2076
+ if (
2077
+ Number.isFinite(numericalRadius) &&
2078
+ Number.isFinite(dependencyValues.numericalRadius)
2079
+ ) {
2080
+ return {
2081
+ success: true,
2082
+ instructions: [
2083
+ {
2084
+ setDependency: "numericalRadius",
2085
+ desiredValue: numericalRadius,
2086
+ },
2087
+ ],
2088
+ };
2089
+ }
2090
+
2091
+ if (dependencyValues.prescribedRadius !== undefined) {
2092
+ return {
2093
+ success: true,
2094
+ instructions: [
2095
+ {
2096
+ setDependency: "prescribedRadius",
2097
+ desiredValue: desiredStateVariableValues.radius,
2098
+ },
2099
+ ],
2100
+ };
2101
+ }
2102
+
2103
+ if (dependencyValues.numThroughPoints === 0) {
2104
+ // just change essential value of radius
2105
+ // (and numericalRadius if we have a numerical radius)
2106
+ let instructions = [
2107
+ {
2108
+ setDependency: "essentialRadius",
2109
+ desiredValue: desiredStateVariableValues.radius,
2110
+ },
2111
+ ];
2112
+
2113
+ return {
2114
+ success: true,
2115
+ instructions,
2116
+ };
2117
+ } else {
2118
+ console.warn(
2119
+ "Can't change radius of circle with non-numerical values through points",
2120
+ );
2121
+ return { success: false };
2122
+ }
2123
+ },
2124
+ };
2125
+
2126
+ stateVariableDefinitions.diameter = {
2127
+ public: true,
2128
+ isLocation: true,
2129
+ shadowingInstructions: {
2130
+ createComponentOfType: "math",
2131
+ addAttributeComponentsShadowingStateVariables:
2132
+ returnRoundingAttributeComponentShadowing(),
2133
+ },
2134
+ returnDependencies: () => ({
2135
+ radius: {
2136
+ dependencyType: "stateVariable",
2137
+ variableName: "radius",
2138
+ },
2139
+ }),
2140
+ definition({ dependencyValues }) {
2141
+ return {
2142
+ setValue: {
2143
+ diameter: dependencyValues.radius.multiply(2).simplify(),
2144
+ },
2145
+ };
2146
+ },
2147
+ inverseDefinition: function ({
2148
+ desiredStateVariableValues,
2149
+ dependencyValues,
2150
+ }) {
2151
+ return {
2152
+ success: true,
2153
+ instructions: [
2154
+ {
2155
+ setDependency: "radius",
2156
+ desiredValue: desiredStateVariableValues.diameter
2157
+ .divide(2)
2158
+ .simplify(),
2159
+ },
2160
+ ],
2161
+ };
2162
+ },
2163
+ };
2164
+
2165
+ stateVariableDefinitions.center = {
2166
+ forRenderer: true,
2167
+ isLocation: true,
2168
+ public: true,
2169
+ shadowingInstructions: {
2170
+ createComponentOfType: "math",
2171
+ addAttributeComponentsShadowingStateVariables:
2172
+ returnRoundingAttributeComponentShadowing(),
2173
+ returnWrappingComponents(prefix) {
2174
+ if (prefix === "centerX") {
2175
+ return [];
2176
+ } else {
2177
+ // entire array
2178
+ // wrap by both <point> and <xs>
2179
+ return [
2180
+ ["point", { componentType: "mathList", isAttribute: "xs" }],
2181
+ ];
2182
+ }
2183
+ },
2184
+ },
2185
+ isArray: true,
2186
+ entryPrefixes: ["centerX"],
2187
+ stateVariablesDeterminingDependencies: [
2188
+ "numThroughPoints",
2189
+ "havePrescribedCenter",
2190
+ "havePrescribedRadius",
2191
+ ],
2192
+ returnArraySizeDependencies: () => ({}),
2193
+ returnArraySize() {
2194
+ return [2];
2195
+ },
2196
+ returnArrayDependenciesByKey({ arrayKeys, stateValues }) {
2197
+ // console.log(`array dependencies by key for circle center`)
2198
+ // console.log(arrayKeys)
2199
+ // console.log(stateValues);
2200
+
2201
+ let globalDependencies = {};
2202
+ let dependenciesByKey = {};
2203
+
2204
+ if (stateValues.havePrescribedCenter) {
2205
+ // if have prescribed center, we separate dependencies by key
2206
+
2207
+ // add havePrescribedCenter as a value
2208
+ // so that it changes only when dependencies are recalculated
2209
+ globalDependencies.havePrescribedCenter = {
2210
+ dependencyType: "value",
2211
+ value: true,
2212
+ };
2213
+
2214
+ for (let arrayKey of arrayKeys) {
2215
+ let varEnding = Number(arrayKey) + 1;
2216
+ dependenciesByKey[arrayKey] = {
2217
+ numericalCenterX: {
2218
+ dependencyType: "stateVariable",
2219
+ variableName: "numericalCenterX" + varEnding,
2220
+ },
2221
+ prescribedCenterX: {
2222
+ dependencyType: "stateVariable",
2223
+ variableName: "prescribedCenterX" + varEnding,
2224
+ },
2225
+ };
2226
+ }
2227
+
2228
+ if (
2229
+ stateValues.havePrescribedRadius &&
2230
+ stateValues.numThroughPoints > 0
2231
+ ) {
2232
+ globalDependencies.haveCenterRadiusPoints = {
2233
+ dependencyType: "value",
2234
+ value: true,
2235
+ };
2236
+ }
2237
+ } else {
2238
+ for (let arrayKey of arrayKeys) {
2239
+ let varEnding = Number(arrayKey) + 1;
2240
+ dependenciesByKey[arrayKey] = {
2241
+ essentialCenterX: {
2242
+ dependencyType: "stateVariable",
2243
+ variableName: "essentialCenterX" + varEnding,
2244
+ },
2245
+ };
2246
+ }
2247
+
2248
+ // if don't have a prescribed center, we used global dependencies
2249
+ // (other than essential center)
2250
+ globalDependencies.numericalCenter = {
2251
+ dependencyType: "stateVariable",
2252
+ variableName: "numericalCenter",
2253
+ };
2254
+
2255
+ globalDependencies.numThroughPoints = {
2256
+ dependencyType: "stateVariable",
2257
+ variableName: "numThroughPoints",
2258
+ };
2259
+ globalDependencies.throughPoints = {
2260
+ dependencyType: "stateVariable",
2261
+ variableName: "throughPoints",
2262
+ };
2263
+
2264
+ if (stateValues.havePrescribedRadius) {
2265
+ // we call prescribedRadius as radius
2266
+ // as we will treat the same as calculated radius
2267
+ // for case with one through point
2268
+ globalDependencies.radius = {
2269
+ dependencyType: "stateVariable",
2270
+ variableName: "prescribedRadius",
2271
+ };
2272
+ } else if (stateValues.numThroughPoints == 1) {
2273
+ // if didn't have prescribed radius but just one point
2274
+ // we treat the radius calculated above as prescribed
2275
+ globalDependencies.radius = {
2276
+ dependencyType: "stateVariable",
2277
+ variableName: "radius",
2278
+ };
2279
+ }
2280
+ }
2281
+
2282
+ return { dependenciesByKey, globalDependencies };
2283
+ },
2284
+ arrayDefinitionByKey: function ({
2285
+ globalDependencyValues,
2286
+ dependencyValuesByKey,
2287
+ arrayKeys,
2288
+ }) {
2289
+ // console.log(`definition of center of circle`);
2290
+ // console.log(globalDependencyValues);
2291
+ // console.log(dependencyValuesByKey);
2292
+
2293
+ if (globalDependencyValues.havePrescribedCenter) {
2294
+ if (globalDependencyValues.haveCenterRadiusPoints) {
2295
+ console.warn(
2296
+ "Can't calculate circle with specified radius and center and through points",
2297
+ );
2298
+ return {
2299
+ setValue: {
2300
+ center: [me.fromAst("\uff3f"), me.fromAst("\uff3f")],
2301
+ },
2302
+ };
2303
+ }
2304
+
2305
+ let center = {};
2306
+ for (let arrayKey of arrayKeys) {
2307
+ if (
2308
+ Number.isFinite(dependencyValuesByKey[arrayKey].numericalCenterX)
2309
+ ) {
2310
+ center[arrayKey] = me.fromAst(
2311
+ dependencyValuesByKey[arrayKey].numericalCenterX,
2312
+ );
2313
+ } else {
2314
+ center[arrayKey] =
2315
+ dependencyValuesByKey[arrayKey].prescribedCenterX;
2316
+ }
2317
+ }
2318
+ return { setValue: { center } };
2319
+ }
2320
+
2321
+ if (
2322
+ globalDependencyValues.numericalCenter.every((x) =>
2323
+ Number.isFinite(x),
2324
+ )
2325
+ ) {
2326
+ return {
2327
+ setValue: {
2328
+ center: globalDependencyValues.numericalCenter.map((x) =>
2329
+ me.fromAst(x),
2330
+ ),
2331
+ },
2332
+ };
2333
+ }
2334
+
2335
+ if (globalDependencyValues.radius !== undefined) {
2336
+ // have a radius defined and no center
2337
+ if (globalDependencyValues.numThroughPoints === 0) {
2338
+ // only radius specified. Create centered at origin as a default.
2339
+
2340
+ let center = {};
2341
+
2342
+ for (let arrayKey of arrayKeys) {
2343
+ let value = dependencyValuesByKey[arrayKey].essentialCenterX;
2344
+ if (!(value instanceof me.class)) {
2345
+ if (Number.isFinite(value)) {
2346
+ value = me.fromAst(value);
2347
+ } else {
2348
+ value = me.fromAst("\uff3f");
2349
+ }
2350
+ }
2351
+
2352
+ center[arrayKey] = value;
2353
+ }
2354
+
2355
+ return { setValue: { center } };
2356
+ } else if (globalDependencyValues.numThroughPoints === 1) {
2357
+ // radius and one through point
2358
+ // create a circle with top being the point
2359
+
2360
+ let center;
2361
+
2362
+ // it is possible, as dependency are being worked out
2363
+ // that throughPoints[0][1], so catch the resulting error
2364
+ try {
2365
+ center = [
2366
+ globalDependencyValues.throughPoints[0][0],
2367
+ globalDependencyValues.throughPoints[0][1]
2368
+ .subtract(globalDependencyValues.radius)
2369
+ .simplify(),
2370
+ ];
2371
+ } catch (e) {
2372
+ center = [me.fromAst("\uff3f"), me.fromAst("\uff3f")];
2373
+ }
2374
+
2375
+ return { setValue: { center } };
2376
+ } else {
2377
+ console.warn(
2378
+ "Can't create circle through more than one point with given radius when don't have numerical values",
2379
+ );
2380
+ return {
2381
+ setValue: {
2382
+ center: [me.fromAst("\uff3f"), me.fromAst("\uff3f")],
2383
+ },
2384
+ };
2385
+ }
2386
+ }
2387
+
2388
+ // don't have prescribed radius
2389
+ if (globalDependencyValues.numThroughPoints === 0) {
2390
+ let center = {};
2391
+
2392
+ for (let arrayKey of arrayKeys) {
2393
+ let value = dependencyValuesByKey[arrayKey].essentialCenterX;
2394
+ if (!(value instanceof me.class)) {
2395
+ if (Number.isFinite(value)) {
2396
+ value = me.fromAst(value);
2397
+ } else {
2398
+ value = me.fromAst("\uff3f");
2399
+ }
2400
+ }
2401
+
2402
+ center[arrayKey] = value;
2403
+ }
2404
+
2405
+ return { setValue: { center } };
2406
+ } else {
2407
+ // Must have at least two points, as case with one through point
2408
+ // used calculated radius
2409
+
2410
+ console.warn(
2411
+ "Can't create circle through more than one point when don't have numerical values",
2412
+ );
2413
+ return {
2414
+ setValue: {
2415
+ center: [me.fromAst("\uff3f"), me.fromAst("\uff3f")],
2416
+ },
2417
+ };
2418
+ }
2419
+ },
2420
+
2421
+ async inverseArrayDefinitionByKey({
2422
+ desiredStateVariableValues,
2423
+ globalDependencyValues,
2424
+ stateValues,
2425
+ dependencyNamesByKey,
2426
+ workspace,
2427
+ }) {
2428
+ // console.log('inverse definition of center of circle')
2429
+ // console.log(desiredStateVariableValues)
2430
+ // console.log(globalDependencyValues)
2431
+
2432
+ if (globalDependencyValues.havePrescribedCenter) {
2433
+ let instructions = [];
2434
+
2435
+ for (let arrayKey in desiredStateVariableValues.center) {
2436
+ instructions.push({
2437
+ setDependency: dependencyNamesByKey[arrayKey].prescribedCenterX,
2438
+ desiredValue: desiredStateVariableValues.center[arrayKey],
2439
+ arrayKey,
2440
+ });
2441
+ }
2442
+
2443
+ return {
2444
+ success: true,
2445
+ instructions,
2446
+ };
2447
+ }
2448
+
2449
+ // since don't have prescribed center
2450
+ // we work with global dependency values
2451
+
2452
+ // if have any empty values in desired value,
2453
+ // merge with current values, or value from workspace
2454
+
2455
+ if (!workspace.desiredCenter) {
2456
+ workspace.desiredCenter = {};
2457
+ }
2458
+
2459
+ for (let key = 0; key < 2; key++) {
2460
+ if (desiredStateVariableValues.center[key] !== undefined) {
2461
+ workspace.desiredCenter[key] =
2462
+ desiredStateVariableValues.center[key];
2463
+ } else if (workspace.desiredCenter[key] === undefined) {
2464
+ workspace.desiredCenter[key] = (await stateValues.center)[key];
2465
+ }
2466
+ }
2467
+
2468
+ let numericalCenter = [];
2469
+ let desiredCenterIsNumeric = true;
2470
+
2471
+ for (let i = 0; i < 2; i++) {
2472
+ let component = workspace.desiredCenter[i].evaluate_to_constant();
2473
+ if (!Number.isFinite(component)) {
2474
+ desiredCenterIsNumeric = false;
2475
+ break;
2476
+ }
2477
+ numericalCenter.push(component);
2478
+ }
2479
+
2480
+ if (
2481
+ desiredCenterIsNumeric &&
2482
+ globalDependencyValues.numericalCenter.every((x) =>
2483
+ Number.isFinite(x),
2484
+ )
2485
+ ) {
2486
+ return {
2487
+ success: true,
2488
+ instructions: [
2489
+ {
2490
+ setDependency: "numericalCenter",
2491
+ desiredValue: numericalCenter,
2492
+ },
2493
+ ],
2494
+ };
2495
+ }
2496
+
2497
+ if (globalDependencyValues.numThroughPoints === 0) {
2498
+ // just change essential value of center
2499
+ // (and numericalCenter if we have a numerical center)
2500
+
2501
+ let instructions = [];
2502
+
2503
+ for (let arrayKey in desiredStateVariableValues.center) {
2504
+ instructions.push({
2505
+ setDependency: dependencyNamesByKey[arrayKey].essentialCenterX,
2506
+ desiredValue: desiredStateVariableValues.center[arrayKey],
2507
+ arrayKey,
2508
+ });
2509
+ }
2510
+
2511
+ return {
2512
+ success: true,
2513
+ instructions,
2514
+ };
2515
+ } else {
2516
+ console.warn(
2517
+ "Haven't implemented changing center of circle through points with non numerical values",
2518
+ );
2519
+ return { success: false };
2520
+ }
2521
+ },
2522
+ };
2523
+
2524
+ stateVariableDefinitions.nearestPoint = {
2525
+ returnDependencies: () => ({
2526
+ numericalCenter: {
2527
+ dependencyType: "stateVariable",
2528
+ variableName: "numericalCenter",
2529
+ },
2530
+ numericalRadius: {
2531
+ dependencyType: "stateVariable",
2532
+ variableName: "numericalRadius",
2533
+ },
2534
+ }),
2535
+ definition({ dependencyValues }) {
2536
+ let radius = dependencyValues.numericalRadius;
2537
+ let centerX = dependencyValues.numericalCenter[0];
2538
+ let centerY = dependencyValues.numericalCenter[1];
2539
+ return {
2540
+ setValue: {
2541
+ nearestPoint: function ({ variables, scales }) {
2542
+ let x1 = variables.x1?.evaluate_to_constant();
2543
+ let x2 = variables.x2?.evaluate_to_constant();
2544
+
2545
+ if (!(Number.isFinite(x1) && Number.isFinite(x2))) {
2546
+ return {};
2547
+ }
2548
+
2549
+ if (
2550
+ !(
2551
+ Number.isFinite(centerX) &&
2552
+ Number.isFinite(centerY) &&
2553
+ Number.isFinite(radius)
2554
+ )
2555
+ ) {
2556
+ return {};
2557
+ }
2558
+
2559
+ let theta = Math.atan2(x2 - centerY, x1 - centerX);
2560
+
2561
+ let result = {
2562
+ x1: centerX + radius * Math.cos(theta),
2563
+ x2: centerY + radius * Math.sin(theta),
2564
+ };
2565
+
2566
+ if (variables.x3 !== undefined) {
2567
+ result.x3 = 0;
2568
+ }
2569
+
2570
+ return result;
2571
+ },
2572
+ },
2573
+ };
2574
+ },
2575
+ };
2576
+
2577
+ return stateVariableDefinitions;
2578
+ }
2579
+
2580
+ async moveCircle({
2581
+ center,
2582
+ radius,
2583
+ throughAngles,
2584
+ transient,
2585
+ actionId,
2586
+ sourceInformation = {},
2587
+ skipRendererUpdate = false,
2588
+ }) {
2589
+ let instructions = [];
2590
+
2591
+ let numThroughPoints = await this.stateValues.numThroughPoints;
2592
+ let numericalPrescribedCenter = await this.stateValues
2593
+ .numericalPrescribedCenter;
2594
+
2595
+ if (numThroughPoints <= 1 || numericalPrescribedCenter.length > 0) {
2596
+ instructions.push({
2597
+ updateType: "updateValue",
2598
+ componentName: this.componentName,
2599
+ stateVariable: "numericalCenter",
2600
+ value: center,
2601
+ });
2602
+ }
2603
+
2604
+ let numericalThroughPoints = [];
2605
+
2606
+ if (numThroughPoints >= 1) {
2607
+ // set numerical through points for two reasons
2608
+ // 1. if have circle prescribed by center and one point
2609
+ // need to move the point to preserve the radius
2610
+ // 2. if have through points that are constrained/attracted
2611
+ // to objects, set through points to attempt to keep their relative
2612
+ // positions constant even as they get adjusted by the constraints
2613
+
2614
+ if (throughAngles === undefined) {
2615
+ throughAngles = await this.stateValues.throughAngles;
2616
+ }
2617
+ if (radius === undefined) {
2618
+ radius = await this.stateValues.numericalRadius;
2619
+ }
2620
+
2621
+ for (let i = 0; i < numThroughPoints; i++) {
2622
+ let theta = throughAngles[i];
2623
+ let pt = [
2624
+ center[0] + radius * Math.cos(theta),
2625
+ center[1] + radius * Math.sin(theta),
2626
+ ];
2627
+ numericalThroughPoints.push(pt);
2628
+ }
2629
+
2630
+ instructions.push({
2631
+ updateType: "updateValue",
2632
+ componentName: this.componentName,
2633
+ stateVariable: "numericalThroughPoints",
2634
+ value: numericalThroughPoints,
2635
+ });
2636
+ }
2637
+
2638
+ // Note: we set skipRendererUpdate to true
2639
+ // so that we can make further adjustments before the renderers are updated
2640
+ if (transient) {
2641
+ await this.coreFunctions.performUpdate({
2642
+ updateInstructions: instructions,
2643
+ transient,
2644
+ actionId,
2645
+ sourceInformation,
2646
+ skipRendererUpdate: true,
2647
+ });
2648
+ } else {
2649
+ await this.coreFunctions.performUpdate({
2650
+ updateInstructions: instructions,
2651
+ actionId,
2652
+ sourceInformation,
2653
+ skipRendererUpdate: true,
2654
+ event: {
2655
+ verb: "interacted",
2656
+ object: {
2657
+ componentName: this.componentName,
2658
+ componentType: this.componentType,
2659
+ },
2660
+ result: {
2661
+ center,
2662
+ },
2663
+ },
2664
+ });
2665
+ }
2666
+
2667
+ // we attempt to keep the radius of the circle fixed
2668
+ // even if one of the points defining it is constrained
2669
+
2670
+ // if circle is based on more than 1 point (center or throughpoints)
2671
+ // and a subset of those points appear to be constrained while preserving their relationship
2672
+ // then move the other points in attempt to preserve their relationship with the constrained points,
2673
+ // which will keep the radius of the circle fixed
2674
+
2675
+ let resultingCenter = await this.stateValues.numericalCenter;
2676
+ let resultingNumericalThroughPoints = await this.stateValues
2677
+ .numericalThroughPoints;
2678
+ let tol = 1e-6;
2679
+
2680
+ if (numericalPrescribedCenter.length > 0 && numThroughPoints === 1) {
2681
+ // center and one through point
2682
+
2683
+ let throughPointUnchanged = numericalThroughPoints[0].every(
2684
+ (v, i) => v === resultingNumericalThroughPoints[0][i],
2685
+ );
2686
+
2687
+ let centerUnchanged = center.every((v, i) => v === resultingCenter[i]);
2688
+
2689
+ if (throughPointUnchanged && !centerUnchanged) {
2690
+ let theta = throughAngles[0];
2691
+ let newNumericalThroughPoints = [
2692
+ [
2693
+ resultingCenter[0] + radius * Math.cos(theta),
2694
+ resultingCenter[1] + radius * Math.sin(theta),
2695
+ ],
2696
+ ];
2697
+
2698
+ let newInstructions = [
2699
+ {
2700
+ updateType: "updateValue",
2701
+ componentName: this.componentName,
2702
+ stateVariable: "numericalThroughPoints",
2703
+ value: newNumericalThroughPoints,
2704
+ },
2705
+ ];
2706
+ return await this.coreFunctions.performUpdate({
2707
+ updateInstructions: newInstructions,
2708
+ transient,
2709
+ actionId,
2710
+ sourceInformation,
2711
+ skipRendererUpdate,
2712
+ });
2713
+ } else if (centerUnchanged && !throughPointUnchanged) {
2714
+ let theta = throughAngles[0];
2715
+ let newCenter = [
2716
+ resultingNumericalThroughPoints[0][0] - radius * Math.cos(theta),
2717
+ resultingNumericalThroughPoints[0][1] - radius * Math.sin(theta),
2718
+ ];
2719
+
2720
+ let newInstructions = [
2721
+ {
2722
+ updateType: "updateValue",
2723
+ componentName: this.componentName,
2724
+ stateVariable: "numericalCenter",
2725
+ value: newCenter,
2726
+ },
2727
+ ];
2728
+ return await this.coreFunctions.performUpdate({
2729
+ updateInstructions: newInstructions,
2730
+ transient,
2731
+ actionId,
2732
+ sourceInformation,
2733
+ skipRendererUpdate,
2734
+ });
2735
+ }
2736
+ } else if (numThroughPoints >= 2) {
2737
+ let throughPointsChanged = [];
2738
+ let numThroughPointsChanged = 0;
2739
+
2740
+ for (let [ind, pt] of numericalThroughPoints.entries()) {
2741
+ if (
2742
+ !pt.every(
2743
+ (v, i) =>
2744
+ Math.abs(v - resultingNumericalThroughPoints[ind][i]) < tol,
2745
+ )
2746
+ ) {
2747
+ throughPointsChanged.push(ind);
2748
+ numThroughPointsChanged++;
2749
+ }
2750
+ }
2751
+
2752
+ if (
2753
+ numThroughPointsChanged > 0 &&
2754
+ numThroughPointsChanged < numThroughPoints
2755
+ ) {
2756
+ // A subset of points were altered from the requested location.
2757
+ // Check to see if the relationship among them is preserved
2758
+
2759
+ let changedInd1 = throughPointsChanged[0];
2760
+ let relationshipPreserved = true;
2761
+
2762
+ if (numThroughPointsChanged > 1) {
2763
+ let orig1 = numericalThroughPoints[changedInd1];
2764
+ let changed1 = resultingNumericalThroughPoints[changedInd1];
2765
+
2766
+ let changevec1 = orig1.map((v, i) => v - changed1[i]);
2767
+
2768
+ for (let ind of throughPointsChanged.slice(1)) {
2769
+ let orig2 = numericalThroughPoints[ind];
2770
+ let changed2 = resultingNumericalThroughPoints[ind];
2771
+ let changevec2 = orig2.map((v, i) => v - changed2[i]);
2772
+
2773
+ if (
2774
+ !changevec1.every((v, i) => Math.abs(v - changevec2[i]) < tol)
2775
+ ) {
2776
+ relationshipPreserved = false;
2777
+ break;
2778
+ }
2779
+ }
2780
+ }
2781
+
2782
+ if (relationshipPreserved) {
2783
+ let thetaOfChanged = throughAngles[changedInd1];
2784
+
2785
+ let newCenter = [
2786
+ resultingNumericalThroughPoints[changedInd1][0] -
2787
+ radius * Math.cos(thetaOfChanged),
2788
+ resultingNumericalThroughPoints[changedInd1][1] -
2789
+ radius * Math.sin(thetaOfChanged),
2790
+ ];
2791
+
2792
+ let newNumericalThroughPoints = [];
2793
+
2794
+ for (let i = 0; i < numThroughPoints; i++) {
2795
+ if (throughPointsChanged.includes(i)) {
2796
+ newNumericalThroughPoints.push(
2797
+ resultingNumericalThroughPoints[i],
2798
+ );
2799
+ } else {
2800
+ let theta = throughAngles[i];
2801
+ let pt = [
2802
+ newCenter[0] + radius * Math.cos(theta),
2803
+ newCenter[1] + radius * Math.sin(theta),
2804
+ ];
2805
+ newNumericalThroughPoints.push(pt);
2806
+ }
2807
+ }
2808
+
2809
+ let newInstructions = [
2810
+ {
2811
+ updateType: "updateValue",
2812
+ componentName: this.componentName,
2813
+ stateVariable: "numericalThroughPoints",
2814
+ value: newNumericalThroughPoints,
2815
+ },
2816
+ ];
2817
+ return await this.coreFunctions.performUpdate({
2818
+ updateInstructions: newInstructions,
2819
+ transient,
2820
+ actionId,
2821
+ sourceInformation,
2822
+ skipRendererUpdate,
2823
+ });
2824
+ }
2825
+ }
2826
+ }
2827
+
2828
+ // if no modifications were made, still need to update renderers
2829
+ // as original update was performed with skipping renderer update
2830
+ return await this.coreFunctions.updateRenderers({
2831
+ actionId,
2832
+ sourceInformation,
2833
+ skipRendererUpdate,
2834
+ });
2835
+ }
2836
+
2837
+ async circleClicked({
2838
+ actionId,
2839
+ name,
2840
+ sourceInformation = {},
2841
+ skipRendererUpdate = false,
2842
+ }) {
2843
+ if (!(await this.stateValues.fixed)) {
2844
+ await this.coreFunctions.triggerChainedActions({
2845
+ triggeringAction: "click",
2846
+ componentName: name, // use name rather than this.componentName to get original name if adapted
2847
+ actionId,
2848
+ sourceInformation,
2849
+ skipRendererUpdate,
2850
+ });
2851
+ }
2852
+
2853
+ this.coreFunctions.resolveAction({ actionId });
2854
+ }
2855
+
2856
+ async circleFocused({
2857
+ actionId,
2858
+ name,
2859
+ sourceInformation = {},
2860
+ skipRendererUpdate = false,
2861
+ }) {
2862
+ if (!(await this.stateValues.fixed)) {
2863
+ await this.coreFunctions.triggerChainedActions({
2864
+ triggeringAction: "focus",
2865
+ componentName: name, // use name rather than this.componentName to get original name if adapted
2866
+ actionId,
2867
+ sourceInformation,
2868
+ skipRendererUpdate,
2869
+ });
2870
+ }
2871
+
2872
+ this.coreFunctions.resolveAction({ actionId });
2873
+ }
2874
+ }
2875
+
2876
+ function circleFromTwoNumericalPoints({ point1, point2 }) {
2877
+ let xcenter = (point1[0] + point2[0]) / 2;
2878
+ let ycenter = (point1[1] + point2[1]) / 2;
2879
+ let numericalCenter = [xcenter, ycenter];
2880
+ let numericalRadius = Math.sqrt(
2881
+ Math.pow(xcenter - point1[0], 2) + Math.pow(ycenter - point1[1], 2),
2882
+ );
2883
+ return { numericalCenter, numericalRadius };
2884
+ }