Conversation
|
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. |
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 WireCheers, |
|
@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. |
|
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.
Note: I have not yet reviewed Trevor's PR, but I will do that. Can you
include a Qt screenshot in your PR description? I'll do one for macOS once
I have it implemented.
…--Travis
On Tue, Mar 17, 2026 at 4:08 PM Peter Bartfai ***@***.***> wrote:
*pbartfai* left a comment (tcobbs/ldview#101)
<#101 (comment)>
@pbartfai <https://github.com/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 <https://github.com/tcobbs> 4.7 has been released
I've prepared the Qt UI...
—
Reply to this email directly, view it on GitHub
<#101 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AE4BDKKQDDTTMILLWC6KEQL4RHEHBAVCNFSM6AAAAACKWOSG3CVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DANZYGI3TMMBUGY>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
|
@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:
|
|
After starting to review the PR, I realized that it has two (maybe three)
unrelated features. I asked Trevor if he would be willing to split it into
multiple PRs. I'll see what he says.
…--Travis
On Tue, Mar 17, 2026 at 7:19 PM Travis Cobbs ***@***.***> wrote:
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.
Note: I have not yet reviewed Trevor's PR, but I will do that. Can you
include a Qt screenshot in your PR description? I'll do one for macOS once
I have it implemented.
--Travis
On Tue, Mar 17, 2026 at 4:08 PM Peter Bartfai ***@***.***>
wrote:
> *pbartfai* left a comment (tcobbs/ldview#101)
> <#101 (comment)>
>
> @pbartfai <https://github.com/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 <https://github.com/tcobbs> 4.7 has been released
> I've prepared the Qt UI...
>
> —
> Reply to this email directly, view it on GitHub
> <#101 (comment)>, or
> unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AE4BDKKQDDTTMILLWC6KEQL4RHEHBAVCNFSM6AAAAACKWOSG3CVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DANZYGI3TMMBUGY>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>
|
The easiest would be merging this PR first then update Mac and Qt code. |
|
Hi Travis, I can confirm that this PR only contains the following: Stud logo options 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.
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.
There are 7 (essentially 5 with 2 build fixes) commits in this PR. The first 2 commits delineate the stud logo preference behaviour:
...and 3-5 address the manually selected or automatically applied, stud cylinder and edge color management :
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:
If this breakdown is acceptable to you, I will be pleased to close this PR, and create 2 new ones respectively ? Cheers, |
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, |
Thanks for your replies. I can accept leaving this PR alone. It may take me a few days to review all the changes. |
tcobbs
left a comment
There was a problem hiding this comment.
@trevorsandy I'm still reviewing, but I wanted to submit the comments on the code that I have so far reviewed.
| , m_useStudStyle(false) | ||
| , m_studStyle(0) |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
The a logic seems broken here.
| return ((a >> 24) & 0xFF) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); | |
| return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); |
There was a problem hiding this comment.
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()) |
There was a problem hiding this comment.
Always prefer an empty check over a length/size 0 check (or inverted logic like here).
| if (rgbaString.length()) | |
| if (!rgbaString.empty()) |
|
|
||
| bool m_automateEdgeColor; | ||
| bool m_useStudStyle; | ||
| int m_studStyle; |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
| #define STUD_STYLE_USE_KEY "UseStudStyle" | |
| #define USE_STUD_STYLE_KEY "UseStudStyle" |
| char Data[TC_STUD_STYLE_MAX_DATA_LEN]; | ||
| }; | ||
|
|
||
| static struct TCStudStylePrimitive StudStylePrimitives[] = |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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[] = |
There was a problem hiding this comment.
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"; |
There was a problem hiding this comment.
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[] = |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
| 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); |
There was a problem hiding this comment.
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" : "")); |
There was a problem hiding this comment.
| 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" : "")); |
There was a problem hiding this comment.
Same as above regarding author and name.
| static LDLColor getAlgorithmicEdgeColor(const TCVector& Value, const float ValueLum, | ||
| const float EdgeLum, const float Contrast, const float Saturation) |
There was a problem hiding this comment.
| 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) |
| 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); |
There was a problem hiding this comment.
These need to be assigned using static setters from LDPreferences.cpp.
| 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"); |
There was a problem hiding this comment.
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> |
| } | ||
| else if (strcasecmp(m_modelName, "stud.dat") == 0) | ||
| { | ||
| if (TCUserDefaults::boolForKey(STUD_STYLE_USE_KEY, false)) |
There was a problem hiding this comment.
Use a static member variable with a setter that is called from LDPreferences.

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
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.
This feature is a subset of the comprehensive behaviour which also includes high-contrast studs and edge lines.
With the addition of high-contrast studs and automatic edge line colors, the stud style ini file options have changed to:
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,