Skip to content

Stud logo preference#101

Open
trevorsandy wants to merge 7 commits intotcobbs:masterfrom
trevorsandy:STUD_LOGO
Open

Stud logo preference#101
trevorsandy wants to merge 7 commits intotcobbs:masterfrom
trevorsandy:STUD_LOGO

Conversation

@trevorsandy
Copy link
Contributor

@trevorsandy trevorsandy commented Oct 30, 2025

Enable the ability to select stud logo preference using the following stud logo geometry:

0 Plain
1 Single Wire
2 Double Wire
3 Raised Flat
4 Raised Rounded
5 Subtle Rounded

Screenshot 2025-10-30 201136

Only the Windows GUI is implemented; however from other platforms (i.e. macOS/Linux), you can set the the following options using [Sessions] in your .ini file.

[Sessions]
LDView Preset - Quality with Edges/UseStudLogo=1
LDView Preset - Quality with Edges/StudLogo=2

This feature is a subset of the comprehensive behaviour which also includes high-contrast studs and edge lines.

HighContrastSamples

With the addition of high-contrast studs and automatic edge line colors, the stud style ini file options have changed to:

[Sessions]
LDView Preset - Quality with Edges/AutomateEdgeColor=1
LDView Preset - Quality with Edges/UseStudStyle=1
LDView Preset - Quality with Edges/StudStyle=2

And stud geometry options are extended to:

0 Plain
1 Single Wire
2 Double Wire
3 Raised Flat
4 Raised Rounded
5 Subtle Rounded
6 High Contrast Plain
7 High Contrast Single Wire

Cheers,

@tcobbs
Copy link
Owner

tcobbs commented Oct 30, 2025

Did you use LDView's OBI code for the high-contrast work? (I'm guessing not, since it requires modified parts.) The OBI code was added years ago, but never became mainstream due to the requirement of having updated parts.

@trevorsandy
Copy link
Contributor Author

trevorsandy commented Oct 30, 2025

Did you use LDView's OBI code for the high-contrast work?

I did not. The high-contrast behaviour simply extends the primitives in TCStudLogoPrimitive StudLogoPrimitives and adds the ability to manipulate the stud cylinder and edge line colour both manually and automatically. The resulting behaviour adds an 'automate edge line color' checkbox to the Geometry tab and two new options to the Primitives tab's 'use stud logo geometry' combo control:

HighContrast =6 High Contrast Plain
HighContrastSingleWire =7 High Contrast Single Wire

Cheers,

@trevorsandy
Copy link
Contributor Author

I've added the full feature set which includes preference options to manually configure high-contrast studs and edge lines along with the preference to automatically adjust edge line color. The automate edge line color preference is set under the Geometry tab while use stud geometry is under Primitives.

Screenshot 2025-11-08 231244

Example model file.

Details
0 FILE StudStyles.ldr
0 Stud Styles
0 Name: studStyles.ldr
0 Author: LPub3D
0 !LICENSE Not redistributable : see NonCAreadme.txt
1 4 0 0 0 1 0 0 0 1 0 0 0 1 3001.dat
1 4 -40 24 0 1 0 0 0 1 0 0 0 1 3001.dat
1 4 40 24 0 1 0 0 0 1 0 0 0 1 3001.dat
0 ROTSTEP END
1 4 80 0 0 1 0 0 0 1 0 0 0 1 3001.dat
1 4 120 24 0 1 0 0 0 1 0 0 0 1 3001.dat
1 15 40 -24 0 1 0 0 0 1 0 0 0 1 3001.dat
0 STEP
1 1 -20 -8 0 1 0 0 0 1 0 0 0 1 subModel-1.ldr
0 STEP
1 15 -60 16 0 1 0 0 0 1 0 0 0 1 subModel-2.ldr
0 ROTSTEP 0 270 0 REL
1 0 100 -8 0 1 0 0 0 1 0 0 0 1 subModel-2.ldr
0 ROTSTEP 0 180 0 REL
1 1 140 16 0 1 0 0 0 1 0 0 0 1 subModel-1.ldr
1 15 40 -64 10 1 0 0 0 1 0 0 0 1 67329.dat
1 71 30 -88 10 1 0 0 0 1 0 0 0 1 15411.dat
1 288 50 -88 10 1 0 0 0 1 0 0 0 1 4070.dat
0 ROTSTEP 0 90 0 REL
0 NOFILE
0 FILE submodel-1.ldr
0 
0 Name: subModel-1.ldr
0 Author: LPub3D
0 !LICENSE Not redistributable : see NonCAreadme.txt
1 1 0 0 0 1 0 0 0 1 0 0 0 1 87580.dat
0 ROTSTEP END
1 14 0 -24 0 1 0 0 0 1 0 0 0 1 3062b.dat
0 STEP
0 NOFILE
0 FILE submodel-2.ldr
0 
0 Name: subModel-2.ldr
0 Author: LPub3D
0 !LICENSE Not redistributable : see NonCAreadme.txt
1 0 0 0 0 1 0 0 0 1 0 0 0 1 87580.dat
0 ROTSTEP END
1 25 0 -24 0 1 0 0 0 1 0 0 0 1 3062a.dat
0 STEP
0 NOFILE

Cheers,

@tcobbs
Copy link
Owner

tcobbs commented Jan 15, 2026

@pbartfai Do we want to update this PR to support macOS and Qt before releasing 4.7 or after? (Qt for you, macOS for me.)

@pbartfai
Copy link
Collaborator

@pbartfai Do we want to update this PR to support macOS and Qt before releasing 4.7 or after? (Qt for you, macOS for me.)

Let's release 4.7 first.

@pbartfai
Copy link
Collaborator

@pbartfai Do we want to update this PR to support macOS and Qt before releasing 4.7 or after? (Qt for you, macOS for me.)

Let's release 4.7 first.

@tcobbs 4.7 has been released
I've prepared the Qt UI...

@tcobbs
Copy link
Owner

tcobbs commented Mar 18, 2026 via email

@tcobbs
Copy link
Owner

tcobbs commented Mar 18, 2026

@trevorsandy Sorry for the delay. I am finally ready to review this and hopefully merge, but I have hit some possible problems. I'm guessing that this PR includes all of the local changes that you have made to LDView for LPub3d, because there is a bunch of stuff in here that is totally unrelated to the stud logo settings the that PR title makes this seem to be. (You do state in the description that this includes high-contrast studs and edge lines, and maybe that's all that's here in addition to the stud logos.)

While I don't have a problem in principle of incorporating all of these changes, I would greatly prefer if they didn't all show up in one single PR. (Having said that, I am not saying that separate PRs is an absolute requirement.)

How hard would it be to narrow down this PR to just the stud logo changes, and then create one or two other PRs for the other changes? I promise that I will deal with them in a timely manner.

Finally, can you confirm that this PR only contains the following:

  1. Stud logo options
  2. High-contrast studs
  3. High-contrast edge lines
  4. Windows-only preferences UI changes for the above

@tcobbs
Copy link
Owner

tcobbs commented Mar 18, 2026 via email

@pbartfai
Copy link
Collaborator

Great. I'll do the macOS one. Do you know what the best way is to handle this? I think it's probably easiest to just merge Trevor's Windows-only PR to our master and then create new PRs for Qt and macOS.

The easiest would be merging this PR first then update Mac and Qt code.

@trevorsandy
Copy link
Contributor Author

trevorsandy commented Mar 19, 2026

Hi Travis, I can confirm that this PR only contains the following:

Stud logo options
High-contrast studs
High-contrast edge lines
Windows-only preferences UI changes for the above

I was deliberate to remove content that only implicated LPub3D such as the ability to suppress high-contrast colour management for 'fade previous steps' and 'highlight current step' behaviour.

I'm guessing that this PR includes all of the local changes that you have made to LDView for LPub3d, because there is a bunch of stuff in here that is totally unrelated to the stud logo settings the that PR title makes this seem to be.

There is no code in this PR that is not necessary to implement the behaviour outlined above. If you remain convinced otherwise, I will be pleased to clarify any suspicious code you care to specifically reference.

How hard would it be to narrow down this PR to just the stud logo changes, and then create one or two other PRs for the other changes? I promise that I will deal with them in a timely manner.

There are 7 (essentially 5 with 2 build fixes) commits in this PR. The first 2 commits delineate the stud logo preference behaviour:

  1. Add stud logo dialogue to Windows preferences UI - e1145b3
  2. Add stud logo preference including command line option - ef321d8

...and 3-5 address the manually selected or automatically applied, stud cylinder and edge color management :

  1. Rename stud logo to stud style - f8dcb51
  2. Setup stud style and automate edge line color - 7e3ed83
  3. Setup Windows preferences UI - 2879a1a

As you can see, with the exception of f8dcb51 which transitions naming for better maintainability, these commits are broken down into control logic and Windows UI implementation. This shows that the optimal PR breakdown would create 2 PRs:

  1. Stud logo preference - e1145b3, ef321d8, and f8dcb51
  2. Automate edge line color (introducing high-contrast edge lines and stud cylinder colour management) - 7e3ed83 and 2879a1a

If this breakdown is acceptable to you, I will be pleased to close this PR, and create 2 new ones respectively ?

Cheers,

@trevorsandy
Copy link
Contributor Author

Do you know what the best way is to handle
this? I think it's probably easiest to just merge Trevor's Windows-only PR
to our master and then create new PRs for Qt and macOS.

FWIW, stud logo and edge line colour management was added to LeoCAD, in quite the same manner as this PR. Leo added his change commits to the PR branch until he was satisfied that the content reflected what he wanted - at this point he merged the PR to master. This is also how I manage large updates so as not to implicate the best-so-far state of master until I am pleased with what is being added.

Cheers,

@tcobbs
Copy link
Owner

tcobbs commented Mar 19, 2026

Do you know what the best way is to handle
this? I think it's probably easiest to just merge Trevor's Windows-only PR
to our master and then create new PRs for Qt and macOS.

FWIW, stud logo and edge line colour management was added to LeoCAD, in quite the same manner as this PR. Leo added his change commits to the PR branch until he was satisfied that the content reflected what he wanted - at this point he merged the PR to master. This is also how I manage large updates so as not to implicate the best-so-far state of master until I am pleased with what is being added.

Thanks for your replies. I can accept leaving this PR alone. It may take me a few days to review all the changes.

Copy link
Owner

@tcobbs tcobbs left a comment

Choose a reason for hiding this comment

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

@trevorsandy I'm still reviewing, but I wanted to submit the comments on the code that I have so far reviewed.

Comment on lines +81 to +82
, m_useStudStyle(false)
, m_studStyle(0)
Copy link
Owner

Choose a reason for hiding this comment

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

All the other new variables should be initialized here also for consistency, even if all LDView's code paths cause them to be set up elsewhere.


TCULong LDPreferences::getRGBAColor(int r, int g, int b, int a)
{
return ((a >> 24) & 0xFF) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
Copy link
Owner

Choose a reason for hiding this comment

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

The a logic seems broken here.

Suggested change
return ((a >> 24) & 0xFF) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);

Copy link
Owner

Choose a reason for hiding this comment

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

Also, is there a reason that you chose to use ARGB when LDLPalette uses RGBA? It looks like the actual values are stored as strings, so switching to RGBA might still be a good option. I think that such a change would be limited to LDPreferences.cpp.

If you did that, you could use the static LDLPalette::colorForRGBA instead of reimplementing it. (I realize it's small, but since your implementation seems to be broken, it's still a source of potential bugs.)

std::string rgbaString;

rgbaString = getStringSetting(key);
if (rgbaString.length())
Copy link
Owner

Choose a reason for hiding this comment

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

Always prefer an empty check over a length/size 0 check (or inverted logic like here).

Suggested change
if (rgbaString.length())
if (!rgbaString.empty())


bool m_automateEdgeColor;
bool m_useStudStyle;
int m_studStyle;
Copy link
Owner

Choose a reason for hiding this comment

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

Even though this maps to stud-logo[2-5], it still seems like it should be an enum.

#define WIREFRAME_THICKNESS_KEY "WireframeThickness"
#define ZOOM_MAX_KEY "ZoomMax" // NO UI
#define AUTOMATE_EDGE_COLOR_KEY "AutomateEdgeColor"
#define STUD_STYLE_USE_KEY "UseStudStyle"
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
#define STUD_STYLE_USE_KEY "UseStudStyle"
#define USE_STUD_STYLE_KEY "UseStudStyle"

char Data[TC_STUD_STYLE_MAX_DATA_LEN];
};

static struct TCStudStylePrimitive StudStylePrimitives[] =
Copy link
Owner

Choose a reason for hiding this comment

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

I'm going to have to give a hard veto on having hard-coded strings that are copied out of the LDraw Parts Library, for two reasons:

  • I'm almost certain that this is a CC license violation, since LDView is dual GPLV2/BSD licensed.
  • The underlying files might in fact update.

If these strings are absolutely necessary for the functionality to work, then load them out of the library the first time an LDraw file is opened and then keep them in memory after that. That would obviously require a somewhat different data structure.

Also, StudStylePrimitives should be studStylePrimitives.

Copy link
Owner

Choose a reason for hiding this comment

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

I see that you use the 4242 color as a "stud color" indicator. While that simplifies the code, I still can't allow LDraw files to be hard-coded into LDView. I see that all the 4242 color codes apply so -4cyli.dat files. Furthermore, only the outer rings in the stud4 files use *-4cyli.dat in color 16. Code could be written to exempt those from a generic (update all the *-4cyli.dat colors). Having said that, I don't understand why those cylinders don't have their colors updated. You are consistent in using color 16 for all of those outer cylinders, but it doesn't seem like this should be desired.

Copy link
Owner

Choose a reason for hiding this comment

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

After updating to load the files and then figure out where to stick the 4242s, it's probably better to just have both versions in memory at all times. The code would then just pick one instead of calculating it.

}

// Allocate C-style buffer for return
char* buffer = static_cast<char*>(malloc(result.size() + 1));
Copy link
Owner

Choose a reason for hiding this comment

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

This is leaked. The function should probably return std::string, especially since the return above cannot be freed.

Side note: a buffer allocations like this should be:

char* buffer = new char[result.size() + 1];

instead of malloc (with corresponding delete[]).

char Data[TC_STUD_STYLE_MAX_DATA_LEN];
};

static struct TCStudStylePrimitive StudStylePrimitives[] =
Copy link
Owner

Choose a reason for hiding this comment

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

I see that you use the 4242 color as a "stud color" indicator. While that simplifies the code, I still can't allow LDraw files to be hard-coded into LDView. I see that all the 4242 color codes apply so -4cyli.dat files. Furthermore, only the outer rings in the stud4 files use *-4cyli.dat in color 16. Code could be written to exempt those from a generic (update all the *-4cyli.dat colors). Having said that, I don't understand why those cylinders don't have their colors updated. You are consistent in using color 16 for all of those outer cylinders, but it doesn't seem like this should be desired.

if (sm_studCylinderColorEnabled)
return input;

const std::string studColor = "4242";
Copy link
Owner

Choose a reason for hiding this comment

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

Given the weird color numbers being used in LDConfig.ldr, how safe is 4242?

char Data[TC_STUD_STYLE_MAX_DATA_LEN];
};

static struct TCStudStylePrimitive StudStylePrimitives[] =
Copy link
Owner

Choose a reason for hiding this comment

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

After updating to load the files and then figure out where to stick the 4242s, it's probably better to just have both versions in memory at all times. The code would then just pick one instead of calculating it.

sprintf(tempPath, "/tmp/ldview_stud_style%d_%d_%s", studStyle, studCylinder, dictName);
#endif

std::ifstream existingSubModelStream(tempPath, std::ios::binary);
Copy link
Owner

Choose a reason for hiding this comment

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

I think it would be better to update LDLModel::load, LDLModel::read, and LDLModel::getLine to take an optional std::istringstream. Then, create an istringstream with the string data for the stud file and use that instead of writing the string data to tmp and then reading it out of tmp. Then LDLModel::getLine can simply read from the string stream instead of reading from the file stream.

char style[10] = "";
if (studStyle > 1)
sprintf(style, "%d", studStyle);
sprintf(data, "0 Stud %s\n0 Name: %s\n0 Author: James Jessiman\n0 !LDRAW_ORG Primitive\n0 BFC CERTIFY CCW\n1 16 0 0 0 1 0 0 0 1 0 0 0 1 stud%s-logo%s.dat\n", (openStud ? "Open" : style), dictName, (openStud ? "2" : ""), style);
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
sprintf(data, "0 Stud %s\n0 Name: %s\n0 Author: James Jessiman\n0 !LDRAW_ORG Primitive\n0 BFC CERTIFY CCW\n1 16 0 0 0 1 0 0 0 1 0 0 0 1 stud%s-logo%s.dat\n", (openStud ? "Open" : style), dictName, (openStud ? "2" : ""), style);
snprintf(data, sizeof(data), "0 Stud %s\n0 Name: %s\n0 Author: James Jessiman\n0 !LDRAW_ORG Primitive\n0 BFC CERTIFY CCW\n1 16 0 0 0 1 0 0 0 1 0 0 0 1 stud%s-logo%s.dat\n", (openStud ? "Open" : style), dictName, (openStud ? "2" : ""), style);

Copy link
Owner

Choose a reason for hiding this comment

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

Unless I'm misunderstanding, this is a wrapper around one of the other files, right? If that is so, the author should probably be LDView, and maybe "LDView wrapper" should be appended to the name? For LPub3D, this is completely hidden away, but in LDView itself, the information will be visible in the model tree.

{
bool isOpen = isStudStylePrimitive(dictName, studStyle) == 2;
if (studStyle == 7 && (strcasecmp(dictName, "stud.dat") == 0 || isOpen))
sprintf(data, "0 Stud %s\n0 Name: %s\n0 Author: James Jessiman\n0 !LDRAW_ORG Primitive\n0 BFC CERTIFY CCW\n1 16 0 0 0 1 0 0 0 1 0 0 0 1 stud%s-logo.dat\n", (isOpen ? "Open" : ""), dictName, (isOpen ? "2" : ""));
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
sprintf(data, "0 Stud %s\n0 Name: %s\n0 Author: James Jessiman\n0 !LDRAW_ORG Primitive\n0 BFC CERTIFY CCW\n1 16 0 0 0 1 0 0 0 1 0 0 0 1 stud%s-logo.dat\n", (isOpen ? "Open" : ""), dictName, (isOpen ? "2" : ""));
snprintf(data, sizeof(data), "0 Stud %s\n0 Name: %s\n0 Author: James Jessiman\n0 !LDRAW_ORG Primitive\n0 BFC CERTIFY CCW\n1 16 0 0 0 1 0 0 0 1 0 0 0 1 stud%s-logo.dat\n", (isOpen ? "Open" : ""), dictName, (isOpen ? "2" : ""));

Copy link
Owner

Choose a reason for hiding this comment

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

Same as above regarding author and name.

Comment on lines +109 to +110
static LDLColor getAlgorithmicEdgeColor(const TCVector& Value, const float ValueLum,
const float EdgeLum, const float Contrast, const float Saturation)
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
static LDLColor getAlgorithmicEdgeColor(const TCVector& Value, const float ValueLum,
const float EdgeLum, const float Contrast, const float Saturation)
static LDLColor getAlgorithmicEdgeColor(const TCVector& value, const float valueLum,
const float edgeLum, const float contrast, const float saturation)

Comment on lines +486 to +494
sm_partEdgeColorEnabled = TCUserDefaults::boolForKey(PART_EDGE_COLOR_ENABLED_KEY, sm_partEdgeColorEnabled);
sm_blackEdgeColorEnabled = TCUserDefaults::boolForKey(BLACK_EDGE_COLOR_ENABLED_KEY, sm_blackEdgeColorEnabled);
sm_darkEdgeColorEnabled = TCUserDefaults::boolForKey(DARK_EDGE_COLOR_ENABLED_KEY, sm_darkEdgeColorEnabled);
sm_automateEdgeColor = TCUserDefaults::boolForKey(AUTOMATE_EDGE_COLOR_KEY, sm_automateEdgeColor);
sm_useStudStyle = TCUserDefaults::boolForKey(STUD_STYLE_USE_KEY, sm_useStudStyle);
sm_studStyle = (int)TCUserDefaults::longForKey(STUD_STYLE_KEY, sm_studStyle);
sm_partColorLDIndex = TCUserDefaults::floatForKey(PART_COLOR_LD_INDEX_KEY, sm_partColorLDIndex);
sm_partEdgeContrast = TCUserDefaults::floatForKey(PART_EDGE_CONTRAST_KEY, sm_partEdgeContrast);
sm_partEdgeSaturation = TCUserDefaults::floatForKey(PART_EDGE_SATURATION_KEY, sm_partEdgeSaturation);
Copy link
Owner

Choose a reason for hiding this comment

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

These need to be assigned using static setters from LDPreferences.cpp.

Comment on lines +500 to +509
std::string rgbaString = TCUserDefaults::stringForKey(STUD_CYLINDER_COLOR_KEY, "27,42,52,255");
if (sscanf(rgbaString.c_str(), "%d,%d,%d,%d", &r, &g, &b, &a) == 4)
sm_studCylinderColor = LDLColor{ (TCByte)r, (TCByte)g, (TCByte)b, (TCByte)a };
rgbaString = TCUserDefaults::stringForKey(PART_EDGE_COLOR_KEY, "0,0,0,255");
if (sscanf(rgbaString.c_str(), "%d,%d,%d,%d", &r, &g, &b, &a) == 4)
sm_partEdgeColor = LDLColor{ (TCByte)r, (TCByte)g, (TCByte)b, (TCByte)a };
rgbaString = TCUserDefaults::stringForKey(BLACK_EDGE_COLOR_KEY, "255,255,255,255");
if (sscanf(rgbaString.c_str(), "%d,%d,%d,%d", &r, &g, &b, &a) == 4)
sm_blackEdgeColor = LDLColor{ (TCByte)r, (TCByte)g, (TCByte)b, (TCByte)a };
rgbaString = TCUserDefaults::stringForKey(DARK_EDGE_COLOR_KEY, "27,42,52,255");
Copy link
Owner

Choose a reason for hiding this comment

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

These need static members so that they can be assigned using static setters from LDPreferences.cpp.

#include <TCFoundation/TCVector.h>
#include <TCFoundation/TCLocalStrings.h>
#include <TCFoundation/TCUserDefaults.h>
#include <LDLib/LDUserDefaultsKeys.h>
Copy link
Owner

Choose a reason for hiding this comment

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

Also not allowed.

}
else if (strcasecmp(m_modelName, "stud.dat") == 0)
{
if (TCUserDefaults::boolForKey(STUD_STYLE_USE_KEY, false))
Copy link
Owner

Choose a reason for hiding this comment

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

Use a static member variable with a setter that is called from LDPreferences.

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