Skip to content

Commit

Permalink
Implemented error messages in types
Browse files Browse the repository at this point in the history
  • Loading branch information
RunDevelopment committed Jan 9, 2024
1 parent 1291c2b commit 4085610
Show file tree
Hide file tree
Showing 15 changed files with 145 additions and 83 deletions.
4 changes: 4 additions & 0 deletions backend/src/navi.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ def intersect(*items: ExpressionJson) -> ExpressionJson:
return {"type": "intersection", "items": list(items)}


def intersect_with_error(*items: ExpressionJson) -> ExpressionJson:
return union(intersect(*items), *[intersect("Error", item) for item in items])


def named(name: str, fields: dict[str, ExpressionJson] | None = None) -> ExpressionJson:
return {"type": "named", "name": name, "fields": fields}

Expand Down
2 changes: 1 addition & 1 deletion backend/src/nodes/properties/outputs/file_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def __init__(
if of_input is None
else f"splitFilePath(Input{of_input}.path).dir"
)
directory_type = navi.intersect(directory_type, output_type)
directory_type = navi.intersect_with_error(directory_type, output_type)
super().__init__(directory_type, label, associated_type=str)

def get_broadcast_type(self, value: str):
Expand Down
8 changes: 5 additions & 3 deletions backend/src/nodes/properties/outputs/generic_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(
output_type: navi.ExpressionJson = "number",
):
super().__init__(
navi.intersect("number", output_type),
navi.intersect_with_error("number", output_type),
label,
associated_type=Union[int, float],
)
Expand All @@ -36,7 +36,7 @@ def __init__(
label: str,
output_type: navi.ExpressionJson = "string",
):
super().__init__(navi.intersect("string", output_type), label)
super().__init__(navi.intersect_with_error("string", output_type), label)

def get_broadcast_type(self, value: str):
return navi.literal(value)
Expand Down Expand Up @@ -73,7 +73,9 @@ def __init__(
channels: int | None = None,
):
super().__init__(
output_type=navi.intersect(color_type, navi.Color(channels=channels)),
output_type=navi.intersect_with_error(
color_type, navi.Color(channels=channels)
),
label=label,
kind="generic",
)
Expand Down
2 changes: 1 addition & 1 deletion backend/src/nodes/properties/outputs/numpy_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def __init__(
assume_normalized: bool = False,
):
super().__init__(
navi.intersect(image_type, navi.Image(channels=channels)),
navi.intersect_with_error(image_type, navi.Image(channels=channels)),
label,
kind=kind,
has_handle=has_handle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,24 @@
let source = Input0;
let guide = Input1;
let valid = bool::and(
// guide image must be larger than source image
guide.width > source.width,
let kScale = bool::and(
// guide image's size must be `k * source.size` for `k>1`
guide.width / source.width == int,
guide.width / source.width == guide.height / source.height
);
Image {
width: guide.width,
height: guide.height,
channels: source.channels,
} & if valid { any } else { never }
if guide.width <= source.width {
error("The guide image must be larger than the source image.")
} else if bool::not(kScale) {
error("The size of the guide image must be an integer multiple of the size of the source image (e.g. 2x, 3x, 4x, ...).")
} else {
Image {
width: guide.width,
height: guide.height,
channels: source.channels,
}
}
"""
).with_never_reason(
"The guide image must be larger than the source image, and the size of the guide image must be an integer multiple of the size of the source image (e.g. 2x, 3x, 4x, ...)."
),
],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,18 @@ class StretchMode(Enum):
outputs=[
ImageOutput(
image_type="""
let valid: bool = match Input1 {
let minMaxRangeValid: bool = match Input1 {
StretchMode::Manual => Input4 < Input5,
_ => true,
};
if valid { Input0 } else { never }
if minMaxRangeValid {
Input0
} else {
error("Minimum must be less than Maximum.")
}
""",
).with_never_reason("Minimum must be less than the Maximum."),
),
],
)
def stretch_contrast_node(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,24 @@
outputs=[
ImageOutput(
image_type="""
let anyImages = bool::or(Input0 == Image, Input1 == Image, Input2 == Image, Input3 == Image);
def isImage(i: any) = match i { Image => true, _ => false };
let anyImages = bool::or(isImage(Input0), isImage(Input1), isImage(Input2), isImage(Input3));
def getWidth(i: any) = match i { Image => i.width, _ => Image.width };
def getHeight(i: any) = match i { Image => i.height, _ => Image.height };
if bool::not(anyImages) {
error("At least one channel must be an image.")
} else {
def getWidth(i: any) = match i { Image => i.width, _ => Image.width };
def getHeight(i: any) = match i { Image => i.height, _ => Image.height };
let valid = if anyImages { any } else { never };
valid & Image {
width: getWidth(Input0) & getWidth(Input1) & getWidth(Input2) & getWidth(Input3),
height: getHeight(Input0) & getHeight(Input1) & getHeight(Input2) & getHeight(Input3),
Image {
width: getWidth(Input0) & getWidth(Input1) & getWidth(Input2) & getWidth(Input3),
height: getHeight(Input0) & getHeight(Input1) & getHeight(Input2) & getHeight(Input3),
}
}
""",
channels=4,
assume_normalized=True,
).with_never_reason(
"All input channels must have the same size, and at least one input channel must be an image."
)
).with_never_reason("All input channels must have the same size.")
],
)
def combine_rgba_node(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,15 @@
let fg = Input2;
let bg = Input3;
let valid = bool::and(
fg > bg,
image.width == trimap.width,
image.height == trimap.height,
);
if valid {
Image { width: image.width, height: image.height }
if fg <= bg {
error("The foreground threshold must be greater than the background threshold.")
} else if bool::or(image.width != trimap.width, image.height != trimap.height) {
error("The image and trimap must have the same size.")
} else {
never
Image { width: image.width, height: image.height }
}
""",
channels=4,
).with_never_reason(
"The image and trimap must have the same size,"
" and the foreground threshold must be greater than the background threshold."
),
],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,24 @@
outputs=[
ImageOutput(
image_type="""
let anyImages = bool::or(Input0 == Image, Input1 == Image);
def isImage(i: any) = match i { Image => true, _ => false };
let anyImages = bool::or(isImage(Input0), isImage(Input1));
def getWidth(i: any) = match i { Image => i.width, _ => Image.width };
def getHeight(i: any) = match i { Image => i.height, _ => Image.height };
if bool::not(anyImages) {
error("At least one input must be an image.")
} else {
def getWidth(i: any) = match i { Image => i.width, _ => Image.width };
def getHeight(i: any) = match i { Image => i.height, _ => Image.height };
let valid = if anyImages { any } else { never };
valid & Image {
width: getWidth(Input0) & getWidth(Input1),
height: getHeight(Input0) & getHeight(Input1),
Image {
width: getWidth(Input0) & getWidth(Input1),
height: getHeight(Input0) & getHeight(Input1),
}
}
""",
channels=4,
assume_normalized=True,
).with_never_reason(
"RGB and Alpha must have the same size, and at least one must be an image."
)
).with_never_reason("RGB and Alpha must have the same size.")
],
)
def merge_transparency_node(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,21 @@ def quantize_image(image: np.ndarray, palette: np.ndarray):
outputs=[
ImageOutput(
image_type="""
let valid = bool::and(
Input0.width >= Input1.width,
number::mod(Input0.width, Input1.width) == 0,
Input0.height >= Input1.height,
number::mod(Input0.height, Input1.height) == 0,
Input0.channels == Input1.channels,
);
Image {
width: max(Input0.width, Input1.width),
height: max(Input0.height, Input1.height),
channels: Input0.channels,
} & if valid { any } else { never }""",
if Input0.channels != Input1.channels {
error("The target image and reference image must have the same number of channels.")
} else if bool::or(Input0.width < Input1.width, Input0.height < Input1.height) {
error("The target image must be larger than the reference image.")
} else if bool::or(number::mod(Input0.width, Input1.width) != 0, number::mod(Input0.height, Input1.height) != 0) {
error("The size of the target image must be an integer multiple of the size of the reference image (e.g. 2x, 3x, 4x, 8x).")
} else {
Image {
width: max(Input0.width, Input1.width),
height: max(Input0.height, Input1.height),
channels: Input0.channels,
}
}
""",
assume_normalized=True,
).with_never_reason(
"Target image must be larger than reference image in both dimensions, must have dimensions that are a multiple of each other, and must have the same number of channels."
)
],
)
Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
},
"dependencies": {
"@babel/plugin-transform-react-jsx": "^7.17.12",
"@chainner/navi": "^0.7.0",
"@chainner/navi": "^0.7.1",
"@chakra-ui/icons": "^2.1.1",
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.9.0",
Expand Down
8 changes: 2 additions & 6 deletions src/common/nodes/checkNodeValidity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,8 @@ export const checkNodeValidity = ({
}

// eslint-disable-next-line no-unreachable-loop
for (const { outputId } of functionInstance.outputErrors) {
const output = schema.outputs.find((o) => o.id === outputId)!;

return invalid(
`Some inputs are incompatible with each other. ${output.neverReason ?? ''}`
);
for (const { message } of functionInstance.outputErrors) {
return invalid(`Some inputs are incompatible with each other. ${message ?? ''}`);
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/common/types/chainner-scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import {
const code = `
struct null;
struct Error { message: string }
def error(message: invStrSet("")): Error {
Error { message: message }
}
struct Seed;
struct Directory { path: string }
Expand Down
Loading

0 comments on commit 4085610

Please sign in to comment.