mirror of https://codeberg.org/topola/topola.git
feat(topola-egui): Add way to access subprogress and subprogress bar
This will be useful in future commits when I will be improving reconfiguration triggers.
This commit is contained in:
parent
24ff7f0dd8
commit
0f3f96d4af
|
|
@ -43,15 +43,26 @@ impl StatusBar {
|
||||||
let linear_progress = activity.estimate_linear_progress();
|
let linear_progress = activity.estimate_linear_progress();
|
||||||
let value = linear_progress.value();
|
let value = linear_progress.value();
|
||||||
let maximum = linear_progress.maximum();
|
let maximum = linear_progress.maximum();
|
||||||
|
let ratio = *value as f32 / *maximum as f32;
|
||||||
|
|
||||||
ui.add(
|
ui.add(egui::ProgressBar::new(ratio).text(format!(
|
||||||
egui::ProgressBar::new((value / maximum) as f32).text(format!(
|
"{:.1}% ({:.1}/{:.1})",
|
||||||
"{:.1} ({:.1}/{:.1})",
|
ratio * 100.0,
|
||||||
value / maximum * 100.0,
|
value,
|
||||||
value,
|
maximum
|
||||||
maximum
|
)));
|
||||||
)),
|
|
||||||
);
|
let linear_subprogress = linear_progress.subscale();
|
||||||
|
let value = linear_subprogress.value();
|
||||||
|
let maximum = linear_subprogress.maximum();
|
||||||
|
let ratio = *value as f32 / *maximum as f32;
|
||||||
|
|
||||||
|
ui.add(egui::ProgressBar::new(ratio).text(format!(
|
||||||
|
"{:.1}% ({:.1}/{:.1})",
|
||||||
|
ratio * 100.0,
|
||||||
|
value,
|
||||||
|
maximum
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use crate::{
|
||||||
board::{edit::BoardEdit, AccessMesadata},
|
board::{edit::BoardEdit, AccessMesadata},
|
||||||
layout::via::ViaWeight,
|
layout::via::ViaWeight,
|
||||||
router::ng,
|
router::ng,
|
||||||
stepper::{Abort, EstimateLinearProgress, LinearProgress, Step},
|
stepper::{Abort, EstimateLinearProgress, LinearScale, Step},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
|
@ -172,21 +172,28 @@ impl<M: AccessMesadata + Clone> Abort<Invoker<M>> for ExecutionStepper<M> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since enum_dispatch does not really support generics, we implement this the
|
// Since enum_dispatch does not really support generics, we implement this the
|
||||||
// long way.
|
// long way by using `match`.
|
||||||
impl<M> EstimateLinearProgress for ExecutionStepper<M> {
|
impl<M> EstimateLinearProgress for ExecutionStepper<M> {
|
||||||
type Value = f64;
|
type Value = usize;
|
||||||
|
type Subscale = LinearScale<f64>;
|
||||||
|
|
||||||
fn estimate_linear_progress(&self) -> LinearProgress<f64> {
|
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
|
||||||
match self {
|
match self {
|
||||||
ExecutionStepper::MultilayerAutoroute(autoroute) => {
|
ExecutionStepper::MultilayerAutoroute(autoroute) => {
|
||||||
autoroute.estimate_linear_progress()
|
autoroute.estimate_linear_progress()
|
||||||
}
|
}
|
||||||
ExecutionStepper::PlanarAutoroute(autoroute) => autoroute.estimate_linear_progress(),
|
ExecutionStepper::PlanarAutoroute(autoroute) => autoroute.estimate_linear_progress(),
|
||||||
ExecutionStepper::TopoAutoroute(toporoute) => toporoute.estimate_linear_progress(),
|
ExecutionStepper::TopoAutoroute(..) => {
|
||||||
ExecutionStepper::PlaceVia(place_via) => place_via.estimate_linear_progress(),
|
LinearScale::new(0, 0, LinearScale::new(0.0, 0.0, ()))
|
||||||
ExecutionStepper::RemoveBands(remove_bands) => remove_bands.estimate_linear_progress(),
|
}
|
||||||
ExecutionStepper::MeasureLength(measure_length) => {
|
ExecutionStepper::PlaceVia(..) => {
|
||||||
measure_length.estimate_linear_progress()
|
LinearScale::new(0, 0, LinearScale::new(0.0, 0.0, ()))
|
||||||
|
}
|
||||||
|
ExecutionStepper::RemoveBands(..) => {
|
||||||
|
LinearScale::new(0, 0, LinearScale::new(0.0, 0.0, ()))
|
||||||
|
}
|
||||||
|
ExecutionStepper::MeasureLength(..) => {
|
||||||
|
LinearScale::new(0, 0, LinearScale::new(0.0, 0.0, ()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
board::AccessMesadata, geometry::shape::MeasureLength as MeasureLengthTrait, graph::MakeRef,
|
board::AccessMesadata, geometry::shape::MeasureLength as MeasureLengthTrait, graph::MakeRef,
|
||||||
stepper::EstimateLinearProgress,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{invoker::GetDebugOverlayData, selection::BandSelection, Autorouter, AutorouterError};
|
use super::{invoker::GetDebugOverlayData, selection::BandSelection, Autorouter, AutorouterError};
|
||||||
|
|
@ -48,7 +47,4 @@ impl MeasureLengthExecutionStepper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EstimateLinearProgress for MeasureLengthExecutionStepper {
|
|
||||||
type Value = f64;
|
|
||||||
}
|
|
||||||
impl GetDebugOverlayData for MeasureLengthExecutionStepper {}
|
impl GetDebugOverlayData for MeasureLengthExecutionStepper {}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ use crate::{
|
||||||
geometry::{edit::Edit, primitive::PrimitiveShape},
|
geometry::{edit::Edit, primitive::PrimitiveShape},
|
||||||
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
|
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
|
||||||
stepper::{
|
stepper::{
|
||||||
Abort, EstimateLinearProgress, LinearProgress, ReconfiguratorStatus, Reconfigure, Step,
|
Abort, EstimateLinearProgress, LinearScale, ReconfiguratorStatus, Reconfigure, Step,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -120,9 +120,10 @@ impl<M: AccessMesadata> Reconfigure<Autorouter<M>> for MultilayerAutorouteExecut
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EstimateLinearProgress for MultilayerAutorouteExecutionStepper {
|
impl EstimateLinearProgress for MultilayerAutorouteExecutionStepper {
|
||||||
type Value = f64;
|
type Value = usize;
|
||||||
|
type Subscale = LinearScale<f64>;
|
||||||
|
|
||||||
fn estimate_linear_progress(&self) -> LinearProgress<f64> {
|
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
|
||||||
self.planar.estimate_linear_progress()
|
self.planar.estimate_linear_progress()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ use crate::{
|
||||||
geometry::primitive::PrimitiveShape,
|
geometry::primitive::PrimitiveShape,
|
||||||
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
|
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
|
||||||
stepper::{
|
stepper::{
|
||||||
Abort, EstimateLinearProgress, LinearProgress, ReconfiguratorStatus, Reconfigure, Step,
|
Abort, EstimateLinearProgress, LinearScale, ReconfiguratorStatus, Reconfigure, Step,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -142,9 +142,10 @@ impl<M: AccessMesadata> Abort<Autorouter<M>> for MultilayerAutorouteReconfigurat
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EstimateLinearProgress for MultilayerAutorouteReconfigurator {
|
impl EstimateLinearProgress for MultilayerAutorouteReconfigurator {
|
||||||
type Value = f64;
|
type Value = usize;
|
||||||
|
type Subscale = LinearScale<f64>;
|
||||||
|
|
||||||
fn estimate_linear_progress(&self) -> LinearProgress<f64> {
|
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
|
||||||
self.stepper.estimate_linear_progress()
|
self.stepper.estimate_linear_progress()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ use crate::{
|
||||||
AccessMesadata,
|
AccessMesadata,
|
||||||
},
|
},
|
||||||
layout::{via::ViaWeight, LayoutEdit},
|
layout::{via::ViaWeight, LayoutEdit},
|
||||||
stepper::EstimateLinearProgress,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{invoker::GetDebugOverlayData, Autorouter, AutorouterError};
|
use super::{invoker::GetDebugOverlayData, Autorouter, AutorouterError};
|
||||||
|
|
@ -53,7 +52,4 @@ impl PlaceViaExecutionStepper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EstimateLinearProgress for PlaceViaExecutionStepper {
|
|
||||||
type Value = f64;
|
|
||||||
}
|
|
||||||
impl GetDebugOverlayData for PlaceViaExecutionStepper {}
|
impl GetDebugOverlayData for PlaceViaExecutionStepper {}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use crate::{
|
||||||
router::{
|
router::{
|
||||||
navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper, RouteStepper, Router,
|
navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper, RouteStepper, Router,
|
||||||
},
|
},
|
||||||
stepper::{Abort, EstimateLinearProgress, LinearProgress, Reconfigure, Step},
|
stepper::{Abort, EstimateLinearProgress, LinearScale, Reconfigure, Step},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
|
@ -304,16 +304,22 @@ impl<M: AccessMesadata> Reconfigure<Autorouter<M>> for PlanarAutorouteExecutionS
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EstimateLinearProgress for PlanarAutorouteExecutionStepper {
|
impl EstimateLinearProgress for PlanarAutorouteExecutionStepper {
|
||||||
type Value = f64;
|
type Value = usize;
|
||||||
|
type Subscale = LinearScale<f64>;
|
||||||
|
|
||||||
fn estimate_linear_progress(&self) -> LinearProgress<f64> {
|
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
|
||||||
LinearProgress::new(
|
LinearScale::new(
|
||||||
self.curr_ratline_index as f64
|
self.curr_ratline_index,
|
||||||
+ self.route.as_ref().map_or(0.0, |route| {
|
self.configuration().ratlines.len(),
|
||||||
route.estimate_linear_progress().value()
|
LinearScale::new(
|
||||||
/ route.estimate_linear_progress().maximum()
|
self.route
|
||||||
}),
|
.as_ref()
|
||||||
self.configuration().ratlines.len() as f64,
|
.map_or(0.0, |route| *route.estimate_linear_progress().value()),
|
||||||
|
self.route
|
||||||
|
.as_ref()
|
||||||
|
.map_or(0.0, |route| *route.estimate_linear_progress().maximum()),
|
||||||
|
(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ use crate::{
|
||||||
geometry::primitive::PrimitiveShape,
|
geometry::primitive::PrimitiveShape,
|
||||||
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
|
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
|
||||||
stepper::{
|
stepper::{
|
||||||
Abort, EstimateLinearProgress, LinearProgress, ReconfiguratorStatus, Reconfigure, Step,
|
Abort, EstimateLinearProgress, LinearScale, ReconfiguratorStatus, Reconfigure, Step,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -121,9 +121,10 @@ impl<M: AccessMesadata> Abort<Autorouter<M>> for PlanarAutorouteReconfigurator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EstimateLinearProgress for PlanarAutorouteReconfigurator {
|
impl EstimateLinearProgress for PlanarAutorouteReconfigurator {
|
||||||
type Value = f64;
|
type Value = usize;
|
||||||
|
type Subscale = LinearScale<f64>;
|
||||||
|
|
||||||
fn estimate_linear_progress(&self) -> LinearProgress<f64> {
|
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
|
||||||
self.stepper.estimate_linear_progress()
|
self.stepper.estimate_linear_progress()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,7 @@
|
||||||
|
|
||||||
//! Provides functionality to remove bands from the layout.
|
//! Provides functionality to remove bands from the layout.
|
||||||
|
|
||||||
use crate::{
|
use crate::board::{edit::BoardEdit, AccessMesadata};
|
||||||
board::{edit::BoardEdit, AccessMesadata},
|
|
||||||
stepper::EstimateLinearProgress,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{invoker::GetDebugOverlayData, selection::BandSelection, Autorouter, AutorouterError};
|
use super::{invoker::GetDebugOverlayData, selection::BandSelection, Autorouter, AutorouterError};
|
||||||
|
|
||||||
|
|
@ -48,7 +45,4 @@ impl RemoveBandsExecutionStepper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EstimateLinearProgress for RemoveBandsExecutionStepper {
|
|
||||||
type Value = f64;
|
|
||||||
}
|
|
||||||
impl GetDebugOverlayData for RemoveBandsExecutionStepper {}
|
impl GetDebugOverlayData for RemoveBandsExecutionStepper {}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ use crate::{
|
||||||
ng,
|
ng,
|
||||||
thetastar::ThetastarStepper,
|
thetastar::ThetastarStepper,
|
||||||
},
|
},
|
||||||
stepper::{Abort, EstimateLinearProgress, LinearProgress, OnEvent, Step},
|
stepper::{Abort, EstimateLinearProgress, LinearScale, OnEvent, Step},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Stores the interactive input data from the user.
|
/// Stores the interactive input data from the user.
|
||||||
|
|
@ -101,11 +101,14 @@ impl<M: AccessMesadata + Clone> Abort<Invoker<M>> for ActivityStepper<M> {
|
||||||
// Since enum_dispatch does not really support generics, we implement this the
|
// Since enum_dispatch does not really support generics, we implement this the
|
||||||
// long way.
|
// long way.
|
||||||
impl<M> EstimateLinearProgress for ActivityStepper<M> {
|
impl<M> EstimateLinearProgress for ActivityStepper<M> {
|
||||||
type Value = f64;
|
type Value = usize;
|
||||||
|
type Subscale = LinearScale<f64>;
|
||||||
|
|
||||||
fn estimate_linear_progress(&self) -> LinearProgress<f64> {
|
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
|
||||||
match self {
|
match self {
|
||||||
ActivityStepper::Interaction(..) => LinearProgress::new(0.0, 0.0),
|
ActivityStepper::Interaction(..) => {
|
||||||
|
LinearScale::new(0, 0, LinearScale::new(0.0, 0.0, ()))
|
||||||
|
}
|
||||||
ActivityStepper::Execution(execution) => execution.estimate_linear_progress(),
|
ActivityStepper::Execution(execution) => execution.estimate_linear_progress(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -193,9 +196,10 @@ impl<M: AccessMesadata + Clone> OnEvent<ActivityContext<'_, M>, InteractiveEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M> EstimateLinearProgress for ActivityStepperWithStatus<M> {
|
impl<M> EstimateLinearProgress for ActivityStepperWithStatus<M> {
|
||||||
type Value = f64;
|
type Value = usize;
|
||||||
|
type Subscale = LinearScale<f64>;
|
||||||
|
|
||||||
fn estimate_linear_progress(&self) -> LinearProgress<f64> {
|
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
|
||||||
self.activity.estimate_linear_progress()
|
self.activity.estimate_linear_progress()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -233,10 +233,6 @@ impl<R: AccessRules + Clone + std::panic::RefUnwindSafe> AutorouteExecutionStepp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M> EstimateLinearProgress for AutorouteExecutionStepper<M> {
|
|
||||||
type Value = f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M> GetDebugOverlayData for AutorouteExecutionStepper<M> {
|
impl<M> GetDebugOverlayData for AutorouteExecutionStepper<M> {
|
||||||
fn maybe_topo_navmesh(&self) -> Option<pie::navmesh::NavmeshRef<'_, super::PieNavmeshBase>> {
|
fn maybe_topo_navmesh(&self) -> Option<pie::navmesh::NavmeshRef<'_, super::PieNavmeshBase>> {
|
||||||
Some(pie::navmesh::NavmeshRef {
|
Some(pie::navmesh::NavmeshRef {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ use crate::{
|
||||||
thetastar::{ThetastarError, ThetastarStepper},
|
thetastar::{ThetastarError, ThetastarStepper},
|
||||||
Router, RouterThetastarStrategy,
|
Router, RouterThetastarStrategy,
|
||||||
},
|
},
|
||||||
stepper::{EstimateLinearProgress, LinearProgress, Step},
|
stepper::{EstimateLinearProgress, LinearScale, Step},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Getters, Dissolve)]
|
#[derive(Getters, Dissolve)]
|
||||||
|
|
@ -101,8 +101,9 @@ impl<R: AccessRules> Step<Router<'_, R>, BandTermsegIndex> for RouteStepper {
|
||||||
|
|
||||||
impl EstimateLinearProgress for RouteStepper {
|
impl EstimateLinearProgress for RouteStepper {
|
||||||
type Value = f64;
|
type Value = f64;
|
||||||
|
type Subscale = ();
|
||||||
|
|
||||||
fn estimate_linear_progress(&self) -> LinearProgress<f64> {
|
fn estimate_linear_progress(&self) -> LinearScale<f64> {
|
||||||
self.thetastar.estimate_linear_progress()
|
self.thetastar.estimate_linear_progress()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ use thiserror::Error;
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use crate::stepper::{EstimateLinearProgress, LinearProgress, Step};
|
use crate::stepper::{EstimateLinearProgress, LinearScale, Step};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct MinScored<K, T>(pub K, pub T);
|
pub struct MinScored<K, T>(pub K, pub T);
|
||||||
|
|
@ -451,8 +451,13 @@ where
|
||||||
K: Measure + Copy + Sub<Output = K>,
|
K: Measure + Copy + Sub<Output = K>,
|
||||||
{
|
{
|
||||||
type Value = K;
|
type Value = K;
|
||||||
|
type Subscale = ();
|
||||||
|
|
||||||
fn estimate_linear_progress(&self) -> LinearProgress<Self::Value> {
|
fn estimate_linear_progress(&self) -> LinearScale<Self::Value> {
|
||||||
LinearProgress::new(self.progress_estimate_value, self.progress_estimate_maximum)
|
LinearScale::new(
|
||||||
|
self.progress_estimate_value,
|
||||||
|
self.progress_estimate_maximum,
|
||||||
|
(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,22 +84,36 @@ pub trait OnEvent<Ctx, Event> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Getters)]
|
#[derive(Clone, Copy, Debug, Getters)]
|
||||||
pub struct LinearProgress<V> {
|
pub struct LinearScale<V, S = ()> {
|
||||||
value: V,
|
value: V,
|
||||||
maximum: V,
|
maximum: V,
|
||||||
|
subscale: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> LinearProgress<V> {
|
impl<V, S> LinearScale<V, S> {
|
||||||
pub fn new(value: V, maximum: V) -> Self {
|
pub fn new(value: V, maximum: V, subscale: S) -> Self {
|
||||||
Self { value, maximum }
|
Self {
|
||||||
|
value,
|
||||||
|
maximum,
|
||||||
|
subscale,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: Default> Default for LinearScale<V> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
value: V::default(),
|
||||||
|
maximum: V::default(),
|
||||||
|
subscale: (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Some steppers report estimates of how far they are from completion.
|
/// Some steppers report estimates of how far they are from completion.
|
||||||
pub trait EstimateLinearProgress {
|
pub trait EstimateLinearProgress {
|
||||||
type Value: Default;
|
type Value;
|
||||||
|
type Subscale;
|
||||||
|
|
||||||
fn estimate_linear_progress(&self) -> LinearProgress<Self::Value> {
|
fn estimate_linear_progress(&self) -> LinearScale<Self::Value, Self::Subscale>;
|
||||||
LinearProgress::new(Self::Value::default(), Self::Value::default())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue