Skip to content

Commit

Permalink
feat: make components nestable
Browse files Browse the repository at this point in the history
  • Loading branch information
ksassnowski authored Aug 22, 2024
1 parent 4382047 commit e047572
Show file tree
Hide file tree
Showing 16 changed files with 149 additions and 99 deletions.
6 changes: 5 additions & 1 deletion src/components/Angle.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
:color="color"
:size="labelSize"
/>

<slot />
</g>
</template>

Expand All @@ -33,6 +35,7 @@ import { useGraphContext } from "../composables/useGraphContext.ts";
import { Color } from "../types.ts";
import { useColors } from "../composables/useColors.ts";
import { usePointerIntersection } from "../composables/usePointerIntersection.ts";
import { useLocalToWorld } from "../composables/useLocalToWorld.ts";
import { pointInsideSector } from "../utils/geometry.ts";
import Label from "./Label.vue";
Expand All @@ -58,7 +61,8 @@ const props = withDefaults(
},
);
const { matrix, invScale } = useGraphContext();
const { invScale } = useGraphContext();
const matrix = useLocalToWorld(toRef(props, "position"));
const { parseColor } = useColors();
const stroke = parseColor(toRef(props, "color"), "stroke");
Expand Down
4 changes: 3 additions & 1 deletion src/components/Arc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
:color="color"
:label="label"
:label-size="labelSize"
/>
>
<slot />
</Angle>
</template>

<script setup lang="ts">
Expand Down
26 changes: 15 additions & 11 deletions src/components/Circle.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
<template>
<circle
v-bind="$attrs"
:cx="position.x"
:cy="position.y"
:r="scaledRadius"
:fill="fill"
:stroke="stroke"
:stroke-width="lineWidth * invScale"
:stroke-dasharray="dashArray"
/>
<g v-bind="$attrs">
<circle
:cx="position.x"
:cy="position.y"
:r="scaledRadius"
:fill="fill"
:stroke="stroke"
:stroke-width="lineWidth * invScale"
:stroke-dasharray="dashArray"
/>
<slot />
</g>
</template>

<script setup lang="ts">
Expand All @@ -19,6 +21,7 @@ import { useGraphContext } from "../composables/useGraphContext.ts";
import { type Color } from "../types.ts";
import { useColors } from "../composables/useColors.ts";
import { usePointerIntersection } from "../composables/usePointerIntersection.ts";
import { useLocalToWorld } from "../composables/useLocalToWorld.ts";
import { pointInsideCircle } from "../utils/geometry.ts";
const props = withDefaults(
Expand All @@ -37,7 +40,8 @@ const props = withDefaults(
},
);
const { matrix, invScale } = useGraphContext();
const { invScale } = useGraphContext();
const matrix = useLocalToWorld(toRef(props, "position"));
const { parseColor } = useColors();
const stroke = parseColor(toRef(props, "color"), "stroke");
Expand Down
30 changes: 17 additions & 13 deletions src/components/Ellipse.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
<template>
<ellipse
v-bind="$attrs"
:cx="position.x"
:cy="position.y"
:rx="scaledRadius.x"
:ry="scaledRadius.y"
:fill="fill"
:stroke="stroke"
:stroke-width="lineWidth * invScale"
:stroke-dasharray="dashArray"
:transform="`rotate(${-rotation}, ${position.x}, ${position.y})`"
/>
<g v-bind="$attrs">
<ellipse
:cx="position.x"
:cy="position.y"
:rx="scaledRadius.x"
:ry="scaledRadius.y"
:fill="fill"
:stroke="stroke"
:stroke-width="lineWidth * invScale"
:stroke-dasharray="dashArray"
:transform="`rotate(${-rotation}, ${position.x}, ${position.y})`"
/>
<slot />
</g>
</template>

<script setup lang="ts">
Expand All @@ -21,6 +23,7 @@ import { useGraphContext } from "../composables/useGraphContext.ts";
import { type Color } from "../types.ts";
import { useColors } from "../composables/useColors.ts";
import { usePointerIntersection } from "../composables/usePointerIntersection.ts";
import { useLocalToWorld } from "../composables/useLocalToWorld.ts";
import { pointInsideEllipse } from "../utils/geometry.ts";
const props = withDefaults(
Expand All @@ -44,7 +47,8 @@ const props = withDefaults(
},
);
const { matrix, invScale } = useGraphContext();
const { invScale } = useGraphContext();
const matrix = useLocalToWorld(toRef(props, "position"));
const { parseColor } = useColors();
const stroke = parseColor(toRef(props, "color"), "stroke");
Expand Down
15 changes: 8 additions & 7 deletions src/components/FunctionPlot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { computed, onMounted, ref, toRef, watch } from "vue";
import { Color } from "../types.ts";
import { useGraphContext } from "../composables/useGraphContext.ts";
import { useLocalToWorld } from "../composables/useLocalToWorld.ts";
import { PossibleVector2, Vector2 } from "../utils/Vector2.ts";
import { useColors } from "../composables/useColors.ts";
Expand All @@ -35,7 +36,8 @@ const props = withDefaults(
let animationFrameID: number | null = null;
const { domain, scale, offset, size, invScale } = useGraphContext();
const { domain, size, invScale } = useGraphContext();
const matrix = useLocalToWorld();
const { parseColor } = useColors();
const color = parseColor(toRef(props, "color"), "stroke");
Expand All @@ -53,18 +55,17 @@ const visiblePoints = computed(() => {
});
const path = computed(() => {
const points = visiblePoints.value;
const points = visiblePoints.value.map((point) =>
point.transform(matrix.value),
);
if (points.length === 0) {
return "";
}
return `M ${points[0].x * scale.value.x + offset.value.x} ${points[0].y * scale.value.y + offset.value.y} L ${points
return `M ${points[0].x} ${points[0].y} L ${points
.slice(1)
.map(
(p) =>
`${p.x * scale.value.x + offset.value.x} ${p.y * scale.value.y + offset.value.y}`,
)
.map((p) => `${p.x} ${p.y}`)
.join("L ")}`;
});
Expand Down
10 changes: 5 additions & 5 deletions src/components/Graph.vue
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
<script setup lang="ts">
import { computed, onMounted, provide, ref } from "vue";
import { graphContext } from "../types.ts";
import { graphContext, parentToWorld } from "../types.ts";
import { PossibleVector2, Vector2 } from "../utils/Vector2.ts";
import { useColors } from "../composables/useColors.ts";
import { Matrix2D } from "../utils/Matrix2D.ts";
Expand Down Expand Up @@ -187,10 +187,9 @@ const size = computed(() => {
const invScale = computed(() => Math.max(1, props.width / size.value.x));
const matrixWorld = computed(() => {
const matrix = new Matrix2D();
matrix.translate([offset.value.x, offset.value.y]);
matrix.scale([scale.value.x, -scale.value.y]);
return matrix;
return new Matrix2D()
.translate([offset.value.x, offset.value.y])
.scale([scale.value.x, -scale.value.y]);
});
const cursor = computed<Vector2 | null>(() => {
Expand All @@ -215,6 +214,7 @@ const context = {
};
provide(graphContext, context);
provide(parentToWorld, matrixWorld);
function formatLabelValue(value: number) {
return value.toFixed(2).replace(/\.?0+$/, "");
Expand Down
6 changes: 5 additions & 1 deletion src/components/Label.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
>
{{ text }}
</text>

<slot />
</g>
</template>

Expand All @@ -33,6 +35,7 @@ import { type PossibleVector2, Vector2 } from "../utils/Vector2.ts";
import { useGraphContext } from "../composables/useGraphContext.ts";
import { useColors } from "../composables/useColors.ts";
import { usePointerIntersection } from "../composables/usePointerIntersection.ts";
import { useLocalToWorld } from "../composables/useLocalToWorld.ts";
import { pointInsideRectangle } from "../utils/geometry.ts";
const props = withDefaults(
Expand Down Expand Up @@ -64,7 +67,8 @@ const sizes = {
large: 12,
};
const { matrix, invScale } = useGraphContext();
const { invScale } = useGraphContext();
const matrix = useLocalToWorld(toRef(props, "position"));
const { colors, parseColor } = useColors();
const color = parseColor(toRef(props, "color"), "stroke");
Expand Down
6 changes: 5 additions & 1 deletion src/components/Line.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
:color="color"
:size="labelSize"
/>

<slot />
</g>
</template>

Expand All @@ -28,6 +30,7 @@ import Label from "./Label.vue";
import { useGraphContext } from "../composables/useGraphContext.ts";
import { useColors } from "../composables/useColors.ts";
import { usePointerIntersection } from "../composables/usePointerIntersection.ts";
import { useLocalToWorld } from "../composables/useLocalToWorld.ts";
import { distanceToLineSegment } from "../utils/geometry.ts";
const props = withDefaults(
Expand Down Expand Up @@ -56,8 +59,9 @@ if (props.to === undefined && props.slope === undefined) {
throw new Error("Line requires either a `to` prop or a `slope` prop");
}
const { domain, matrix, invScale } = useGraphContext();
const { domain, invScale } = useGraphContext();
const { parseColor } = useColors();
const matrix = useLocalToWorld();
const color = parseColor(toRef(props, "color"), "stroke");
const active = defineModel("active", { default: false });
Expand Down
8 changes: 6 additions & 2 deletions src/components/Point.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
:color="color"
size="small"
/>

<slot />
</g>
</template>

Expand All @@ -29,6 +31,7 @@ import Label from "./Label.vue";
import { useGraphContext } from "../composables/useGraphContext.ts";
import { useColors } from "../composables/useColors.ts";
import { usePointerIntersection } from "../composables/usePointerIntersection.ts";
import { useLocalToWorld } from "../composables/useLocalToWorld.ts";
import { pointInsideCircle } from "../utils/geometry.ts";
type LabelPosition = "top" | "bottom" | "left" | "right";
Expand All @@ -54,13 +57,14 @@ const props = withDefaults(
},
);
const { matrix, invScale } = useGraphContext();
const { invScale } = useGraphContext();
const matrix = useLocalToWorld(toRef(props, "position"));
const { parseColor } = useColors();
const color = parseColor(toRef(props, "color"), "points");
const labelActive = ref(false);
const active = defineModel("active", { default: false });
usePointerIntersection(active, (point) => {
const center = Vector2.wrap(props.position);
const center = position.value;
const pointActive = pointInsideCircle(
center,
(props.radius + props.lineWidth) / matrix.value.a +
Expand Down
4 changes: 3 additions & 1 deletion src/components/PolyLine.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { PossibleVector2, Vector2 } from "../utils/Vector2.ts";
import { useGraphContext } from "../composables/useGraphContext.ts";
import { useColors } from "../composables/useColors.ts";
import { usePointerIntersection } from "../composables/usePointerIntersection.ts";
import { useLocalToWorld } from "../composables/useLocalToWorld.ts";
import { distanceToLineSegment } from "../utils/geometry.ts";
const props = withDefaults(
Expand All @@ -37,7 +38,8 @@ const props = withDefaults(
},
);
const { matrix, invScale } = useGraphContext();
const { invScale } = useGraphContext();
const matrix = useLocalToWorld();
const { parseColor } = useColors();
const color = parseColor(toRef(props, "color"), "stroke");
Expand Down
8 changes: 6 additions & 2 deletions src/components/Polygon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
:radius="angleRadius"
:dashed="angleDashed"
/>

<slot />
</g>
</template>

Expand All @@ -28,6 +30,7 @@ import { type PossibleVector2, Vector2 } from "../utils/Vector2.ts";
import { useGraphContext } from "../composables/useGraphContext.ts";
import { useColors } from "../composables/useColors.ts";
import { usePointerIntersection } from "../composables/usePointerIntersection.ts";
import { useLocalToWorld } from "../composables/useLocalToWorld.ts";
import { pointInsidePolygon } from "../utils/geometry.ts";
const props = withDefaults(
Expand All @@ -52,8 +55,9 @@ if (props.vertices.length < 3) {
throw new Error("A polygon must have at least 3 vertices");
}
const { matrix, invScale } = useGraphContext();
const { invScale } = useGraphContext();
const { parseColor } = useColors();
const matrix = useLocalToWorld();
const stroke = parseColor(toRef(props, "color"), "stroke");
const fill = parseColor(toRef(props, "fill"));
Expand All @@ -62,7 +66,7 @@ usePointerIntersection(active, (point) =>
pointInsidePolygon(vertices.value, point),
);
const vertices = computed(() => props.vertices.map((v) => Vector2.wrap(v)));
const vertices = computed(() => props.vertices.map(Vector2.wrap));
const points = computed(() =>
vertices.value.map((v) => v.transform(matrix.value)),
);
Expand Down
24 changes: 14 additions & 10 deletions src/components/Sector.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<template>
<path
v-bind="$attrs"
:d="`M ${position.x} ${position.y} L ${line1To.x} ${line1To.y} A ${scaledRadius} ${scaledRadius} 0 ${sweep} 0 ${line2To.x} ${line2To.y} L ${position.x} ${position.y} z`"
:stroke="strokeColor"
:stroke-width="lineWidth * invScale"
stroke-linejoin="bevel"
:fill="fillColor"
:stroke-dasharray="dashArray"
/>
<g v-bind="$attrs">
<path
:d="`M ${position.x} ${position.y} L ${line1To.x} ${line1To.y} A ${scaledRadius} ${scaledRadius} 0 ${sweep} 0 ${line2To.x} ${line2To.y} L ${position.x} ${position.y} z`"
:stroke="strokeColor"
:stroke-width="lineWidth * invScale"
stroke-linejoin="bevel"
:fill="fillColor"
:stroke-dasharray="dashArray"
/>
<slot />
</g>
</template>

<script setup lang="ts">
Expand All @@ -19,6 +21,7 @@ import { DEG2RAD } from "../utils/constants.ts";
import { useGraphContext } from "../composables/useGraphContext.ts";
import { useColors } from "../composables/useColors.ts";
import { usePointerIntersection } from "../composables/usePointerIntersection.ts";
import { useLocalToWorld } from "../composables/useLocalToWorld.ts";
import { pointInsideSector } from "../utils/geometry.ts";
const props = withDefaults(
Expand All @@ -43,7 +46,8 @@ const props = withDefaults(
},
);
const { matrix, invScale } = useGraphContext();
const { invScale } = useGraphContext();
const matrix = useLocalToWorld(toRef(props, "position"));
const { color, fill } = toRefs(props);
const { parseColor } = useColors();
const active = defineModel("active", { default: false });
Expand Down
Loading

0 comments on commit e047572

Please sign in to comment.