Skip to content

VIX-3697 New Arch Prop#738

Open
da-markson wants to merge 15 commits intoVixenLights:feature/VIX-3693from
da-markson:VIX-3697
Open

VIX-3697 New Arch Prop#738
da-markson wants to merge 15 commits intoVixenLights:feature/VIX-3693from
da-markson:VIX-3697

Conversation

@da-markson
Copy link
Contributor

The code needs a complete review as this will be the basis for future props.

  1. Complete working version of the Arch prop wizard and Setup Display.
  2. Added the Dimming Curve into the Wizard
  3. Incorporated the new OpenGL preview

Other notes:

  1. The Tree prop is now broken. I will fix all the issues once the Arch prop coding and processes are reviewed and approved
  2. The Display Setup of the Arch prop is still using simple text boxes and the like. The custom implementation of the WPF grid is proving challenging to modify to support implementing more user friendly UI components. I'll work on this next.
  3. The CategoryOrder attribute is not working, so the current display ordering of the categories doesn't make sense. This too may be because of the customization of the WPF grid that I'll explore. The intended ordering should be Attributes, Rotation, Dimming Curve, Creation.
  4. I also need to add in Color handling into the Wizard. Once everything else is reviewed, I'll create a wizard page for that too.

@jeffu231 jeffu231 changed the base branch from master to feature/VIX-3693 November 29, 2025 16:24
Copy link
Contributor

@johncbaur johncbaur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should address high level comments in confluence before tackling the comments identified here.
https://vixenlights.atlassian.net/wiki/x/AwALDQ

Copy link
Contributor

@jeffu231 jeffu231 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if this is still WIP, but many of the comments have yet to be addressed with these last updates. The Extended.Wpf package needs to also have a replacement since its license prevents us from using it. If it is still WIP, then I will await further comment until you are ready.

@da-markson
Copy link
Contributor Author

da-markson commented Feb 16, 2026 via email

@da-markson
Copy link
Contributor Author

A lot of changes. Please review the Confluence page for details

@@ -0,0 +1,58 @@
namespace Vixen.Sys.Props.HelperTools
{
public class PropParameters : Dictionary<string, object>
Copy link
Contributor

@johncbaur johncbaur Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still trying to figure out the purpose for this helper class but if we continue forward with it, I think all usages of it should use the nameof() method on properties instead of string constants. This should allow developers to rename properties and this code will go along for the ride.

UPDATE: So I have reviewed more of the changes. I don't think we can use the nameof because the explicit properties have been removed and been replaced with the Dictionary. I think this PropParameters has some downsides:

  1. Using strings as keys to this Dictionary is inefficient when compared to directory property usage.
  2. It also makes it difficult to rename properties as we will have to search for the strings.
  3. I think it might be harder to read and inspect the code.
  4. Prevents using reflection to discover properties
  5. May complicate XML serialization if we go down that path
  6. Prevents being able to implement INotifyPropertyChange
  7. Prevents putting additional code in the setter of the property

}
else if (!VixenSystem.Props.IsUniquePropTitle(value))
{
MessageBox.Show($"{value} already exists. Enter a unique name.", "Prop name must be unique", MessageBoxButtons.OK, MessageBoxIcon.Error);
Copy link
Contributor

@johncbaur johncbaur Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My objection is with the MessageBox. Models should not directly spawn UI's. I think we should strive for clear separation of business rules and UI.
Not sure it reaching out to the database of props is a good idea.
This is just speculation but what if I created a temporary Prop for some use case and as soon as the name is set it pops up this message box when the prop was going to get destroyed?



private ObservableCollection<AxisRotationModel> _rotations;
public ObservableCollection<AxisRotationModel> Rotations
Copy link
Contributor

@johncbaur johncbaur Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rotations should not be unique to a LightProp.cs. I think this property was originally on the model at the base level. I sort of would like to do a deeper dive discussion with @jeffu231 on why we need both a model and a prop. Seems like if we combined these classes we might make our lives easier. Need to have a discussion before taking any action on this comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to move this class back to Vixen.Application ViewModels folder.

public static readonly IPropertyData IsSubNodeProperty = RegisterProperty<bool>(nameof(IsSubNode), false);
#endregion

private void OnIsTopNodeChanged()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method appears not be used.

}
public static readonly IPropertyData IsTopNodeProperty = RegisterProperty<bool>(nameof(IsTopNode), true);

public bool IsSubNode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If IsTopNode and IsSubNode are inverses of each other used for visibility there are several methods of handling that in the XAML with InverseBooleanToVisibilityConverter. Only change if this appeals to you.


#endregion

#region Temporary properties to get Change Prop to work
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a strange region name. What is meant by Temporary?

IPropFactory newPropFactory = PropFactory.CreateInstance(propType);

// Create a default Prop
(IProp newProp, IPropGroup propGroup) = newPropFactory.CreateBaseProp();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where I noticed that supporting groups got broken. The previous design was that the prop(s) were not created until the user had gone through Wizard to know how many props to create. Seems like to me we may need to hide the Group page when editing the prop after initial creation. We also probably need to look into allowing the user to just hit finish rather than forcing them to go through all the pages. I think Nick was asking for this feature as well.

internal void UpdatePreviewModel(IProp prop, bool force = false)
{
if (force == true)
DrawProp(prop?.PropModel);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want to have this force argument. Is there a strong need for it? If we continue to support a prop preview and a full setup preview, we have two OpenTK contexts running simultaneously. My testing showed that this could result in only one of the two actually drawing because the other one seemed to get confused.

I tried to document this on the DrawProp method.
///
/// This method needs to be called within the OpenTK control render method.
/// Otherwise the two OpenTK controls get confused and the prop preview won't render properly or consistently.
///


#endregion]

private Axis GetAxis(string axis)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method appears to be no longer used.

};
}

private string GetAxis(Axis axis)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method appears to be no longer used.

<ResourceDictionary Source="../Themes/Theme.xaml"/>
</ResourceDictionary.MergedDictionaries>

<BooleanToVisibilityConverter x:Key="BoolToVis"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This XAML file already has a BooleanToVisibilityConverter.


namespace VixenApplication.SetupDisplay.Wizards.Factory
{
public static class PropFactory
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this just be another method in the PropWizardFactory?
If not, best practice is that each class gets its own cs file.

NodeCount = 25;
Title = "Basic Attributes";
Description = $"Enter attributes for {PropType.Arch.GetEnumDescription()}";
NodeCountMinimum = 3;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want a NodeCountMaximum? I know all my arches are ~70 pixels, so 100 is not very large. You see videos of putting arches over sidewalks and driveways. I would imagine a driveway would be more than 100 pixels. Seems like the maximum could grow to something unreasonable like the size of short or int.
The legacy Arch smart object seems to let the count grow.

/// Gets or sets the NodeCount value.
/// Gets or sets the number of lights.
/// </summary>
/// <remarks>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the Wizard pages are effectively models. We shouldn't need to create Catel properties on the models.

/// <summary>
/// Gets or sets if the wizard will additionally generate a left and right prop.
/// </summary>
public bool LeftRight
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wizard pages are the model and I don't believe need to be Catel properties.

/// </summary>
/// <param name="nodePoints"></param>
/// <param name="angleInDegrees"></param>
protected static void RotateNodePoints(List<NodePoint> nodePoints, int angleInDegrees)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is only used by the Line model which really isn't in use anymore.
We should clean this up.

// (Optionally) rotate the points along the X, Y, and Z axis
RotatePoints(treePoints);
//ToDo : Replace null with rotation
RotatePoints(treePoints, null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rotations on the tree were working. Not sure why this code was changed to pass null.
Not sure why the argument was added.

private IEnumerable<NodePoint> Get3DNodePoints(float width, float height)
protected override IEnumerable<NodePoint> Get3DNodePoints()
{
float width = (float)PropParameters["Width"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure the prop models need a width and height.
The props were rendering with a normalized with and height of 1x1.

Suggest setting width and height local variables to 1.0f here.

protected BaseLightProp(string name, PropType propType) : base(name, propType)
{
StringType = StringTypes.Pixel;
Rotations = new ObservableCollection<AxisRotationModel>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably a duplicate comment but Rotations belong in BaseProp.

/// </summary>
/// <syntax>
/// <p>Function Definition Table syntax:</p>
/// <p>Function Definition Table syntax:<br>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this change intentional?
Look like we have a 'p' tag that is not closed.

LeftRight = false;

}
DimmingTypeOption = DimmingType.NoCurve;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't these Dimming options be initialized in the base class?

[PropertyOrder(11)]
public int NodeSize
private int _lightSize;
public int LightSize
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be moved to BaseLightProp ? Seems like all Light props are going to have a Light size.

catch (Exception ex)
{
Logging.Error(ex, $"An error occured handling Arch property {e.PropertyName} changed");
private void Arch_PropertyChanged(object? sender, PropertyChangedEventArgs e)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This event handler can probably be removed.



// Create Preview model
PropModel = new ArchModel();
Copy link
Contributor

@johncbaur johncbaur Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class looks like it was updated such that the Arch properties are disconnected from the PropModel properties. The properties on the Arch (prop) can be changed and they won't be reflected on the PropModel. Seems like that could cause problems and was not the original design.


public MountingPositionType MountingPosition { get; set; }

public ObservableCollection<AxisRotationModel> Rotations { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't have to define Rotations on every prop model. We should define it once in the base class.

Gamma = 2.2;
Curve = null;

ColorTypeOption = ColorType.SingleColor;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the color attributes get initialized in the base class BaseLightProp?

InitializeComponent();
}

private ColorType ColorTypeOption
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why this view has properties. Was this cloned from the DimmingWizardPageView? Standard MVVM you bind directly to the view model.

}
}

private void SingleColorButtonClick(object sender, RoutedEventArgs e)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally in MVVM you set up commands and the view model has boolean flags to determine which controls are visible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what this file is. Seems like it should not be in source control.

Library
}

public enum ColorType
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider using ColorProperty.cs - ElementColorType ?

SelectedItem="{Binding SelectedColorSet, Mode=TwoWay}">
<ComboBox.ItemsSource>
<x:Array Type="{x:Type sys:String}">
<sys:String>RGB</sys:String>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably create an enumeration for the Color Mixing order we support. I think EnumDescriptionTypeConverter can decorate the enum to provide strings in the WPF UIs.

}
private static readonly IPropertyData SingleColorProperty = RegisterProperty<Color>(nameof(SingleColorOption));

public ObservableCollection<string> ColorSetNames
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When doing color mixing (RGB) I think we should consider creating an enum for the color sets / color order we support. This probably means we need separate state for discrete color set names vs RGB, RGBW, color order. The former is entirely user defined. The RGB, RGBW options are determined at compile time and can be represented in an enumeration.

<RowDefinition Height="auto" />
</Grid.RowDefinitions>

<WindowsFormsHost x:Name="ZedGraphControlHost" Grid.Row="0" Grid.Column="0" Width="295" Height="295" Margin="0,3,0,0">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hesitate to make these comments given I imagine getting this curve stuff working on the Wizard page was probably a lot of work. I commend the effort. Ideally we would have a WPF user control that we drop on this Wizard page that could be reused.

The fact that it does not fit on the Wizard page as it stands is not ideal. The part that off the page could be moved to the right of the drawing area but I am not sure it is worth the effort or to have two versions of the Curve editor in the solution.

Given Jeff views curves as an Advanced feature used sparingly. The Wizard page could just have a button to launch the existing WinForms curve editor and we don't have to touch that code.

#endregion

#region Public Properties
public ColorType ColorTypeOption
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wizard pages are models and don't need to use Catel properties. I think there extra overhead with Catel properties that is not needed for these models and sets a bad example to copy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants