start of Appearance function (#4743)
* initial commit Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix docs Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * add more samples Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updatres Signed-off-by: Jess Frazelle <github@jessfraz.com> * regenerate docs Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * patterns and appearance samples Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fmt Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
@ -22,3 +22,5 @@ once fixed in engine will just start working here with no language changes.
 | 
			
		||||
 | 
			
		||||
- **Chamfers**: Chamfers cannot intersect, you will get an error. Only simple
 | 
			
		||||
    chamfer cases work currently.
 | 
			
		||||
 | 
			
		||||
- **Appearance**: Changing the appearance on a loft does not work.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										210
									
								
								docs/kcl/appearance.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -19,6 +19,7 @@ layout: manual
 | 
			
		||||
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
 | 
			
		||||
* [`angledLineToX`](kcl/angledLineToX)
 | 
			
		||||
* [`angledLineToY`](kcl/angledLineToY)
 | 
			
		||||
* [`appearance`](kcl/appearance)
 | 
			
		||||
* [`arc`](kcl/arc)
 | 
			
		||||
* [`arcTo`](kcl/arcTo)
 | 
			
		||||
* [`asin`](kcl/asin)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2883
									
								
								docs/kcl/std.json
									
									
									
									
									
								
							
							
						
						
							
								
								
									
										23
									
								
								docs/kcl/types/AppearanceData.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,23 @@
 | 
			
		||||
---
 | 
			
		||||
title: "AppearanceData"
 | 
			
		||||
excerpt: "Data for appearance."
 | 
			
		||||
layout: manual
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Data for appearance.
 | 
			
		||||
 | 
			
		||||
**Type:** `object`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Properties
 | 
			
		||||
 | 
			
		||||
| Property | Type | Description | Required |
 | 
			
		||||
|----------|------|-------------|----------|
 | 
			
		||||
| `color` |`string`| Color of the new material, a hex string like "#ff0000". | No |
 | 
			
		||||
| `metalness` |`number` (**maximum:** 100.0)| Metalness of the new material, a percentage like 95.7. | No |
 | 
			
		||||
| `roughness` |`number` (**maximum:** 100.0)| Roughness of the new material, a percentage like 95.7. | No |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -68,7 +68,7 @@
 | 
			
		||||
    "yargs": "^17.7.2"
 | 
			
		||||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "vite",
 | 
			
		||||
    "start": "vite --port=3000 --host=0.0.0.0",
 | 
			
		||||
    "start:prod": "vite preview --port=3000",
 | 
			
		||||
    "serve": "vite serve --port=3000",
 | 
			
		||||
    "build": "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source \"$HOME/.cargo/env\" && curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -y && yarn build:wasm && vite build",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						@ -1721,7 +1721,9 @@ dependencies = [
 | 
			
		||||
 "parse-display 0.9.1",
 | 
			
		||||
 "pretty_assertions",
 | 
			
		||||
 "pyo3",
 | 
			
		||||
 "regex",
 | 
			
		||||
 "reqwest",
 | 
			
		||||
 "rgba_simple",
 | 
			
		||||
 "ropey",
 | 
			
		||||
 "schemars",
 | 
			
		||||
 "serde",
 | 
			
		||||
@ -2971,6 +2973,12 @@ dependencies = [
 | 
			
		||||
 "rand 0.8.5",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rgba_simple"
 | 
			
		||||
version = "0.10.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "e6cd655523701785087f69900df39892fb7b9b0721aa67682f571c38c32ac58a"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ring"
 | 
			
		||||
version = "0.17.8"
 | 
			
		||||
 | 
			
		||||
@ -40,10 +40,12 @@ miette = "7.2.0"
 | 
			
		||||
mime_guess = "2.0.5"
 | 
			
		||||
parse-display = "0.9.1"
 | 
			
		||||
pyo3 = { version = "0.22.6", optional = true }
 | 
			
		||||
regex = "1.11.1"
 | 
			
		||||
reqwest = { version = "0.12", default-features = false, features = [
 | 
			
		||||
    "stream",
 | 
			
		||||
    "rustls-tls",
 | 
			
		||||
] }
 | 
			
		||||
rgba_simple = "0.10.0"
 | 
			
		||||
ropey = "1.6.1"
 | 
			
		||||
schemars = { version = "0.8.17", features = [
 | 
			
		||||
    "impl_json_schema",
 | 
			
		||||
 | 
			
		||||
@ -489,6 +489,12 @@ fn get_autocomplete_snippet_from_schema(
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if prop_name == "color" {
 | 
			
		||||
                        fn_docs.push_str(&format!("\t{}: ${{{}:\"#ff0000\"}},\n", prop_name, i));
 | 
			
		||||
                        i += 1;
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if let Some((new_index, snippet)) = get_autocomplete_snippet_from_schema(prop, i)? {
 | 
			
		||||
                        fn_docs.push_str(&format!("\t{}: {},\n", prop_name, snippet));
 | 
			
		||||
                        i = new_index + 1;
 | 
			
		||||
@ -946,6 +952,21 @@ mod tests {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn get_autocomplete_snippet_appearance() {
 | 
			
		||||
        let appearance_fn: Box<dyn StdLibFn> = Box::new(crate::std::appearance::Appearance);
 | 
			
		||||
        let snippet = appearance_fn.to_autocomplete_snippet().unwrap();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            snippet,
 | 
			
		||||
            r#"appearance({
 | 
			
		||||
	color: ${0:"#
 | 
			
		||||
                .to_owned()
 | 
			
		||||
                + "\"#"
 | 
			
		||||
                + r#"ff0000"},
 | 
			
		||||
}, ${1:%})${}"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // We want to test the snippets we compile at lsp start.
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn get_all_stdlib_autocomplete_snippets() {
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ use std::{fmt, iter::Enumerate, num::NonZeroUsize};
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use parse_display::Display;
 | 
			
		||||
use tokeniser::Input;
 | 
			
		||||
use tower_lsp::lsp_types::SemanticTokenType;
 | 
			
		||||
use winnow::{
 | 
			
		||||
    self,
 | 
			
		||||
@ -17,7 +18,6 @@ use crate::{
 | 
			
		||||
    parsing::ast::types::{ItemVisibility, VariableKind},
 | 
			
		||||
    source_range::{ModuleId, SourceRange},
 | 
			
		||||
};
 | 
			
		||||
use tokeniser::Input;
 | 
			
		||||
 | 
			
		||||
mod tokeniser;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -10,13 +10,12 @@ use winnow::{
 | 
			
		||||
    Located, Stateful,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::TokenStream;
 | 
			
		||||
use crate::{
 | 
			
		||||
    parsing::token::{Token, TokenType},
 | 
			
		||||
    source_range::ModuleId,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::TokenStream;
 | 
			
		||||
 | 
			
		||||
lazy_static! {
 | 
			
		||||
    pub(crate) static ref RESERVED_WORDS: FnvHashMap<&'static str, TokenType> = {
 | 
			
		||||
        let mut set = FnvHashMap::default();
 | 
			
		||||
@ -365,9 +364,8 @@ fn keyword_type_or_word(i: &mut Input<'_>) -> PResult<Token> {
 | 
			
		||||
mod tests {
 | 
			
		||||
    use winnow::Located;
 | 
			
		||||
 | 
			
		||||
    use crate::parsing::token::TokenSlice;
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::parsing::token::TokenSlice;
 | 
			
		||||
    fn assert_parse_err<'i, P, O, E>(mut p: P, s: &'i str)
 | 
			
		||||
    where
 | 
			
		||||
        O: std::fmt::Debug,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										263
									
								
								src/wasm-lib/kcl/src/std/appearance.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,263 @@
 | 
			
		||||
//! Standard library appearance.
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use derive_docs::stdlib;
 | 
			
		||||
use kcmc::{each_cmd as mcmd, ModelingCmd};
 | 
			
		||||
use kittycad_modeling_cmds::{self as kcmc, shared::Color};
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
use rgba_simple::Hex;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use validator::Validate;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{ExecState, KclValue, Solid, SolidSet},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
lazy_static::lazy_static! {
 | 
			
		||||
    static ref HEX_REGEX: Regex = Regex::new(r"^#[0-9a-fA-F]{6}$").unwrap();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Data for appearance.
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Validate)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct AppearanceData {
 | 
			
		||||
    /// Color of the new material, a hex string like "#ff0000".
 | 
			
		||||
    #[schemars(regex(pattern = "#[0-9a-fA-F]{6}"))]
 | 
			
		||||
    pub color: String,
 | 
			
		||||
    /// Metalness of the new material, a percentage like 95.7.
 | 
			
		||||
    #[validate(range(min = 0.0, max = 100.0))]
 | 
			
		||||
    pub metalness: Option<f64>,
 | 
			
		||||
    /// Roughness of the new material, a percentage like 95.7.
 | 
			
		||||
    #[validate(range(min = 0.0, max = 100.0))]
 | 
			
		||||
    pub roughness: Option<f64>,
 | 
			
		||||
    // TODO(jess): we can also ambient occlusion here I just don't know what it is.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Set the appearance of a solid. This only works on solids, not sketches or individual paths.
 | 
			
		||||
pub async fn appearance(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (data, solid_set): (AppearanceData, SolidSet) = args.get_data_and_solid_set()?;
 | 
			
		||||
 | 
			
		||||
    // Validate the data.
 | 
			
		||||
    data.validate().map_err(|err| {
 | 
			
		||||
        KclError::Semantic(KclErrorDetails {
 | 
			
		||||
            message: format!("Invalid appearance data: {}", err),
 | 
			
		||||
            source_ranges: vec![args.source_range],
 | 
			
		||||
        })
 | 
			
		||||
    })?;
 | 
			
		||||
 | 
			
		||||
    // Make sure the color if set is valid.
 | 
			
		||||
    if !HEX_REGEX.is_match(&data.color) {
 | 
			
		||||
        return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
            message: format!("Invalid hex color (`{}`), try something like `#fff000`", data.color),
 | 
			
		||||
            source_ranges: vec![args.source_range],
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let result = inner_appearance(data, solid_set, args).await?;
 | 
			
		||||
    Ok(result.into())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Set the appearance of a solid. This only works on solids, not sketches or individual paths.
 | 
			
		||||
///
 | 
			
		||||
/// This will work on any solid, including extruded solids, revolved solids, and shelled solids.
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// /// Add color to an extruded solid.
 | 
			
		||||
/// exampleSketch = startSketchOn("XZ")
 | 
			
		||||
///   |> startProfileAt([0, 0], %)
 | 
			
		||||
///   |> lineTo([10, 0], %)
 | 
			
		||||
///   |> lineTo([0, 10], %)
 | 
			
		||||
///   |> lineTo([-10, 0], %)
 | 
			
		||||
///   |> close(%)
 | 
			
		||||
///
 | 
			
		||||
/// example = extrude(5, exampleSketch)
 | 
			
		||||
///  |> appearance({color= '#ff0000', metalness= 50, roughness= 50}, %)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// /// Add color to a revolved solid.
 | 
			
		||||
/// sketch001 = startSketchOn('XY')
 | 
			
		||||
///     |> circle({ center = [15, 0], radius = 5 }, %)
 | 
			
		||||
///     |> revolve({ angle = 360, axis = 'y' }, %)
 | 
			
		||||
///     |> appearance({
 | 
			
		||||
///         color = '#ff0000',
 | 
			
		||||
///         metalness = 90,
 | 
			
		||||
///         roughness = 90
 | 
			
		||||
///     }, %)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// /// Add color to different solids.
 | 
			
		||||
/// fn cube(center) {
 | 
			
		||||
///    return startSketchOn('XY')
 | 
			
		||||
///    |> startProfileAt([center[0] - 10, center[1] - 10], %)
 | 
			
		||||
///    |> lineTo([center[0] + 10, center[1] - 10], %)
 | 
			
		||||
///     |> lineTo([center[0] + 10, center[1] + 10], %)
 | 
			
		||||
///     |> lineTo([center[0] - 10, center[1] + 10], %)
 | 
			
		||||
///     |> close(%)
 | 
			
		||||
///    |> extrude(10, %)
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// example0 = cube([0, 0])
 | 
			
		||||
/// example1 = cube([20, 0])
 | 
			
		||||
/// example2 = cube([40, 0])
 | 
			
		||||
///
 | 
			
		||||
///  appearance({color= '#ff0000', metalness= 50, roughness= 50}, [example0, example1])
 | 
			
		||||
///  appearance({color= '#00ff00', metalness= 50, roughness= 50}, example2)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// /// You can set the appearance before or after you shell it will yield the same result.
 | 
			
		||||
/// /// This example shows setting the appearance _after_ the shell.
 | 
			
		||||
/// firstSketch = startSketchOn('XY')
 | 
			
		||||
///     |> startProfileAt([-12, 12], %)
 | 
			
		||||
///     |> line([24, 0], %)
 | 
			
		||||
///     |> line([0, -24], %)
 | 
			
		||||
///     |> line([-24, 0], %)
 | 
			
		||||
///     |> close(%)
 | 
			
		||||
///     |> extrude(6, %)
 | 
			
		||||
///
 | 
			
		||||
/// shell({
 | 
			
		||||
///     faces = ['end'],
 | 
			
		||||
///     thickness = 0.25,
 | 
			
		||||
/// }, firstSketch)
 | 
			
		||||
///     |> appearance({
 | 
			
		||||
///         color = '#ff0000',
 | 
			
		||||
///         metalness = 90,
 | 
			
		||||
///         roughness = 90
 | 
			
		||||
///     }, %)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// /// You can set the appearance before or after you shell it will yield the same result.
 | 
			
		||||
/// /// This example shows setting the appearance _before_ the shell.
 | 
			
		||||
/// firstSketch = startSketchOn('XY')
 | 
			
		||||
///     |> startProfileAt([-12, 12], %)
 | 
			
		||||
///     |> line([24, 0], %)
 | 
			
		||||
///     |> line([0, -24], %)
 | 
			
		||||
///     |> line([-24, 0], %)
 | 
			
		||||
///     |> close(%)
 | 
			
		||||
///     |> extrude(6, %)
 | 
			
		||||
///     |> appearance({
 | 
			
		||||
///         color = '#ff0000',
 | 
			
		||||
///         metalness = 90,
 | 
			
		||||
///         roughness = 90
 | 
			
		||||
///     }, %)
 | 
			
		||||
///
 | 
			
		||||
/// shell({
 | 
			
		||||
///     faces = ['end'],
 | 
			
		||||
///     thickness = 0.25,
 | 
			
		||||
/// }, firstSketch)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// /// Setting the appearance of a 3D pattern can be done _before_ or _after_ the pattern.
 | 
			
		||||
/// /// This example shows _before_ the pattern.
 | 
			
		||||
/// exampleSketch = startSketchOn('XZ')
 | 
			
		||||
///   |> startProfileAt([0, 0], %)
 | 
			
		||||
///   |> line([0, 2], %)
 | 
			
		||||
///   |> line([3, 1], %)
 | 
			
		||||
///   |> line([0, -4], %)
 | 
			
		||||
///   |> close(%)
 | 
			
		||||
///
 | 
			
		||||
/// example = extrude(1, exampleSketch)
 | 
			
		||||
///     |> appearance({
 | 
			
		||||
///         color = '#ff0000',
 | 
			
		||||
///         metalness = 90,
 | 
			
		||||
///         roughness = 90
 | 
			
		||||
///     }, %)
 | 
			
		||||
///   |> patternLinear3d({
 | 
			
		||||
///       axis = [1, 0, 1],
 | 
			
		||||
///       instances = 7,
 | 
			
		||||
///       distance = 6
 | 
			
		||||
///     }, %)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// /// Setting the appearance of a 3D pattern can be done _before_ or _after_ the pattern.
 | 
			
		||||
/// /// This example shows _after_ the pattern.
 | 
			
		||||
/// exampleSketch = startSketchOn('XZ')
 | 
			
		||||
///   |> startProfileAt([0, 0], %)
 | 
			
		||||
///   |> line([0, 2], %)
 | 
			
		||||
///   |> line([3, 1], %)
 | 
			
		||||
///   |> line([0, -4], %)
 | 
			
		||||
///   |> close(%)
 | 
			
		||||
///
 | 
			
		||||
/// example = extrude(1, exampleSketch)
 | 
			
		||||
///   |> patternLinear3d({
 | 
			
		||||
///       axis = [1, 0, 1],
 | 
			
		||||
///       instances = 7,
 | 
			
		||||
///       distance = 6
 | 
			
		||||
///     }, %)
 | 
			
		||||
///     |> appearance({
 | 
			
		||||
///         color = '#ff0000',
 | 
			
		||||
///         metalness = 90,
 | 
			
		||||
///         roughness = 90
 | 
			
		||||
///     }, %)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// /// Color the result of a 2D pattern that was extruded.
 | 
			
		||||
/// exampleSketch = startSketchOn('XZ')
 | 
			
		||||
///   |> startProfileAt([.5, 25], %)
 | 
			
		||||
///   |> line([0, 5], %)
 | 
			
		||||
///   |> line([-1, 0], %)
 | 
			
		||||
///   |> line([0, -5], %)
 | 
			
		||||
///   |> close(%)
 | 
			
		||||
///   |> patternCircular2d({
 | 
			
		||||
///        center = [0, 0],
 | 
			
		||||
///        instances = 13,
 | 
			
		||||
///        arcDegrees = 360,
 | 
			
		||||
///        rotateDuplicates = true
 | 
			
		||||
///      }, %)
 | 
			
		||||
///
 | 
			
		||||
/// example = extrude(1, exampleSketch)
 | 
			
		||||
///     |> appearance({
 | 
			
		||||
///         color = '#ff0000',
 | 
			
		||||
///         metalness = 90,
 | 
			
		||||
///         roughness = 90
 | 
			
		||||
///     }, %)
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "appearance",
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_appearance(data: AppearanceData, solid_set: SolidSet, args: Args) -> Result<SolidSet, KclError> {
 | 
			
		||||
    let solids: Vec<Box<Solid>> = solid_set.into();
 | 
			
		||||
 | 
			
		||||
    for solid in &solids {
 | 
			
		||||
        // Set the material properties.
 | 
			
		||||
        let rgb = rgba_simple::RGB::<f32>::from_hex(&data.color).map_err(|err| {
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: format!("Invalid hex color (`{}`): {}", data.color, err),
 | 
			
		||||
                source_ranges: vec![args.source_range],
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        let color = Color {
 | 
			
		||||
            r: rgb.red,
 | 
			
		||||
            g: rgb.green,
 | 
			
		||||
            b: rgb.blue,
 | 
			
		||||
            a: 100.0,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        args.batch_modeling_cmd(
 | 
			
		||||
            uuid::Uuid::new_v4(),
 | 
			
		||||
            ModelingCmd::from(mcmd::ObjectSetMaterialParamsPbr {
 | 
			
		||||
                object_id: solid.id,
 | 
			
		||||
                color,
 | 
			
		||||
                metalness: data.metalness.unwrap_or_default() as f32 / 100.0,
 | 
			
		||||
                roughness: data.roughness.unwrap_or_default() as f32 / 100.0,
 | 
			
		||||
                ambient_occlusion: 0.0,
 | 
			
		||||
            }),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        // Idk if we want to actually modify the memory for the colors, but I'm not right now since
 | 
			
		||||
        // I can't think of a use case for it.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(SolidSet::from(solids))
 | 
			
		||||
}
 | 
			
		||||
@ -1096,6 +1096,20 @@ impl<'a> FromKclValue<'a> for super::fillet::FilletData {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> FromKclValue<'a> for super::appearance::AppearanceData {
 | 
			
		||||
    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
 | 
			
		||||
        let obj = arg.as_object()?;
 | 
			
		||||
        let_field_of!(obj, color);
 | 
			
		||||
        let_field_of!(obj, metalness?);
 | 
			
		||||
        let_field_of!(obj, roughness?);
 | 
			
		||||
        Some(Self {
 | 
			
		||||
            color,
 | 
			
		||||
            metalness,
 | 
			
		||||
            roughness,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> FromKclValue<'a> for super::helix::HelixData {
 | 
			
		||||
    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
 | 
			
		||||
        let obj = arg.as_object()?;
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
//! Functions implemented for language execution.
 | 
			
		||||
 | 
			
		||||
pub mod appearance;
 | 
			
		||||
pub mod args;
 | 
			
		||||
pub mod array;
 | 
			
		||||
pub mod assert;
 | 
			
		||||
@ -50,6 +51,7 @@ lazy_static! {
 | 
			
		||||
        Box::new(LegLen),
 | 
			
		||||
        Box::new(LegAngX),
 | 
			
		||||
        Box::new(LegAngY),
 | 
			
		||||
        Box::new(crate::std::appearance::Appearance),
 | 
			
		||||
        Box::new(crate::std::convert::Int),
 | 
			
		||||
        Box::new(crate::std::extrude::Extrude),
 | 
			
		||||
        Box::new(crate::std::segment::SegEnd),
 | 
			
		||||
 | 
			
		||||
@ -141,9 +141,10 @@ impl<'tree> Visitable<'tree> for Node<'tree> {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use std::sync::Mutex;
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    macro_rules! kcl {
 | 
			
		||||
        ( $kcl:expr ) => {{
 | 
			
		||||
            $crate::parsing::top_level_parse($kcl).unwrap()
 | 
			
		||||
 | 
			
		||||
| 
		 After Width: | Height: | Size: 61 KiB  | 
| 
		 After Width: | Height: | Size: 110 KiB  | 
| 
		 After Width: | Height: | Size: 61 KiB  | 
| 
		 After Width: | Height: | Size: 62 KiB  | 
| 
		 After Width: | Height: | Size: 62 KiB  | 
| 
		 After Width: | Height: | Size: 41 KiB  | 
| 
		 After Width: | Height: | Size: 41 KiB  | 
| 
		 After Width: | Height: | Size: 40 KiB  |