olivierkes / manuskript

A open-source tool for writers
http://www.theologeek.ch/manuskript
GNU General Public License v3.0
1.7k stars 226 forks source link

Calendar Generator #1263

Open FastusNumen opened 6 months ago

FastusNumen commented 6 months ago

This is a question, and maybe a future improvement.

Would it be possible to add a calendar generator inside the program?

Until now, I've been using donjon for this, and I copy the calendar in LibreOffice Calc.

It's useful to note down the dates of when stuff happens. Maybe it would be easier to add than adding the date to the timeline. It could be another entry in the navigation menu.

obw commented 6 months ago

Thanks for the Link to this awesome tool, but I think, this is a copy of my Issue #155!

Regards

FastusNumen commented 6 months ago

No, I knew that issue, you already linked to that in this one #1221. If I got it right, you meant to have the time and date on each scene.

This is different. I meant to have an actual calendar in the program. Maybe put it between World and Plots, so you can write down events and stuff directly inside that calendar, without going out.

TheShadowOfHassen commented 6 months ago

For writing fantasy or sci-fi with weird calendars I like a feature like this, however as Manuskript is supposed to be a general purpose writing software, it might be one of the features, that will get in the way for all who don't use a calendar.

When I have a story with a heavy world building, I use Obsidian. It has a plugin for using The Fantasy Calendar, and it works very well.

obw commented 6 months ago

@TheShadowOfHassen Well the possibility to add Metafields like I mention in #155 and then the possibility for Plugins, something like this here should be possible. Let Manuskript in the Core, all-purpose, but when needed, add a Plugin with the needed special functions!

I'm just planning a Comic and could find any solution which is working for me, so I will work on Paper, because I want to make a Storyboard, without a script. It's a try to perfect my one Storytelling, working more visual. I would like to do it in Manuskript and have the possibility to add Krita, for my Scribbles and a specialized View for it, but not possible at the moment for Manuskript!

I see this wish for such a Calendar in a similar scope, a cool Feature, for the people who need it, but also for a Majority, not so much!

That's my way of thinking. The possibility to add Metadata to text elements, is something which Manuskript is actual missing, when we have this, and a working plugin API, then someone could simply add an implemention of a calendar, if it is a normal one, or something completely fantastic, is then just a question, how to implement it! That is also the reason why I had referenced my own old Issue!

I hope what I mean and think is understandable!

FastusNumen commented 6 months ago

In my opinion, adding a calendar generator on the navigation menu wouldn't be too much of a bother for people. I don't use the Outline, but it doesn't annoy me to have it there. Just my opinion though.

In the meantime, I created a code to add my own calendar in World. Just something simple.

It could be a useful alternative to the entry in the navigation menu.

Here is what it looks like:

Screenshot 2023-12-24 152623

This is the code I used:

day_names = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] # names of the days of the week
x = len(day_names)                                                                         # get the number of days in a week
month_names = ["Month 1", "Month 2", "Month 3", "Month 4", "Month 5", "Month 6",
               "Month 7", "Month 8", "Month 9", "Month 10", "Month 11", "Month 12"]        # names of the months
month_days = 30                                                                            # number of days in a month (all the same amount of days for now)
shift = 6                                                                                  # which day of the week starts the year 0 = Monday, 6 = Sunday
s = shift                                                                                  # variable assign the days of the week to the days of the month
'''
Creating a fictional calendar. Its just for a single year.
@:param id: the ID of the last item created. Just create an item before doing this to see which is the last id in the world.opml file.
@:param year: the year you want for your calendar
'''
def calendar_year(id, year):
    global s                                                   # global so it can work on every month, otherwise it would start from 0 each month
    global i                                                   # creates global variable i to assign all ids to the calendar months and days
    i = id + 1                                                 # assign the ID to the global variable i incremented by 1
    print(f'    <outline name="Year {year}" ID="{i}">')        # creates the year
    for month in month_names:                                  # creates months based on the month names you chose
        i += 1                                                 # adds 1 to the last id before assigning it to the month
        print(f'      <outline name="{month}" ID="{i}">')      # creates the month
        md = month_days
        n = 1                                                  # this is the day of the month
        while md > 0:                                          # loop to create the days of the month in the calendar
            if s >= x:                                         # this is to reset the days to 0 after reaching the last one or correct if shift is higher than the number of days
                s -= x
                continue
            i += 1                                                                   # adds 1 to the last id before assigning it to the day
            print(f'        <outline name="{n} - {day_names[s]}" ID="{i}"/>')        # creates the day in the format "Day_number - Day_of_the_week" for example: "25 - Monday"
            n +=1
            s += 1
            md -= 1
        print(f'      </outline>')                                                   # closing tag for month
    print(f'    </outline>')                                                         # closing tag for year

if __name__ == '__main__':
    calendar_year(5, 2024)    # first argument is the ID, second argument is the year

I just copy-paste the output in the world.opml file. It just creates a single year and the months will be all of the same numbers of days. Parameter Id I take it from the last item created.

This is just a quick code I put up for making my own calendars. I have no idea how to add it to Manuskript. I'm not that good with coding.

FastusNumen commented 6 months ago

I tried to add the moon phases. Can be toggled off by putting 0 in moon().

Here how it looks like:

Screenshot 2023-12-24 195951

I didn't know how to add the moon symbols but it can be easily inserted in the code.

Here's the code:

'''
This is how the lunar phases are written. Change the string to see different results on the day name.
'''
new_moon = "(----)"
waxing_crescent_moon = "(---|)"
first_quarter_half_moon = "(--||)"
waxing_gibbous_moon = "(-|||)"
full_moon = "(||||)"
waning_gibbous_moon = "(|||-)"
last_quarter_half_moon = "(||--)"
waning_crescent_moon = "(|---)"

waxing_moons = [new_moon, waxing_crescent_moon, first_quarter_half_moon, waxing_gibbous_moon]
waning_moons = [full_moon, waning_gibbous_moon, last_quarter_half_moon, waning_crescent_moon]

day_names = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] # names of the days of the week
month_names = ["Month 1", "Month 2", "Month 3", "Month 4", "Month 5", "Month 6",
               "Month 7", "Month 8", "Month 9", "Month 10", "Month 11", "Month 12"]        # names of the months

x = len(day_names)                                                                         # get the number of days in a week
month_days = 30                                                                            # number of days in a month (all the same amount of days for now)
moon_cycle = 0                                                                             # the number of days the moon takes to go from full to new
moon_phases_per_cycle = []                                                                 # how many times new_moon, full_moon, etc are repeated. It depends on the moon cycle

'''
This is the function to create the moon phases in the year.
@:param mc: The number of days the moon takes to go from new moon to full moon
'''

def moon (mc):
    global moon_phases_per_cycle                            # made it global since it's used in the other function
    global moon_cycle                                       # same as above
    moon_cycle = mc
    divided = moon_cycle // 8                               # finds out how many times a certain phase must be repeated
    remaining = moon_cycle % 8                              # finds out if there's days left to cover
    waxing_number = remaining // 2                          # assign half of the remaining days to the waxing moons
    waning_number = remaining - waxing_number               # the other half is assigned to the waning moons

    for moons in waning_moons:                              # loop to assign the waning moons to the moon phases in the cycle
        for n in range(divided):
            moon_phases_per_cycle.append(moons)
        if waning_number > 0:                               # this is the loop for the remaining moon phases
            moon_phases_per_cycle.append(moons)
            waning_number -= 1

    for moons in waxing_moons:                              # loop to assign the waxing moons to the moon phases in the cycle
        for n in range(divided):
            moon_phases_per_cycle.append(moons)
        if waxing_number > 0:                               # this is the loop for the remaining moon phases
            moon_phases_per_cycle.append(moons)
            waxing_number -= 1

'''
Creating a fictional calendar. Its just for a single year.
@:param id: The ID of the last item created. Just create an item before doing this to see which is the last id in the world.opml file.
@:param year: The year you want for your calendar
@:param day_shift: The day of the week the year starts on
@:param month_days: The number of days in a month. For now all months have the same number of days.
@:param moon_shift: Shifts the days of the moon phases
'''

def calendar_year(id, year, day_shift, month_days, moon_shift):
    global i                                                   # creates global variable i to assign all ids to the calendar months and days
    i = id + 1                                                 # assign the id to the global variable i incremented by 1
    mnsh = moon_shift
    print(f'    <outline name="Year {year}" ID="{i}">')        # creates the year
    for month in month_names:                                  # creates months based on the month names you chose
        i += 1                                                 # adds 1 to the last id before assigning it to the month
        print(f'      <outline name="{month}" ID="{i}">')      # creates the month
        md = month_days
        n = 1                                                  # this is the day of the month
        while md > 0:                                          # loop to create the days of the month in the calendar
            moon_phases = ""
            if moon_cycle != 0:
                while mnsh >= moon_cycle:  # if you shift the moon cycle by too much this while loop corrects it
                    mnsh -= moon_cycle
                moon_phases = moon_phases_per_cycle[mnsh]
            if day_shift >= x:                                         # this is to reset the days to 0 after reaching the last one or correct if shift is higher than the number of days
                day_shift -= x
                continue
            i += 1                                                           # adds 1 to the last id before assigning it to the day
            print(f'        <outline name="{n} - {day_names[day_shift]} '
                  f'{moon_phases}" ID="{i}"/>')        # creates the day in the format "Day_number - Day_of_the_week" for example: "25 - Monday"
            mnsh += 1
            n +=1
            day_shift += 1
            md -= 1
        print(f'      </outline>')                                                   # closing tag for month
    print(f'    </outline>')                                                         # closing tag for year

if __name__ == '__main__':
    moon(30)                  # the argument is the number of days it takes from full moon to new moon. If you write 0 it won't show the moon phases
    calendar_year(5, 2024, 0, 30, 17)    # first argument is the ID, second argument is the year, third is in which week day the year starts on, fourth the number of days in the months, fifth the shift in the moon phases
TheShadowOfHassen commented 6 months ago

I agree with @obw, I think it's better as a plugin. Also, when the GTK port has plugins, I'll add it myself. I've already written a reasonably powerful fantasy calendar program in GTK.

FastusNumen commented 6 months ago

It's fine, so long as it's not hard to work with it. Can't wait to use it. In the meantime, I'll make do with my messy code. xD

Reaper10 commented 6 months ago

@FastusBellrune this mite be useful as in #934

TheJackiMonster commented 6 months ago

Most problems with calendars and timestamps for metadata of your text files is that there is no solution which works for everyone. For example there could be differences in timezones between chapters. There could be different moon phases, multiple suns or different concepts of time calculation in a story. Especially when you enter science fiction with space ships which can escape a local planet the whole concept of calendars breaks down. But calculating time in general as portions of radioactive particles splitting (as physics does it) is very likely unpractical for many authors.

So even if we had a plugin for calendars, I would think that would need the ability to convert between multiple configurations.

But I personally also noticed calendars and absolute dates to be quite unpractical when you don't write a story with historic background. So calculating time relative might make sense in many cases.

However I still see huge issues for scenes where either your characters may travel through time or simply remember something of their past. Because then the timestamp for that scene might be set as the date from their memory or it might be set as the date they remember.

I also want to remind that even if you have a system which works for you. The chronological order might still differ from the narrative order for your story. Which implies that we would still need at least two different graphs showing the order of your scenes when we add a timeline that uses some kind of calendar/time system.

Don't get me wrong. If we end up with a plugin system in the end that gives most authors an option to add time information to their plot, that's great. But I want to point out why I don't think it's something which ends up being a core functionality of Manuskript.