Eliminate GraphicElementData wrapper around GraphicElement

This commit is contained in:
Keavon Chambers 2023-12-08 17:18:45 -08:00
parent 5a6815dd91
commit 10f2fa92e5
10 changed files with 97 additions and 117 deletions

View file

@ -324,7 +324,7 @@ def update_layer(layer, indent, layer_node_id, next_id, opacity):
"has_primary_output": True,
"implementation": {
"Unresolved": {
"name": "graphene_core::ToGraphicElementData"
"name": "graphene_core::ToGraphicElementNode"
}
},
"metadata": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -206,7 +206,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
name: "To Graphic Element".to_string(),
inputs: vec![NodeInput::Network(generic!(T))],
implementation: DocumentNodeImplementation::proto("graphene_core::ToGraphicElementData"),
implementation: DocumentNodeImplementation::proto("graphene_core::ToGraphicElementNode"),
..Default::default()
},
),
@ -2113,7 +2113,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Text",
category: "Vector",
implementation: NodeImplementation::proto("graphene_core::text::TextGenerator<_, _, _>"),
implementation: NodeImplementation::proto("graphene_core::text::TextGeneratorNode<_, _, _>"),
inputs: vec![
DocumentInputType::none(),
DocumentInputType::value("Text", TaggedValue::String("hello world".to_string()), false),

View file

@ -22,7 +22,7 @@ use graphene_core::transform::{Footprint, Transform};
use graphene_core::vector::style::ViewMode;
use graphene_core::vector::VectorData;
use graphene_core::{Color, GraphicElementData, SurfaceFrame, SurfaceId};
use graphene_core::{Color, GraphicElement, SurfaceFrame, SurfaceId};
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
use interpreted_executor::dynamic_executor::DynamicExecutor;
@ -263,22 +263,22 @@ impl NodeRuntime {
continue;
};
let Some(io_data) = value.downcast_ref::<IORecord<Footprint, graphene_core::GraphicElementData>>() else {
let Some(io_data) = value.downcast_ref::<IORecord<Footprint, graphene_core::GraphicElement>>() else {
continue;
};
let graphic_element_data = &io_data.output;
let graphic_element = &io_data.output;
use graphene_core::renderer::*;
let bounds = graphic_element_data.bounding_box(DAffine2::IDENTITY);
let bounds = graphic_element.bounding_box(DAffine2::IDENTITY);
let render_params = RenderParams::new(ViewMode::Normal, ImageRenderMode::BlobUrl, bounds, true);
let mut render = SvgRender::new();
graphic_element_data.render_svg(&mut render, &render_params);
graphic_element.render_svg(&mut render, &render_params);
let [min, max] = bounds.unwrap_or_default();
render.format_svg(min, max);
let click_targets = self.click_targets.entry(node_id).or_default();
click_targets.clear();
// Add the graphic element data's click targets to the click targets vector
graphic_element_data.add_click_targets(click_targets);
graphic_element.add_click_targets(click_targets);
let old_thumbnail = self.thumbnails.entry(node_id).or_default();
if *old_thumbnail != render.svg {
@ -317,7 +317,7 @@ impl NodeRuntime {
let Some(transform) = try_downcast::<VectorData>(value.as_ref())
.or_else(|| try_downcast::<ImageFrame<Color>>(value.as_ref()))
.or_else(|| try_downcast::<GraphicElementData>(value.as_ref()))
.or_else(|| try_downcast::<GraphicElement>(value.as_ref()))
else {
warn!("Failed to downcast transform input");
continue;

View file

@ -34,7 +34,7 @@ impl core::hash::Hash for GraphicGroup {
/// Internal data for a [`GraphicElement`]. Can be [`VectorData`], [`ImageFrame`], text, or a nested [`GraphicGroup`]
#[derive(Clone, Debug, Hash, PartialEq, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum GraphicElementData {
pub enum GraphicElement {
VectorShape(Box<VectorData>),
ImageFrame(ImageFrame<Color>),
Text(String),
@ -42,18 +42,9 @@ pub enum GraphicElementData {
Artboard(Artboard),
}
// TODO: Remove this wrapper and directly use GraphicElementData
#[derive(Clone, Debug, PartialEq, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GraphicElement {
pub graphic_element_data: GraphicElementData,
}
impl Default for GraphicElement {
fn default() -> Self {
Self {
graphic_element_data: GraphicElementData::VectorShape(Box::new(VectorData::empty())),
}
Self::VectorShape(Box::new(VectorData::empty()))
}
}
@ -81,30 +72,28 @@ impl Artboard {
}
}
pub struct ConstructLayerNode<GraphicElementData, Stack> {
graphic_element_data: GraphicElementData,
pub struct ConstructLayerNode<GraphicElement, Stack> {
graphic_element: GraphicElement,
stack: Stack,
}
#[node_fn(ConstructLayerNode)]
async fn construct_layer<Data: Into<GraphicElementData>, Fut1: Future<Output = Data>, Fut2: Future<Output = GraphicGroup>>(
async fn construct_layer<Data: Into<GraphicElement>, Fut1: Future<Output = Data>, Fut2: Future<Output = GraphicGroup>>(
footprint: crate::transform::Footprint,
graphic_element_data: impl Node<crate::transform::Footprint, Output = Fut1>,
graphic_element: impl Node<crate::transform::Footprint, Output = Fut1>,
mut stack: impl Node<crate::transform::Footprint, Output = Fut2>,
) -> GraphicGroup {
let graphic_element_data = self.graphic_element_data.eval(footprint).await;
let graphic_element = self.graphic_element.eval(footprint).await;
let mut stack = self.stack.eval(footprint).await;
stack.push(GraphicElement {
graphic_element_data: graphic_element_data.into(),
});
stack.push(graphic_element.into());
stack
}
pub struct ToGraphicElementData {}
pub struct ToGraphicElementNode {}
#[node_fn(ToGraphicElementData)]
fn to_graphic_element_data<Data: Into<GraphicElementData>>(graphic_element_data: Data) -> GraphicElementData {
graphic_element_data.into()
#[node_fn(ToGraphicElementNode)]
fn to_graphic_element<Data: Into<GraphicElement>>(data: Data) -> GraphicElement {
data.into()
}
pub struct ConstructArtboardNode<Contents, Location, Dimensions, Background, Clip> {
@ -135,24 +124,24 @@ async fn construct_artboard<Fut: Future<Output = GraphicGroup>>(
}
}
impl From<ImageFrame<Color>> for GraphicElementData {
impl From<ImageFrame<Color>> for GraphicElement {
fn from(image_frame: ImageFrame<Color>) -> Self {
GraphicElementData::ImageFrame(image_frame)
GraphicElement::ImageFrame(image_frame)
}
}
impl From<VectorData> for GraphicElementData {
impl From<VectorData> for GraphicElement {
fn from(vector_data: VectorData) -> Self {
GraphicElementData::VectorShape(Box::new(vector_data))
GraphicElement::VectorShape(Box::new(vector_data))
}
}
impl From<GraphicGroup> for GraphicElementData {
impl From<GraphicGroup> for GraphicElement {
fn from(graphic_group: GraphicGroup) -> Self {
GraphicElementData::GraphicGroup(graphic_group)
GraphicElement::GraphicGroup(graphic_group)
}
}
impl From<Artboard> for GraphicElementData {
impl From<Artboard> for GraphicElement {
fn from(artboard: Artboard) -> Self {
GraphicElementData::Artboard(artboard)
GraphicElement::Artboard(artboard)
}
}
@ -171,7 +160,7 @@ impl DerefMut for GraphicGroup {
/// This is a helper trait used for the Into Implementation.
/// We can't just implement this for all for which from is implemented
/// as that would conflict with the implementation for `Self`
trait ToGraphicElement: Into<GraphicElementData> {}
trait ToGraphicElement: Into<GraphicElement> {}
impl ToGraphicElement for VectorData {}
impl ToGraphicElement for ImageFrame<Color> {}
@ -182,9 +171,8 @@ where
T: ToGraphicElement,
{
fn from(value: T) -> Self {
let element = GraphicElement { graphic_element_data: value.into() };
Self {
elements: (vec![element]),
elements: (vec![value.into()]),
opacity: 1.,
blend_mode: BlendMode::Normal,
transform: DAffine2::IDENTITY,
@ -225,8 +213,8 @@ impl GraphicElement {
usvg::Transform::from_row(cols[0] as f32, cols[1] as f32, cols[2] as f32, cols[3] as f32, cols[4] as f32, cols[5] as f32)
}
match &self.graphic_element_data {
GraphicElementData::VectorShape(vector_data) => {
match self {
GraphicElement::VectorShape(vector_data) => {
use usvg::tiny_skia_path::PathBuilder;
let mut builder = PathBuilder::new();
@ -257,7 +245,7 @@ impl GraphicElement {
path.stroke = Some(usvg::Stroke::default());
usvg::Node::new(usvg::NodeKind::Path(path))
}
GraphicElementData::ImageFrame(image_frame) => {
GraphicElement::ImageFrame(image_frame) => {
if image_frame.image.width * image_frame.image.height == 0 {
return usvg::Node::new(usvg::NodeKind::Group(usvg::Group::default()));
}
@ -274,7 +262,7 @@ impl GraphicElement {
kind: usvg::ImageKind::PNG(png.into()),
}))
}
GraphicElementData::Text(text) => usvg::Node::new(usvg::NodeKind::Text(usvg::Text {
GraphicElement::Text(text) => usvg::Node::new(usvg::NodeKind::Text(usvg::Text {
id: String::new(),
transform: usvg::Transform::identity(),
rendering_mode: usvg::TextRendering::OptimizeSpeed,
@ -290,7 +278,7 @@ impl GraphicElement {
text_flow: usvg::TextFlow::Linear,
}],
})),
GraphicElementData::GraphicGroup(group) => {
GraphicElement::GraphicGroup(group) => {
let group_element = usvg::Node::new(usvg::NodeKind::Group(usvg::Group::default()));
for element in group.iter() {
@ -299,13 +287,7 @@ impl GraphicElement {
group_element
}
// TODO
GraphicElementData::Artboard(_board) => usvg::Node::new(usvg::NodeKind::Group(usvg::Group::default())),
GraphicElement::Artboard(_board) => usvg::Node::new(usvg::NodeKind::Group(usvg::Group::default())),
}
}
}
impl core::hash::Hash for GraphicElement {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.graphic_element_data.hash(state);
}
}

View file

@ -1,6 +1,6 @@
use crate::raster::{BlendMode, Image, ImageFrame};
use crate::uuid::{generate_uuid, ManipulatorGroupId};
use crate::{vector::VectorData, Artboard, Color, GraphicElementData, GraphicGroup};
use crate::{vector::VectorData, Artboard, Color, GraphicElement, GraphicGroup};
use base64::Engine;
use bezier_rs::Subpath;
@ -233,16 +233,14 @@ impl GraphicElementRendered for GraphicGroup {
},
|render| {
for element in self.iter() {
element.graphic_element_data.render_svg(render, render_params);
element.render_svg(render, render_params);
}
},
);
}
fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
self.iter()
.filter_map(|element| element.graphic_element_data.bounding_box(transform * self.transform))
.reduce(Quad::combine_bounds)
self.iter().filter_map(|element| element.bounding_box(transform * self.transform)).reduce(Quad::combine_bounds)
}
fn add_click_targets(&self, _click_targets: &mut Vec<ClickTarget>) {}
@ -394,7 +392,7 @@ impl GraphicElementRendered for Artboard {
// Artboard contents
|render| {
for element in self.graphic_group.iter() {
element.graphic_element_data.render_svg(render, render_params);
element.render_svg(render, render_params);
}
},
);
@ -492,44 +490,44 @@ impl GraphicElementRendered for ImageFrame<Color> {
}
}
impl GraphicElementRendered for GraphicElementData {
impl GraphicElementRendered for GraphicElement {
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
match self {
GraphicElementData::VectorShape(vector_data) => vector_data.render_svg(render, render_params),
GraphicElementData::ImageFrame(image_frame) => image_frame.render_svg(render, render_params),
GraphicElementData::Text(_) => todo!("Render a text GraphicElementData"),
GraphicElementData::GraphicGroup(graphic_group) => graphic_group.render_svg(render, render_params),
GraphicElementData::Artboard(artboard) => artboard.render_svg(render, render_params),
GraphicElement::VectorShape(vector_data) => vector_data.render_svg(render, render_params),
GraphicElement::ImageFrame(image_frame) => image_frame.render_svg(render, render_params),
GraphicElement::Text(_) => todo!("Render a text GraphicElement"),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.render_svg(render, render_params),
GraphicElement::Artboard(artboard) => artboard.render_svg(render, render_params),
}
}
fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
match self {
GraphicElementData::VectorShape(vector_data) => GraphicElementRendered::bounding_box(&**vector_data, transform),
GraphicElementData::ImageFrame(image_frame) => image_frame.bounding_box(transform),
GraphicElementData::Text(_) => todo!("Bounds of a text GraphicElementData"),
GraphicElementData::GraphicGroup(graphic_group) => graphic_group.bounding_box(transform),
GraphicElementData::Artboard(artboard) => artboard.bounding_box(transform),
GraphicElement::VectorShape(vector_data) => GraphicElementRendered::bounding_box(&**vector_data, transform),
GraphicElement::ImageFrame(image_frame) => image_frame.bounding_box(transform),
GraphicElement::Text(_) => todo!("Bounds of a text GraphicElement"),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.bounding_box(transform),
GraphicElement::Artboard(artboard) => artboard.bounding_box(transform),
}
}
fn add_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
match self {
GraphicElementData::VectorShape(vector_data) => vector_data.add_click_targets(click_targets),
GraphicElementData::ImageFrame(image_frame) => image_frame.add_click_targets(click_targets),
GraphicElementData::Text(_) => todo!("click target for text GraphicElementData"),
GraphicElementData::GraphicGroup(graphic_group) => graphic_group.add_click_targets(click_targets),
GraphicElementData::Artboard(artboard) => artboard.add_click_targets(click_targets),
GraphicElement::VectorShape(vector_data) => vector_data.add_click_targets(click_targets),
GraphicElement::ImageFrame(image_frame) => image_frame.add_click_targets(click_targets),
GraphicElement::Text(_) => todo!("click target for text GraphicElement"),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.add_click_targets(click_targets),
GraphicElement::Artboard(artboard) => artboard.add_click_targets(click_targets),
}
}
fn to_usvg_node(&self) -> usvg::Node {
match self {
GraphicElementData::VectorShape(vector_data) => vector_data.to_usvg_node(),
GraphicElementData::ImageFrame(image_frame) => image_frame.to_usvg_node(),
GraphicElementData::Text(text) => text.to_usvg_node(),
GraphicElementData::GraphicGroup(graphic_group) => graphic_group.to_usvg_node(),
GraphicElementData::Artboard(artboard) => artboard.to_usvg_node(),
GraphicElement::VectorShape(vector_data) => vector_data.to_usvg_node(),
GraphicElement::ImageFrame(image_frame) => image_frame.to_usvg_node(),
GraphicElement::Text(text) => text.to_usvg_node(),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.to_usvg_node(),
GraphicElement::Artboard(artboard) => artboard.to_usvg_node(),
}
}
}

View file

@ -8,13 +8,13 @@ pub use to_path::*;
use crate::Node;
pub struct TextGenerator<Text, FontName, Size> {
pub struct TextGeneratorNode<Text, FontName, Size> {
text: Text,
font_name: FontName,
font_size: Size,
}
#[node_fn(TextGenerator)]
#[node_fn(TextGeneratorNode)]
fn generate_text<'a: 'input, T>(editor: EditorApi<'a, T>, text: String, font_name: Font, font_size: f64) -> crate::vector::VectorData {
let buzz_face = editor.font_cache.get(&font_name).map(|data| load_face(data));
crate::vector::VectorData::from_subpaths(to_path(&text, buzz_face, font_size, None))

View file

@ -10,7 +10,7 @@ use crate::raster::ImageFrame;
use crate::raster::Pixel;
use crate::vector::VectorData;
use crate::Artboard;
use crate::GraphicElementData;
use crate::GraphicElement;
use crate::GraphicGroup;
use crate::Node;
@ -70,43 +70,43 @@ impl TransformMut for GraphicGroup {
&mut self.transform
}
}
impl Transform for GraphicElementData {
impl Transform for GraphicElement {
fn transform(&self) -> DAffine2 {
match self {
GraphicElementData::VectorShape(vector_shape) => vector_shape.transform(),
GraphicElementData::ImageFrame(image_frame) => image_frame.transform(),
GraphicElementData::Text(_) => todo!("Transform of text"),
GraphicElementData::GraphicGroup(graphic_group) => graphic_group.transform(),
GraphicElementData::Artboard(artboard) => artboard.transform(),
GraphicElement::VectorShape(vector_shape) => vector_shape.transform(),
GraphicElement::ImageFrame(image_frame) => image_frame.transform(),
GraphicElement::Text(_) => todo!("Transform of text"),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.transform(),
GraphicElement::Artboard(artboard) => artboard.transform(),
}
}
fn local_pivot(&self, pivot: DVec2) -> DVec2 {
match self {
GraphicElementData::VectorShape(vector_shape) => vector_shape.local_pivot(pivot),
GraphicElementData::ImageFrame(image_frame) => image_frame.local_pivot(pivot),
GraphicElementData::Text(_) => todo!("Transform of text"),
GraphicElementData::GraphicGroup(graphic_group) => graphic_group.local_pivot(pivot),
GraphicElementData::Artboard(artboard) => artboard.local_pivot(pivot),
GraphicElement::VectorShape(vector_shape) => vector_shape.local_pivot(pivot),
GraphicElement::ImageFrame(image_frame) => image_frame.local_pivot(pivot),
GraphicElement::Text(_) => todo!("Transform of text"),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.local_pivot(pivot),
GraphicElement::Artboard(artboard) => artboard.local_pivot(pivot),
}
}
fn decompose_scale(&self) -> DVec2 {
match self {
GraphicElementData::VectorShape(vector_shape) => vector_shape.decompose_scale(),
GraphicElementData::ImageFrame(image_frame) => image_frame.decompose_scale(),
GraphicElementData::Text(_) => todo!("Transform of text"),
GraphicElementData::GraphicGroup(graphic_group) => graphic_group.decompose_scale(),
GraphicElementData::Artboard(artboard) => artboard.decompose_scale(),
GraphicElement::VectorShape(vector_shape) => vector_shape.decompose_scale(),
GraphicElement::ImageFrame(image_frame) => image_frame.decompose_scale(),
GraphicElement::Text(_) => todo!("Transform of text"),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.decompose_scale(),
GraphicElement::Artboard(artboard) => artboard.decompose_scale(),
}
}
}
impl TransformMut for GraphicElementData {
impl TransformMut for GraphicElement {
fn transform_mut(&mut self) -> &mut DAffine2 {
match self {
GraphicElementData::VectorShape(vector_shape) => vector_shape.transform_mut(),
GraphicElementData::ImageFrame(image_frame) => image_frame.transform_mut(),
GraphicElementData::Text(_) => todo!("Transform of text"),
GraphicElementData::GraphicGroup(graphic_group) => graphic_group.transform_mut(),
GraphicElementData::Artboard(_) => todo!("Transform of artboard"),
GraphicElement::VectorShape(vector_shape) => vector_shape.transform_mut(),
GraphicElement::ImageFrame(image_frame) => image_frame.transform_mut(),
GraphicElement::Text(_) => todo!("Transform of text"),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.transform_mut(),
GraphicElement::Artboard(_) => todo!("Transform of artboard"),
}
}
}

View file

@ -331,7 +331,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: ImageFrame<Color>, fn_params: [Footprint => ImageFrame<Color>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: graphene_core::GraphicGroup, fn_params: [Footprint => graphene_core::GraphicGroup]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: graphene_core::GraphicElementData, fn_params: [Footprint => graphene_core::GraphicElementData]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: graphene_core::GraphicElement, fn_params: [Footprint => graphene_core::GraphicElement]),
async_node!(graphene_std::wasm_application_io::LoadResourceNode<_>, input: WasmEditorApi, output: Arc<[u8]>, params: [String]),
register_node!(graphene_std::wasm_application_io::DecodeImageNode, input: Arc<[u8]>, params: []),
async_node!(graphene_std::wasm_application_io::CreateSurfaceNode, input: WasmEditorApi, output: Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>, params: []),
@ -840,14 +840,14 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
input: Vec<graphene_core::vector::bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>,
params: [Vec<graphene_core::uuid::ManipulatorGroupId>]
),
register_node!(graphene_core::text::TextGenerator<_, _, _>, input: WasmEditorApi, params: [String, graphene_core::text::Font, f64]),
register_node!(graphene_core::text::TextGeneratorNode<_, _, _>, input: WasmEditorApi, params: [String, graphene_core::text::Font, f64]),
register_node!(graphene_std::brush::VectorPointsNode, input: VectorData, params: []),
register_node!(graphene_core::ExtractImageFrame, input: WasmEditorApi, params: []),
async_node!(graphene_core::ConstructLayerNode<_, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => graphene_core::GraphicElementData, Footprint => GraphicGroup]),
register_node!(graphene_core::ToGraphicElementData, input: graphene_core::vector::VectorData, params: []),
register_node!(graphene_core::ToGraphicElementData, input: ImageFrame<Color>, params: []),
register_node!(graphene_core::ToGraphicElementData, input: GraphicGroup, params: []),
register_node!(graphene_core::ToGraphicElementData, input: Artboard, params: []),
async_node!(graphene_core::ConstructLayerNode<_, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => graphene_core::GraphicElement, Footprint => GraphicGroup]),
register_node!(graphene_core::ToGraphicElementNode, input: graphene_core::vector::VectorData, params: []),
register_node!(graphene_core::ToGraphicElementNode, input: ImageFrame<Color>, params: []),
register_node!(graphene_core::ToGraphicElementNode, input: GraphicGroup, params: []),
register_node!(graphene_core::ToGraphicElementNode, input: Artboard, params: []),
async_node!(graphene_core::ConstructArtboardNode<_, _, _, _, _>, input: Footprint, output: Artboard, fn_params: [Footprint => GraphicGroup, () => glam::IVec2, () => glam::IVec2, () => Color, () => bool]),
];
let mut map: HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new();