This patch introduces new 2D plotting styles 'marks', 'linesmarks' and 'mark' object.
For more information, see the attached documentation about the patch.
The sample programs for the figures in the documentaion are also attached as the tar+gzipped archive file.
First-pass review of gnuplotmarks patch
I have only glanced at the code, so these comments are based mostly on
reading the pdf description and trying it out. Any or all of these
points may be worth discussion; perhaps we should start a thread on
the gnuplot-beta mailing list? It's not very active these days but
still I think people are more likely to see it there than will see it
here on the patch tracker.
I think the general goal and approach is worth doing.
An unmentioned difference between "with polygons" and "with marks"
is that polygon rendering uses the pm3d code path; the mark code is
purely 2D. This may not be obvious to the user, but may end up
making a difference in applying colors, fill style, borders, etc.
I like that it is terminal-independent. This means it can
replace the recent commit series 6bc3c0eb..edcd8dbb that added
a small number of terminal-dependent special point types.
The functional equivalence is shown by Figure 3.
The "mode" parameter is not a good idea, given that gnuplot uses
fillstyle everywhere else to control the fill and border propertie.
"mode" is both confusing and less capable than allowing a fillstyle
to be attached to a mark object or to a plot "with marks".
I do realize that there are several wrinkles here. A mark that
consists of a polyline does not necessarily have either a "fill area"
or a "border", so some adjustment to the terminology may be needed.
If this ends up requiring an addition or revision to fillstyle syntax,
that's still acceptatble.
The large chunk of code in set.c is more like a plot mode
(calling df_open, reading data, etc) than a typical "set" command.
Because it calls df_open and df_readline, it needs to be protected
against recursive or conflicting use of function blocks
(see commit 44d9343f, Bug #2702). I would prefer putting this code
elsewhere, maybe in a new source file.
I don't know whether it's a bug or my misunderstanding, but I can't
get the variable pointsize scaling to work for "plot with marks".
I think "3" means fill+border? Please switch to using a fillstyle.
What is the "-1"?
Would it be feasible to report instead "mark 2 $triangle"?
"set mark 3 $triangle fillcolor 'blue'"
This command is accepted but so far as I can see the color has no effect.
Is there a way to set a fixed, or at least a default, color for the mark?
plot with linesmarks
I realize that this was already an issue for "plot with linespoints",
but the current program design isn't flexible enough to permit
specifying both a complete set of line properties and a complete set
of point (or in this case mark) properties in a single plot component.
Is it really so bad to require "plot <foo> with lines, '' with marks"
rather than providing a separate plot style that combines the two parts?</foo>
I have just posted an announcement of the creation of this ticket to gnuplot-beta.
The style 'marks' was initially thought of as objects similar to 'circles', 'ellipses', 'boxxy', or 'vectors'. Later, I added the 'units ps' keyword and the 'linesmarks' style, thinking they might have value as an alternative to the 'points' style. However, I don't have many feelings about 'linesmarks', so until the linespoints issue can be resolved, it is fine to remove 'linesmarks' from this implementation.
Incidentally, one difference between 'linesmarks' and 'lines' + 'marks' is the presence of the keywords 'pointinterval' and 'pointnumber'. However, the processing of these options belongs to 'marks', not 'lines'. Removing this process simplifies the code.
Yes. As you say, "3" means fill+border, but it is inappropriate as the default value for mode. In the code at hand, the default value of mode has been changed to follow the fillstyle. Also change the mode value as follows.
"-1" means the fill color using a fillcolor or linecolor setting.
It can be done with,
I would rather not accept such an option, but how can I implement this?
To give the mark a fixed color, specify fillcolor as an integer in the fourth column of the input data. If you give this column a value of -1 (the default value), it will use the fillcolor or linecolour setting; as with polygons, you can specify a different color for each segment separated by whitespace.
In the code at hand,
also works as expected.
This 'mode' parameter gives more freedom in the definition of marks; it is a feature for those who want to make deeper use of marks, and should be left in place.
In the code at hand, the value of mode has been changed as follows
The default value has also been changed to mode=0 instead of mode=3.
Users who do not want to actively use mode can use fillstyle by default. This means that mode can be omitted in the input sequence to set mark, so it is sufficient to give only the (x,y) coordinates when defining the mark.
In the current implementation, a mark contains polylines and polygon's borders, both of which are considered as 'stroke' and have the same color and thickness.
The introduction of 'mode' is also necessary to facilitate the realisation of Fig. 3 in marks.
As noted in the patch documentation, there is a notice regarding this.
In gnuplot, drawing to terminals is done using scaled integer coordinates per terminal. If the scaling is not sufficient for bitmap terminals or even vector terminals in some cases, the mark shape may be distorted due to quantization errors. This distortion is especially noticeable when the mark is smaller.
On the other hand, the 'points' style draws the shape in a way that is optimized for the terminal, resulting in a clean shape with little distortion. For example, a circle mark is drawn as a polygon in the 'marks' style, but in the 'points' style, it is drawn using the terminal's native 'circle function'.
In addition, for the 'wxt' and 'pngcairo' terminals, hinting is performed internally, which may distort the shape. You can see the effect by trying different hinting settings on the wxt terminal.
Apart from the above notices, the advantage is that the same shape can be used for any terminal.
As your example shows, 'pointsize' and scaling (by the 3rd and 4th column) conflict in the current implementation. I'm not sure which one to use if 'pointsize' and scaling are specified at the same time. The 'pointsize' option was introduced to make the 'marks' style similar to the 'points' style, but one option is to eliminate the 'pointsize' option and keep only scaling to reduce user confusion.
Even the 'points' style has different point sizes for each terminal, so there is no guarantee that the marks drawn in the 'marks' style will match the size of the 'points' style.
Attached '001_change_mode_default.patch' is a patch file againt 'gnuplot_marks.patch'
This patch changes the drawing style for mode=0 to follow the fillstyle setting.
Edited
Sorry. I attached file.
Last edit: Hiroki Motoyoshi 2024-04-22
It seems the patch did not successfully attach.
variable mark size in a plot
I am still confused about this. My understanding, perhaps wrong, was that
set mark
does not specify what the coordinate units are, so this must be added later by the units keyword in aplot
orset object
command. So if I want the mark to be interpreted in terms of point size units in a plot command I sayplot using 1:2 with marks units ps
to use a scale factor of one, andplot using 1:2:(3) with marks [units ps]
to make the marks 3x larger. Theunits ps
may be optional because this is the default? But either way it doesn't work. The marks do not scale either with or without an explicit units keyword. How do I tell it to take the scale factor from the third column? Normally this would beps variable
but it doesn't accept that.'mode' vs 'fillstyle'
I think you may have misunderstood the point I tried to make earlier about using the existing 'fillstyle' property rather than introducing a new 'mode' property. I did not mean to use the global fillstyle instead of a per-mark property. I meant that if you are going to store an extra property for each mark, why limit this to two flags (bits 0 and 1 in 'mode')? Why not store a full set of fillstyle properties? That would give consistent syntax with other object types and plot styles. Example:
Here 804 is the result of feeding fillstyle 'transparent solid 0.5' through term.c:style_from_fill().
However the color is problematic. I show 0x000000 ss the RGB representation of lc "black" but is this to be the border color or the fill color? That same ambiguity is present everywhere a fillstyle occurs and is an on-going headache I don't have a good cure for.
Does that make more sense now? I'm still exploring your new code so this is only a first thought, not a final opinion.
variable mark size in a plot
Your description is correct! And "units ps" is the default in the current implementation. In addition, pointsize defaults to 1.0.
The current implementation does not do that and only refers to pointsize to determine the size. However, this does not allow us to change the aspect ratio, so as you said, in the next patch, I have modified it so that the size is determined by multiplying pointsize and xscale(yscale).
The current implementation did not handle the 'ps variable' option.
They are fixed to accept the 'ps variable' option in the next patch.
mode vs fillstyle
Thank you for the detailed explanation. It finally makes sense to me.
I had not considered such an idea at all because fill and stroke were sufficient for my use cases. If this is realized, something like 'fillstyle variable' might be feasible. However, I hope to keep the data given to 'set mark' command as simple as possible. We must carefully consider whether this feature is necessary and what to do with fillstyle codes that seem like a cipher to ordinary users. I rather think it would be useful to add the ability to specify line thickness (whether it can be implemented or not).
Anyway, one idea I have is to add an additional column to the set mark input data, as follows.
The meaning of 'mode' is the same as before. The above addition of a column would be a good compromise between the simplicity of manually preparing mark data and the high functionality of setting a full-spec fillstyle.
Adding the following option you tried as a feature to support setting fillstyle would be helpful (it is not easy for me to implement).
In these settings, the 'fillstyle' option of 'set mark' will overwrite the value of column 5 (or default value) of $element2 or $element3. The remaining question is how to generate the 'fillstyle' raw code such as '804' when creating the data manually.
Attached '002_ps_variable.patch' is a patch file againt 'gnuplot_marks.patch' + '001'.
This patch add the 'ps variable' handling.
After playing a bit using "plot with marks" I have a suggestion. I think that the pointsize property should behave for marks as much as possible exactly as it does for "plot with points".
pointsize
keyword is parsed and interpreted using the lp_parse routine shared by other plot styles.pointsize variable
consumes an input column, and again it acts as a multiplier for the marks regardless of what units are used to define them.size
field of the marks_options structure is no longer needed (but I didn't remove it).I attach a small patchset that I used to test out these ideas. I was looking only at plot commands, not "set object mark", so it is quite possible I unintentionally broke something there.
I also got some funny results from a test case using a function-defined mark mark type, but I'm not sure whether that's something I broke, something I misunderstand, or something that was already there and not working. I will investigate further.
Thanks for the patch, I was worried about interference using lp_parse as no other plotting style has both POINT and FILL styles at the same time. As there doesn't seem to be any major problem, I agree to apply your patch.
The execution of 'Fig11_formulas.gp' failed, but this is due to a restriction on the word order of the options, caused by introducing of 'lp_parse'.
Additional modifications have been made in line with this.
The attached small patch files are after "0001-EAM-Make-ps-N-and-ps-var-act-as-they-do-for-other-pl.patch".
As well as the change to 'pointsize' handling, the values of 'pointinterval' and 'pointnumber' are also changed to use the result of 'lp_parse()'. This patch makes the 'pointinterval' and 'pointnumber' options available also for 'marks' style. This is because this is useful for 'lines + marks' combinations.
This is a patch to stop forcing 'units ps' when the ps option is specified or when plotting a function.
This patch changed internal handling of 'marktype variable'. Previously, it was determined by whether 'marks_options.tag' was negative or not. Now, a member 'variable' to distingwish whether the marktype is 'variable' or not has been introduced to the struct 'marks_opt'. In addition, the 'size', 'number' and 'interval' members, which are no longer used in marks_opts, have been removed.
I've now had a look through all the code. Here are my comments.
Code consolidation
A loop like this appears many times in the code
These should be replaced by a call to a shared utility routine
(struct mark_data *) get_mark(int tag)
I realize the loops are not truly identical, so some care is needed.
Difference between
show
andsave
Please follow the current practice used by the rest of the program:
save_FOO() can write either to a file or to stderr.
show_FOO() always writes to stderr.
As an implementation convenience, show_FOO() may be implemented internally
as save_FOO(stderr).
Key entries
I attach a patch that revises the code path for drawing a mark in the plot key so that the code path is more like that for "plot with points". The order of adding key elements is a little tricky because some terminals do not correctly preserve the full graphics state across the call to do_key_sample().
Fill style
Another thought about how to make it convenient for the user to give a full fill style when defining a mark. What if rather than requiring four columns
using (x):(y):(mode):(color)
this syntax would be accepted
set mark N $DATA using (x):(y) <fillstyle-specifier> <color-specifier>
The program can translate the fill and color specifiers into integer values stored in the mark structure but the user does not need to construct those bit codes manually.
A save_mark(N) routine would have to back-translate the values to reconstruct the input syntax but that should be straightforward.
Do you think that would work?
Limitation of
set mark
The implementation of set_mark() worries me a bit. It does open/read/close on the input stream twice; once to count vertices, then a second time to read and store the data.
This means it cannot be used for piped input, right?
Is there a reason not to use a single pass and reallocate space for the vertices incrementally?
I think this should either be documented as an intentional limitation with error messages added to the program where needed, or changed to use a single-pass algorithm.
Interesting trick for compact definition of mark vertices
This form already works (explicit columns)
Maybe the program should default to
using 2:3
if the data source is an array? That would allow omitting the using specifier, making it simplyset mark 1 Diamond
.Five small patch files include the responses to your point about ‘Code consolidation’ and ‘Limitation of set mark’ are attached.
006_add_empty_option_for_set_mark.patch
This introduces a new syntax for explicitly defining an empty mark.
In the future, this feature may be removed if it proves unnecessary.
007_consolidate_codes_as_get_mark.patch
This is a patch to address your point "Code consolidation", defining the 'get_mark()' function and refactoring 'graphics.c' to use it.
008_add_new_struct_mark_data_member_vertices.patch
This patch adds a 'vertices' member to 'struct mark_data' that holds the number of vertices (including number of empty rows) contained in marks. Previously, 'mark.polygon.type' was used for that role.
009_add_push_mark_and_get_previous_mark_function.patch
This introduces functions for use in next '010_...' patch.
010_refactoring_set_mark.patch
Patch to address your point "Limitation of set mark", including refactoring of the 'set_mark()' function.
In the current implementation, the 'save' command does not support marks at all. It would be helpful if you could advise what output would be desirable. Simply put, the output could be something like.
However, the current implementation of 'set mark' does not allow ‘-’ as input.' What is missing in the current implementation to make ‘-’ available in the 'set_mark()' function?
Thanks. It will take me some time to look through your revised code.
As to how to save the mark definitions, yeah - good question.
I'm not sure what is best. I can think of several options
1 - Don't save them at all
2 - Add a field to the mark structure that is a string containing a copy of the original command that created it; write that string into the save file.
3 - For each mark save the vertices in a datablock (see below)
4 - For each mark save the vertices in an array
Probably it would be good to make it a separate command "save marks", analogous to the existing "save datablocks" command.
After applying these patches
Sorry.
The bug occurred in the 'set_mark' function when replacing a newly constructed mark with an existing mark with the same tag number. No special processing was done as required if the mark to be replaced was the first element in the mark list.
Please look at the attached patch file,
011_BUGFIX_special_treatment_of_replacing_first_mark_in_mark_list.patch
Comments/problems 29 April
missing "break" statement after set.c:371 set_mark();
- example of failure: set mark 1 empty; show mark 1
Not documented: set mark N append <already-defined tag="" number="">
- copies an existing tag
- what is the intended use for this?</already-defined>
Mark symbols are not drawn in the key if the key is outside the plot
- it looks like the mark is clipped even though do_sample_key_mark()
passes clip=FALSE to do_mark()
comment in plot_marks "stored in yhigh" should be "stored in z"
comment in eval_plots "pick up the special..." should be "options specific to marks"
"set object mark" needs to accept keyword "center" (synonym for "at")
- example of failure:
set obj 1 mark mt 1 at 1,1; save "foo"; load "foo"
compiler warning
graphics.c:3120:9: warning: ‘mark’ may be used uninitialized [-Wmaybe-uninitialized]
Attached patch:
- move get_mark() to gadgets.c
- substitute get_mark() for in-line code in show.c
- move function prototypes to the header file corresponding to the
source file that implements that function
- remove unused variables and unnecessary function calls from set_mark()
Attached are multiple patches including the bug fixes that address your "Comments/problems 29 April". I believe many of the features you have noted so far have been implemented in these patches.
Hopefully this will help us finalize the 'marks' style specification.
012_BUGFIX_for_Apr_29_comments.patch
This patch addresses the following bugs.
013_save_mark_object.patch
This patch allows the save command to handle mark objects.
014_show_mark.patch
Would it be feasible to report instead "mark 2 $triangle"?
In addition, the following requests at the early stage of the project is also implemented in this patch.
015_save_marks.patch
This patch is the first implementation of 'save marks'.
The 'save_marks()' function in this patch is revised by "017_introducing_fillstyle_column_for_set_mark_input_data.patch" to treat the 5 column format.
After '017_introducing_fillstyle_column_for_set_mark_input_data.patch', the following script
generats the file "foo" like
016_free_mark.patch
In this patch, the code to free the mark structure was consolidated as the 'free_mark()' function.
017_introducing_fillstyle_column_for_set_mark_input_data.patch
This patch introduces 'fillstyle' as the fifth column of the set mark input data.
Now, the 'set mark' command takes the following four patterns of data as input.
The reason for leaving the 'mode' column is that the 'fillstyle' code returned by the 'style_from_fill()' function does not include border information. The 'mode' column specifies either STROKE or FILL attributes as a drawing method familiar in the cairo library, svg, postscript, etc.
The 5 columns of input data is useful for reproduction of marks with 'save' or 'load' command.
Instead, it also provides a convenience method by introducing the following options,
With these options, the value of 'mode', 'color', 'fillstyle' column data are overrided by these option.
Using '<fillstyle-specifier>', STROKE or FILL can be controlled as follows</fillstyle-specifier>
For example,
018_remove_MARK_ACTION_COPY_in_set_mark.patch
This patch removing the undocumented syntax,
It was introduced to compose a variation on an existing complicated mark with a different paint or stroke attribute, but was not found to be very useful. Use the following command instead. This is more useful because you can make full use of the 'using' and 'fillstyle' options.
The show_mark() function introduced in ‘014_show_mark.patch’ had a bug regarding the initialization of the datablock variable. A patch to fix this bug is attached.019_BUGFIX_initializing_datablock_in_show_mark.patchEDIT
Sorry, it is found that this patch can't fix the bug.
SEGV was occurring when loading a lot of data into set mark. The cause of the bug was that the fillstyle member of the mark_data struct was not properly handled in the mark's reallocation when loading data in set mark.
The attached "020_BUGFIX_fillstyle_reallocation_in_set_mark.patch" fixes this bug.
Last edit: Hiroki Motoyoshi 2024-05-02
Some updates were made.
021_show_marks_datablock_handling.patch
The handling of output to datablock in the 'show_mark()' function was corrected with reference to the 'new_block()' function.
022_save_marks_uses_pseudo_file.patch
"save marks" output changed to use pseudo file as follows.
023_careful_comparison_of_fillstyle_type.patch
Changed to use '(style & 0xf)' for comparison with FS_EMPTY in response to changes made by 'commit 83e4eadb78c8e7a4a41ff9805d9052d42daa0ad3'.
024_defining_subroutine_read_mark_data_and_refactoring_set_mark.patch
This patch makes the data reading part of the 'set_mark()' function using 'df_open()' a subroutine ('read_mark_data()'), making set_mark() itself more compact.
This is also in preparation for the following comment.
025_special_treatment_of_array_input_without_any_data_modification.patch
This patch is intended to implement the following proposal,
After this patch, the following command can be accepted.
Last edit: Hiroki Motoyoshi 2024-05-03
Heh. You are working too fast for me to keep up. I was just about to commit a consolidated version of your work through patchset 018 and a round of adjustments I made in response but was still in the process of testing thoroughly.
I will see if 021-025 are useful on top of what I have here at the moment; otherwise I suggest you wait for me to commit what I have to the master branch and we can both work off that starting tomorrow.
Here's a summary of what I did and where it stands. See also the commit messages.
I committed your patchsets through 018 and my intervening contributions, with the exception of patchsets 14 and 17.
014_show_mark was a misunderstanding of what I tried to say earlier. I apologize for the confusion. The point I was trying to make was that gnuplot's
show
commands are intended to help the user remember what state the program is in during an interactive session. They write only to stderr and the output is generally not in the form of executable commands. I think you misinterpreted my attempt at an example of what the program might include in the output from show; what I was trying to suggest was that if a mark had been created by the commandset mark 8 $heart using 1:2
thatshow mark 1
might report something likemarktype 8, polygon vertices 37, data source $heart
. I think that is much more helpful than a dump of the 37 vertices. A further thought was that it might remember and report back the entireset mark
command; if we were to implement that then it would also be useful as output fromsave
, but let's defer that discussion to later because your currentsave
code is working nicely.017_introducing_fillstyle_column I did not apply because after much experimentation I decided your original idea of three values per vertex
x:y:mode
was in fact a better choice.The code at that point (3fca5d997 marks 18 - remove MARK_ACTION_COPY) compiles and runs correctly on the test figures
I then did a bunch of code cleanup to remove unused variables, misplaced prototypes, whitespace, and a few things that produced compiler warnings. No change to the functional code.
Then I revised the mark data structure back to hold only x:y:mode and added a per-mark fillstyle and fillcolor. The commit message is below.
Tonight I have been working on sanity checks and error messages for things like failure to open or read the file provided to
set mark
.Read the commit message below (or the full set once I push it to SourceForge). Try out the code and let me know if you're OK with the revision. The test figures still work fine with the exception of the two-color marktype 10 in Figure 11.
I had assumed that only the final code would be committed to the main branch once the discussion and specification work here had settled down. I apologize for the haste in getting things done. I also apologize for the sending many patch files in incomplete states.
Thank you for reviewing codes deeply and committing the modified codes.
With your detailed explanation, it make sense for me what 'show' command should print. I agree with you that 'show' command shoud print like "marktype 8, polygon vertices 37, data source $heart".
I would still like to have the ability to copy the contents of the mark's data to a datablock separately from that, so I hope you can discuss this later. I would still like to have the ability to copy the contents of the mark's data to a datablock separately from it, so I hope we can discuss the specification of this later.
There have been some interesting applications of the 'fillstyle' column in practice. But it is a trade-off between interesting application and memory inefficiency, as all rows will have memory allocations. In this respect, the idea of removing them makes sense.
It is important to note that I'm afraid I have to disagree with removing the color column. You mentioned the "original idea of three values," but I originally proposed the four values format, including the 'color' column. As you said, the SourceForge code breaks the tenth mark in Fig11_formulas.gp by removing the color column.
The color column follows the format of the 'fillcolor rgb variable' in the ‘with polygons’ style. The color column is necessary to represent a pseudo-hall by filling it with the same color as the background in the 'append' process, as in the tenth mark in 'Fig11_formulas.gp'. This feature is one of my motivations for creating the original patch because the gnuplot's polygon routine can not draw transparent holes. If possible, I request that the color column be revived and reverted to four values.
If memory efficiency is the biggest issue, you can pack the integer values from two or three columns: mode(2bit) and colour(32bit RGBA) can be packed (34bit) to fit into a double. Even, a pack (42bit) of mode (2bit), colour (24bit RGB) and fillstyle (16bit) will also fit into a double. The trade-off is the packing and unpacking process.
I appreciate you for revising my insufficient codes.
I also will look into your revised version of codes in the SourceForge.