@ai-gui/plugin-physics 0.17.0 → 0.18.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @ai-gui/plugin-physics
2
2
 
3
+ ## 0.18.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [a22ba20]
8
+ - @ai-gui/core@0.18.0
9
+
10
+ ## 0.17.1
11
+
12
+ ### Patch Changes
13
+
14
+ - a06c692: **The view box now holds the angle marks, the labels and the hatching.**
15
+
16
+ It was measured from bodies, surfaces and vector tips only. Everything else a mechanics diagram draws
17
+ sits outside those: the `30°` at the foot of an incline is an arc 28 from its vertex with a label 14
18
+ further out and was measured by nothing at all, so on any diagram whose incline reaches the left edge it
19
+ was simply outside the picture — the one label a 斜面 diagram cannot do without. A vector's label is
20
+ written past its arrowhead and then runs the width of its own text, so "mg cos(30°)" hung off the frame
21
+ beside an arrow pointing at nothing. Hatching hangs below its surface by its own length.
22
+
23
+ Text is estimated rather than measured — this renders to a string, with no DOM to measure in — and errs
24
+ wide: CJK at a full em, Latin at half. A box slightly too big shows white space; one slightly too small
25
+ cuts a word in half. An explicit `view` is still left exactly as given.
26
+
27
+ - @ai-gui/core@0.17.1
28
+
3
29
  ## 0.17.0
4
30
 
5
31
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -193,7 +193,25 @@ function viewBoxOf(diagram) {
193
193
  for (const vector of diagram.vectors ?? []) {
194
194
  const start = vector.from ?? diagram.bodies?.[0]?.at ?? [0, 0];
195
195
  note(start);
196
- note(tipOf(vector, start));
196
+ const tip = tipOf(vector, start);
197
+ note(tip);
198
+ if (vector.label) {
199
+ const lx = tip[0] + (tip[0] - start[0]) * .08 + 8;
200
+ const ly = tip[1] + (tip[1] - start[1]) * .08;
201
+ note([lx, ly]);
202
+ note([lx + textWidth(vector.label), ly + LABEL_HEIGHT]);
203
+ note([lx, ly - LABEL_HEIGHT]);
204
+ }
205
+ }
206
+ for (const angle of diagram.angles ?? []) {
207
+ const radius = angle.radius ?? 28;
208
+ const reach = radius + (angle.label ? ANGLE_LABEL_OFFSET + textWidth(angle.label) : 0);
209
+ note([angle.at[0] - reach, angle.at[1] - reach]);
210
+ note([angle.at[0] + reach, angle.at[1] + reach]);
211
+ }
212
+ for (const surface of diagram.surfaces ?? []) {
213
+ if (!surface.hatch) continue;
214
+ note([Math.min(surface.from[0], surface.to[0]) - HATCH_REACH, Math.min(surface.from[1], surface.to[1]) - HATCH_REACH]);
197
215
  }
198
216
  if (xs.length === 0) return [
199
217
  0,
@@ -209,6 +227,24 @@ function viewBoxOf(diagram) {
209
227
  Math.max(...ys) + pad
210
228
  ];
211
229
  }
230
+ /** How far past the arc a label sits, as the renderer places it. */
231
+ const ANGLE_LABEL_OFFSET = 14;
232
+ /** How far the hatching hangs off a surface, as the renderer draws it. */
233
+ const HATCH_REACH = 15;
234
+ /** One line of label text, in the same units the diagram is drawn in. */
235
+ const LABEL_HEIGHT = 14;
236
+ /**
237
+ * Roughly how wide a label will be.
238
+ *
239
+ * An estimate on purpose: measuring text needs a DOM, and this renders to a string on whatever runs it.
240
+ * Wide-erring — CJK counted at a full em, Latin at half — because a box slightly too big shows white
241
+ * space, and a box slightly too small cuts a word off.
242
+ */
243
+ function textWidth(label) {
244
+ let width = 0;
245
+ for (const character of label) width += /[\u3000-\u9fff\uff00-\uffef]/.test(character) ? LABEL_HEIGHT : LABEL_HEIGHT * .55;
246
+ return width;
247
+ }
212
248
  /** Where a vector ends, whether it was given as a point or as a magnitude and angle. */
213
249
  function tipOf(vector, start) {
214
250
  if (vector.to) return vector.to;
package/dist/index.js CHANGED
@@ -191,7 +191,25 @@ function viewBoxOf(diagram) {
191
191
  for (const vector of diagram.vectors ?? []) {
192
192
  const start = vector.from ?? diagram.bodies?.[0]?.at ?? [0, 0];
193
193
  note(start);
194
- note(tipOf(vector, start));
194
+ const tip = tipOf(vector, start);
195
+ note(tip);
196
+ if (vector.label) {
197
+ const lx = tip[0] + (tip[0] - start[0]) * .08 + 8;
198
+ const ly = tip[1] + (tip[1] - start[1]) * .08;
199
+ note([lx, ly]);
200
+ note([lx + textWidth(vector.label), ly + LABEL_HEIGHT]);
201
+ note([lx, ly - LABEL_HEIGHT]);
202
+ }
203
+ }
204
+ for (const angle of diagram.angles ?? []) {
205
+ const radius = angle.radius ?? 28;
206
+ const reach = radius + (angle.label ? ANGLE_LABEL_OFFSET + textWidth(angle.label) : 0);
207
+ note([angle.at[0] - reach, angle.at[1] - reach]);
208
+ note([angle.at[0] + reach, angle.at[1] + reach]);
209
+ }
210
+ for (const surface of diagram.surfaces ?? []) {
211
+ if (!surface.hatch) continue;
212
+ note([Math.min(surface.from[0], surface.to[0]) - HATCH_REACH, Math.min(surface.from[1], surface.to[1]) - HATCH_REACH]);
195
213
  }
196
214
  if (xs.length === 0) return [
197
215
  0,
@@ -207,6 +225,24 @@ function viewBoxOf(diagram) {
207
225
  Math.max(...ys) + pad
208
226
  ];
209
227
  }
228
+ /** How far past the arc a label sits, as the renderer places it. */
229
+ const ANGLE_LABEL_OFFSET = 14;
230
+ /** How far the hatching hangs off a surface, as the renderer draws it. */
231
+ const HATCH_REACH = 15;
232
+ /** One line of label text, in the same units the diagram is drawn in. */
233
+ const LABEL_HEIGHT = 14;
234
+ /**
235
+ * Roughly how wide a label will be.
236
+ *
237
+ * An estimate on purpose: measuring text needs a DOM, and this renders to a string on whatever runs it.
238
+ * Wide-erring — CJK counted at a full em, Latin at half — because a box slightly too big shows white
239
+ * space, and a box slightly too small cuts a word off.
240
+ */
241
+ function textWidth(label) {
242
+ let width = 0;
243
+ for (const character of label) width += /[\u3000-\u9fff\uff00-\uffef]/.test(character) ? LABEL_HEIGHT : LABEL_HEIGHT * .55;
244
+ return width;
245
+ }
210
246
  /** Where a vector ends, whether it was given as a point or as a magnitude and angle. */
211
247
  function tipOf(vector, start) {
212
248
  if (vector.to) return vector.to;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-gui/plugin-physics",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Force and vector diagrams for teaching mechanics, rendered as SVG from a declarative block.",
5
5
  "keywords": [
6
6
  "llm",
@@ -54,7 +54,7 @@
54
54
  "access": "public"
55
55
  },
56
56
  "dependencies": {
57
- "@ai-gui/core": "0.17.0"
57
+ "@ai-gui/core": "0.18.0"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "tsdown",