Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SkillListActivity: generate skill card images instead of using scans #9

Open
kuhrusty opened this issue Dec 23, 2018 · 90 comments
Open
Labels
enhancement New feature or request

Comments

@kuhrusty
Copy link
Owner

Using scanned skill cards is dumb; it would be better if each skill had the text of its effects (plus some markup to indicate which bits were effects/actions you could use during Rest, or in Assault or Guard stance--the stuff on p. 11 of the rules), and then we render card images accordingly at runtime. (Like, the Immunity skill is two sentences; it's grossly inefficient to have four differently-scaled photos of the card.) However, that means someone will have to write the code to do that. (It would be interesting code...)

@kuhrusty kuhrusty added the enhancement New feature or request label Dec 23, 2018
@kuhrusty
Copy link
Owner Author

Looking into this for issue #22, the card scans are a hair over 90% of the APK's size. That's ridiculous.

@sgbeal
Copy link

sgbeal commented Jun 20, 2019

OTOH, it's only ridiculous because of the 100M Play Store limit.

@kuhrusty
Copy link
Owner Author

Let the record reflect that Stephan has volunteered to enter all card data.

@sgbeal
Copy link

sgbeal commented Jun 20, 2019

That's an unsubstantiated rumor intended to sow... oh, wait. Indeed, i did. i'll have my people call your people.

@sgbeal
Copy link

sgbeal commented Jun 20, 2019

Okay, my people called and said that we have one slight problem they need your people to agree on before this goes too far...

Within the text of skills/masteries, we sometimes see glyphs, e.g.: "If you pass & the GP {GLYPH} cost of the Item..."

Your people are going to want to include the intended glyphs, rather than just the words. To that end, my people suggest a common notation for the glyphs, e.g. {:GP:} or {:WOUND:} or some such. My people, however, are not emotionally invested in any one solution, so they'll leave that up to your people to decide.

The traits denoting when a skill can be used don't have the same problem because they're standalone - they aren't embedded in free-form text. e.g. Alchemy has the "town" trait, which can simply be noted as "town" or "Town" or hashtag "#town", without requiring any weird markup. There are cases where we'll need to denote either/or, and my people suggest using a double-pipe for that for the time being, with multiple traits on either side of the pipe being ANDed with each other, effectively treating outside metaphysical || inside perilous as (outside & metaphysical) || (inside & perilous). (They recommend a double-pipe to avoid any potential confusion with the upper case letter I, depending on the current font being used. Luckily, English has no words which use two upper-case letter I's, and only one(?) which uses two lower-case i's.) Maybe a backslash would suffice: a b / c d. Whichever.

At your earliest convenience, please have your people let my people know how your people would like that handled.

@sgbeal
Copy link

sgbeal commented Jun 20, 2019

Hmmm. It seems that some cards use mixed normal/italics fonts. Do we care or do we want to use, e.g., markdown notation for those?

Draw a card from the Loot deck. If it is an Ally, Item, or Service, you may purchase it for the normal cost (otherwise discard it).

@sgbeal
Copy link

sgbeal commented Jun 20, 2019

Also... i'm just now seeing typos i've never noticed (e.g. Bleeding). Do we retain them or fix them? (Edit: no wonder i haven't noticed the Bleeding typo before - Mean Streets hasn't yet arrived in the EU. It's due to start fulfillment from the UK today, so it "should" be here by Wednesday/Thursday of next week.)

@sgbeal
Copy link

sgbeal commented Jun 20, 2019

Okay, it will take me more than half a day, but this is doable. i'm still on medical leave, so time is not an issue - just finding the energy/drive to get it done is ;). It's also good exercise for my still-recovering hands.

image

@sgbeal
Copy link

sgbeal commented Jun 20, 2019

Milestone - the list of skills has been transcribed. There's certain card-specific metadata which still has to be hashed out, but the text is all captured in a spreadsheet. The Masteries are next (but probably not today) and then the Danger Cards. There are apparently a whopping 60 skills. Who knew?

@sgbeal
Copy link

sgbeal commented Jun 21, 2019

My People have called to inform yours that both the Skill and Mastery lists are now transcribed. The skill list, as seen in the screenshot above, had to be reworked to include the traits in the skill markup, instead of a separate piece of card-level metadata, because many skills/masteries use multiple paragraphs, each with their own trait(s) (though the traits are often identical for 2 paragraphs, which is kind of weird, but that's what we have).

i have been able to capture almost all of the formatting patterns within the markup/text structure (sometimes it's implicit, e.g. a block starting with a stance will have background color Y, where Y is dependent on the card's background color), but there are a couple outliers where there is no apparent rhyme or reason as to why a given paragraph is a different color from the one before it. That can all be cleaned up at our leisure, now that the content of each has been captured. We can, e.g. break each paragraph into a separate JSON object and add any traits as a property of that object, rather than in markup. For some cases that separation would be problematic, though: an example is an attribute bonus, which can appear either at the end or beginning of given paragraph of skill text, but is always styled one particular way (distinct from the paragraph's color).

@sgbeal
Copy link

sgbeal commented Jun 21, 2019

While transcribing the Danger cards, it just occurred to me that it would be hilarious to add support for Google's text-to-speech to read them. (In your best Google Assistant voice: "Cults of death practice necromantic rites & dead things hunt the living who dare enter THE CATACOMBS")

@sgbeal
Copy link

sgbeal commented Jun 21, 2019

My People have just informed me that the transcription of the Danger cards is done, but that one (Highlands Outbreak) has a universe-breaking bug which i was forced to fix:

https://www.boardgamegeek.com/thread/2224679/

Is there really no Badlands Outbreak card? Are they immune to the plague?

So... we've now got all Skills, Masteries, and Danger cards in a gdocs spreadsheet, the link to which i don't yet want to post in a public forum. From there we can easily [enough] restructure it as JSON or TIFF or VCAL or whatever the cool kids are using nowadays.

Seriously, though: think about adding text-to-speech for the danger card flavor text. That would be a hoot. Edit: i just tried it via one of the copy/paste TTS websites and it is indeed funny. In its own way. Not very thematic, though.

@kuhrusty
Copy link
Owner Author

Holy cow, excellent, except for the part about NOW I HAVE TO WRITE THE CODE. (Hopefully this didn't set back your recovery!)

Is there really no Badlands Outbreak card? Are they immune to the plague?

Well, the Badlands are so awful that, if a location does have plague, who can tell?

By the way, it is great + hilarious to be coding with you again. What were the odds??

@kuhrusty
Copy link
Owner Author

One complication here is that, although we're including the font which contains Dungeon Degenerates glyphs (app/src/main/res/font/dungeon_degenerates_icons.otf from here--that's how I'm displaying the XP lightning bolt in SkillListActivity), I don't know how to embed one of those icons in the middle of a TextView (SkillListActivity uses one TextView with @font/caslon_antique_bold for the number of XP, and a second TextView with @font/dungeon_degenerates_icons for the icon).

We may wind up using WebView or something to lay out the card text/icons... which seems horrifyingly heavy-weight, but maybe I'm wrong about that.

@sgbeal
Copy link

sgbeal commented Jun 23, 2019

i admit that such complications didn't occur to me at the time... but now they'll haunt my dreams like a badly-painted mini (that was actually last night's dream). The web view doesn't sound like a half-bad idea. Or is it... dare i say it... a morbad idea? One major benefit of that is that it would, IMO, be easier to control the text size. Some cards need smaller fonts because they are verbose. A simple css tweak would be all that's needed, rather than you writing code to calculate text sizes, word-wrapping, etc.

We also have all of the individual glyphs in SVG format (i'll drop you a link to that), so we could embed those as image tags.

@sgbeal
Copy link

sgbeal commented Jun 23, 2019

With the webview: perhaps... just perhaps... the card-rendering/HTML-generating code could be implemented in JavaScript, taking the card data from a JSON object. That's something which could easily be developed outside of the app, which should simplify the dev process greatly. It would also allow us, Goblinko permitting, to host an online card db using the same code (perhaps a web-based variant of this app). Hmmmm...

i'm now more than half tempted to give that a go. First i'd need to convert the spreadsheets to JSON. Parsing the markup from JS also wouldn't be all that difficult, eliminating the need to write Java code for that.

Here's a video which allegedly demonstrates how to turn on JS for the webview (JS is off by default):

https://www.youtube.com/watch?v=H8N7H6dlMCE

i don't know if there are any limitations in that sandbox which might bite us, though.

@sgbeal
Copy link

sgbeal commented Jun 23, 2019

i'm looking at converting the spreadsheet data to json. For the skill cards that's pretty straightforward except for one question: when a card has multiple paragraphs of text, do we want those all stored together as one string (with markup denoting the paragraph breaks (noting that there are 2 different kinds of paragraph breaks on the cards)) or as an array of paragraph objects which contain the string and any metadata (e.g. color and paragraph break type)? If we do the latter, we could move some of the markup, e.g. {{bold settlement}}, into separate properties of each paragraph object. i'm not sure if that would simplify anything, really, compared to leaving that info in the markup, but it would be an option.

@kuhrusty
Copy link
Owner Author

I'm leaning toward one string. Two things to test:

  • When given a fixed size in which to display itself and too much text to fit at its default-or-whatever font size, will WebView scale the text accordingly? If it doesn't, then there's almost no point in using it. (Unless maybe we scale the text ourselves using JavaScript and CSS? Yuck.)
  • Does WebView display SVGs? Some of the stuff I'm seeing looks considerably more horrible than <img src="foo.svg"/>.

@sgbeal
Copy link

sgbeal commented Jun 23, 2019

For point 1 i figured we can say "if strlen<X, use font size 1em, else if strlen<Y, use 0.8em..." etc. i suspect that would work out well enough. With JS we can dynamically (and easily) adjust the DOM element style properties.

i assume webview supports svg, since all modern browsers do, but haven't researched it.

@kuhrusty
Copy link
Owner Author

  • When given a fixed size in which to display itself and too much text to fit at its default-or-whatever font size, will WebView scale the text accordingly?

Looks like the default behavior is to add scrollbars and draw beyond the fixed size.

Now, that's not necessarily bad... in fact, it's arguably good. My intent was to have code which would render Skill cards as closely as possible to the printed cards, but... since we don't have to constrain ourselves to a fixed-size cardboard rectangle, maybe we don't want to? Would that be weird to have the text on all "cards" be the same easy-to-read size, and have to scroll to see everything on cards with lengthy text?

@kuhrusty
Copy link
Owner Author

Maybe it's an option in Settings, ha ha.

@sgbeal
Copy link

sgbeal commented Jun 23, 2019

i am ambivalent. Let's see how it goes both ways. The scroll approach requires no code but the other approach theoretically requires 5 minutes of coding, so it's tomato/tomahto. (OTOH, we all know how "theoretically" rends to work out in practice.)

i will be tied up with other stuff until Thursday (and my DD KS should arrive Tuesday or Wednesday) but will then take a serious look at converting the card data to json and implementing a renderer in JS.

@kuhrusty
Copy link
Owner Author

i assume webview supports svg, since all modern browsers do, but haven't researched it.

Looks like it does; I've got it displaying your dd-glyphs.svg as a test. (I wasn't sure because googling "android webview svg" gave some... disturbing results.)

@sgbeal
Copy link

sgbeal commented Jun 23, 2019

i didn't think to ask whether JS is on your list of Core Competencies. We don't have to use JS if you don't want to, it just seems like an easy way to simplify development for this case (i'm right at home with JS, despite its annoying quirks). The whole rendering process could be developed outside of the app and reused in a web app.

@kuhrusty
Copy link
Owner Author

JS is not my favorite, but I have done stuff with it; I just can't let anyone watch me write it, because I can't get anything done without documentation open in another window. But if you're writing it, it's fine with me!

@sgbeal
Copy link

sgbeal commented Jun 24, 2019

For later reference: it seems we can determine if a scrollbar is visible, which is info we can use to keep shrinking the font until the scrollbar goes away:

https://stackoverflow.com/questions/4814398/how-can-i-check-if-a-scrollbar-is-visible

The relevant bit is e.scrollHeight > e.clientHeight or e.scrollHeight > e.innerHeight (need to confirm which is more appropriate).

@kuhrusty
Copy link
Owner Author

kuhrusty commented Jul 9, 2019

One other fun thing about using JS instead of SVG is it makes it a little easier to support other languages. (Only a little bit; I mean, we can support language-specific SVGs about as easily as supporting language-specific card text+markup strings.) Because the game is so heavy on English text, I wouldn't have thought this would be useful, but if someone's got a site where entering native language translations will give them a set of native language cards they can print for their copy of the game...

@kuhrusty
Copy link
Owner Author

kuhrusty commented Jul 9, 2019

Hey, I'm sorry; I'm still unclear on how to actually use the JS.

If I add a function foo to the global.morbad block in dd-card-renderer.js (using the version from morbad-card-demo.zip), I can call it from Java, passing arbitrary arguments:

skillWV.loadUrl("javascript:morbad.foo('skill', " + quote(name) + ", " +
                                       quote(textPlusMarkup) + ", " + xp ...

but what function in dd-card-renderer.js should I be using? Or is there some other way I should pass the skill data to JS?

(In the morbad-demo.js you sent me, I see the selectSkill function, but if the intent is that I copy that into dd-card-renderer.js and modify it to take the skill name + text + cost + req etc., the odds of me succeeding are extremely low.)

@sgbeal
Copy link

sgbeal commented Jul 9, 2019

Man, you're asking difficult questions. As my first programming mentor used to say (on the topic of documenting software):

If it was hard to write, it should be hard to use!

Actually... i have to go check.

global.morbad doesn't actually display anything - that object is app-independent. The better place to look for how to use it is morbad-demo.*, one-card.* (added last night as part of the chrome export automation, but i now recognize that that script does about 2x as much work as it really needs to), and mega-deck.* (also added last night). (i just pushed to the gdrive, so that's all up to date.) morbad-demo.* was initially intended to provide you with an app stub with which the Java app would interact. i'll be happy to extend that code with the features you're looking for.

but if the intent is that I copy that into dd-card-renderer.js and modify it

Definitely not. That file is best considered "upstream code". i'd be glad to do a Google Hangout session to help you get it running (i have... somewhere... a Skype account, and could dig that out if preferred). i'll go through morbad-demo in a few minutes and try to see it from the perspective of a Confused Californian character with the Maintain Others' Code Weakness, and add methods to it as seems appropriate.

Ideally you'll just need to:

  • drop in the core library, unmodified: dd-card-renderer.{js,css}
  • drop in an app stub which loads d-c-r and provides the Java/JS glue: something fundamentally similar to morbad-demo.*, and i'll help formulate that code so that it requires as little friction as possible for the Java side.

From webview:

  • Optionally (but recommended) register some dummy object with the JS engine so that the JS code can see "ah, we're running in Morbad Scorepad" (or is it, "AAAAAHHHHHH! We're running in Morbad Scorepad!!!"?) and, if needed, act appropriately. e.g. the font is currently loaded from JS, not CSS, and the JS code tries to figure out if it's running in your app. See the top-most function in dd-card-renderer.js (search for your name).

  • Then open your equivalent of morbad-demo.html (yours will be smaller - something along the size of one-card.html). That will init everything and provide whatever Java-to-JS API we stick in that glue code.

@sgbeal
Copy link

sgbeal commented Jul 9, 2019

PS: i'm putting together a stub bit intended for this use as the glue. i'll post back when it's done.

@kuhrusty
Copy link
Owner Author

kuhrusty commented Jul 9, 2019

  • Optionally (but recommended) register some dummy object with the JS engine so that the JS code can see "ah, we're running in Morbad Scorepad" (or is it, "AAAAAHHHHHH! We're running in Morbad Scorepad!!!"?) and, if needed, act appropriately. e.g. the font is currently loaded from JS, not CSS

I did that bit (including an alternate version of dd-card-fonts.css with different font URLs, although it's not checked in), and confirmed (just because I was curious to see whether I could) that, when I call a JS function from Java, I can have it turn around and call a Java method from JS. (That works.)

  • Then open your equivalent of morbad-demo.html (yours will be smaller - something along the size of one-card.html). That will init everything and provide whatever Java-to-JS API we stick in that glue code.

That's where things go wrong; I lack the JavaScript chops to figure out what I need to do from looking at morbad-demo.{js,html}. Maybe I can chalk that up to looking at an out-of-date version of the code, rather than looking at the code with an out-of-date version of the brain.

@sgbeal
Copy link

sgbeal commented Jul 9, 2019

i'm about 80% done with your stub file. Just need to remove some older crufy from the overblown demo, then we'll be set.

JSON dialect: do you have your own are are we going to use dd-card-renderer's? If the former, we simply need a method in JS which will transform your dialect to mine on the fly.

Looking at your skills.json, i currently envision that Java uses that copy and JS uses its own (which includes all the text). Only the IDs need to match (which they already do, unless i'm very sorely mistaken, but i'll confirm that later). That will give you the ability to do theJsAPI.setSkill(id || JSON object (for user-supplied JSON in the d-c-r dialect)).

@kuhrusty
Copy link
Owner Author

kuhrusty commented Jul 9, 2019

JSON dialect: do you have your own are are we going to use dd-card-renderer's? If the former, we simply need a method in JS which will transform your dialect to mine on the fly.

If I can pass in the various elements (name, text+markup, XP cost, copyright flag, etc.) as individual arguments, that's best, but if I need to assemble those into a single JSON object in the expected format, that's fine too.

Looking at your skills.json, i currently envision that Java uses that copy and JS uses its own (which includes all the text). Only the IDs need to match (which they already do, unless i'm very sorely mistaken, but i'll confirm that later). That will give you the ability to do theJsAPI.setSkill(id || JSON object (for user-supplied JSON in the d-c-r dialect)).

My hope is that, for each skill, I can copy the skill text+markup, mastery text+markup, and optional copyright flag (and anything else I'm forgetting) into the existing skills.json files, and then pass those text+markup strings into JS (or, if necessary, assemble a JSON object in the format expected by dd-card-renderer; if I do that I'll cache the resulting string on the Java Skill object). If I can stick with one skill file per expansion, that's what I'd prefer.

@sgbeal
Copy link

sgbeal commented Jul 9, 2019

If I can pass in the various elements (name, text+markup, XP cost, copyright flag, etc.) as individual arguments, that's best, but if I need to assemble those into a single JSON object in the expected format, that's fine too.

The JS API can't currently handle them piecemeal - it eats only healthy, whole-wheat JSON objects. i'll add "split up card processor" to the TODO list.

My hope is that, for each skill, I can copy the skill text+markup, mastery text+markup, and optional copyright flag (and anything else I'm forgetting) into the existing skills.json files, and then pass those text+markup strings into JS

That would also be fine. If you use a different structure, we'll need one more tiny JS function to translate the structure, but that's trivial.

If I can stick with one skill file per expansion, that's what I'd prefer.

There's no reason in the world you can't. The JS-side copy is currently one big bundle, for simplicity (and because the spreadsheet-to-JS converter doesn't have enough information to split them), but if you're passing the JSON from Java then it's entirely your business how it's split up.

Long live The Stub

The app stub is "done" (for a given definition of done). Please check the gdrive for:

  • morbadscorepad.{css,js,html} = these are your stub app. See the JS code at the bottom of the HTML file for how to set its display options to suit you (toggle between 1/2 cards and enable/disable tap-to-flip-card). That exposes a new global symbol named, for lack of a better word, morbadscorepad (i'm assuming you exposed your Java object as MorebadScorepad (CamelCase) - if not, i'll need to change dd-card-renderer.js to use the name you are exposing.)

  • dd-card-renderer.{css,js} = core library (has been modified in the past few hours)

  • The fonts and glyphs directories (new glyphs were added last night).

  • dd-skills-masteries.js = the core skill db, in the form of a JSONP callback which installs itself in the global morbad object. This is currently installed only so that the JS stub has data to work with. Once the stub is dropped into your app, you can remove/comment this line in morbadscorepad.html:

    <script src="dd-skills-masteries.js">...</script>

morbadscorepad.js will use that data if it's available, but doesn't require it. HOWEVER, if you want to pass skill IDs (as opposed to whole objects) to kuhrusty.setCurrentSkill() then those IDs have to be available to it or it will throw an exception: see the setSkills() and addSkill() methods for how to add a list of skills or an individual skill, respectively. The input objects do not strictly need "id" fields: addSkill() will generate an ID if a given object doesn't have one: name, lowercased, with \W characters removed and \s+ changed to underscores. That seems to be consistent with your JSON-side IDs.

i think that this provides everything the Java world needs.

@kuhrusty
Copy link
Owner Author

kuhrusty commented Jul 9, 2019

(i'm assuming you exposed your Java object as MorebadScorepad (CamelCase) - if not, i'll need to change dd-card-renderer.js to use the name you are exposing.)

Goodness, no, I went with MorbadScorepad. :P

@sgbeal
Copy link

sgbeal commented Jul 9, 2019

Just to confirm: i just ran both of our jsons through a script and they share identical IDs. However, your skills.json has // comments, which are non-standard and not supported by many JSON parsers (including mine), so i had to strip them out in order to test it.

const jR = s2.json.parseFile('rusty.json'),
      jS = s2.json.parseFile('dd-skills-masteries.json'),
      db = {};
var count = 0;
foreach(@jS=>o) db[o.id] = ++count;
foreach(@jR=>o) affirm db[o.id];
affirm 60 === count;
print("done");

@kuhrusty
Copy link
Owner Author

kuhrusty commented Jul 9, 2019

However, your skills.json has // comments, which are non-standard and not supported by many JSON parsers

(Yeah, Android Studio complains about them too; I strip them out before passing them on to Gson or whatever.)

@sgbeal
Copy link

sgbeal commented Jul 9, 2019

(i'm assuming you exposed your Java object as MorebadScorepad (CamelCase) - if not, i'll need to change dd-card-renderer.js to use the name you are exposing.)

Goodness, no, I went with _Morbad_Scorepad. :P

i've made that typo no less than 8 times today :/.

@sgbeal
Copy link

sgbeal commented Jul 10, 2019

If you would, please updated to the latest dd-card-renderer.*. i've just improved the auto-sizing of the skill names significantly.

Also, just fyi, in case this is something you're into: i just found that if you run that JS through a minifier, it will shrink to <20k.

Edit: use mega-deck.html to get a quick visual overview.

@sgbeal
Copy link

sgbeal commented Jul 10, 2019

i need an opinion...

if i move that damned dangling "XP Val" widget out of the way, i can get much tighter automated font-size fits:

image

Is that evil of me?

The problem is that accounting for that widget is essentially impossible because we don't know how large of a gap lies between the bottom of the last line of text and the bottom of the skill text area. When calculating the font size (which i've just sped up a great deal, but am still testing), we're left guessing how much of a gap we're leaving at the end of the card body.

Moving that widget to the top, where it will never overlap anything, eliminates that problem, but of course has the problem of "potential cognitive dissonance".

@kuhrusty
Copy link
Owner Author

Moving that widget to the top, where it will never overlap anything, eliminates that problem, but of course has the problem of "potential cognitive dissonance".

Oh, man, that's a tough call; it really does fit better up by the "Skill" string.

I was going to say maybe you could leave it down at the bottom and just assume the bottom bit of the card was unusable, but I see the original Find Weakness wraps around the XP area. (Same with Levitate.)

In figuring out how much space you have, can you assume a fixed width for the XP area, and that only one line will have to wrap around the XP area? So, with 10 lines, your total available space is 9 * card width + last-line-width; with 11 lines, your total available space is 10 * card width + last-line-width, etc.?

@kuhrusty
Copy link
Owner Author

OK, I've got it displaying just the Skill card in one WebView, and just the Mastery card in the other WebView, and I've got them updating when a new skill is selected (using complete JSON passed in); the only thing I need to goof with is the scale (they don't fit in the available space, and although you can scroll around, you can't zoom in/out). I assume I can fix that by messing with stuff in the HTML or CSS.

@sgbeal
Copy link

sgbeal commented Jul 10, 2019

It's a non-issue now. i came up with some tricks which now allows it to work Pretty Damned Well (a tighter fit than before), despite that XP Val widget (and just copied that to the gdrive). In short...

As before, the algorithm basically works like this: set a massive font size and check for overflow. If it's not overflowing, great. If it is, reduce the font size one step and try again. One "step" means trying the next-lower font-size-XXX CSS entry, where XXX is an increment of 5 ranging from 75 to 400 or so. font-size-100 is the "100% size" of 12pt. Each "step" of XXX equates to 0.6pt. (See the bottom of dd-card-renderer.css.

With that in mind, the changes are...

  • resize like before, except sped it up considerably by checking whether the overflow size is significantly larger than the element's visible size, and skipping over a proportionately large number of font steps. (Pedantic caveat: it can actually be much slower now, depending on how many cards are on the screen: if all 60 are shown on the same page, it can take a whopping 11 seconds to recalc them. If only 2 cards (skill/mastery pair) are shown, it takes about 8ms each on my laptop.)

  • when it's done, measure how much space we have left at the bottom of the element. i found a way to do that by injecting another element which is large enough to guaranty overflow, then measuring the delta of the containing element's scrollHeight. If that gap is smaller than the XP widget's height, or is smaller than 1/3rd of the font's height, then back up one font step and try again (repeatedly, if necessary, though that "shouldn't" happen). (The 1/3rd font height check is basically to force some gap on the bottom for "dangling" letters like "g", "q", and "y", so they don't bleed out of bounds.)

Here's a very interesting case which demonstrates how the XP val thingie is a problem:

image

Note the ostensibly large gap at the bottom of the Skill. i double-checked that by hand and it's actually doing exactly the right thing: if we increase the font size by 1 step (0.6pt) then it gets pushed down 1 line, which overlaps the top of the XP widget by a fraction of a hair. The text does not actually overlap because it's only 1 word, but we can't know that at this level - we can only see how far down the text goes. So the algo, at the end, says, "you're overlapping - back up one font step, please." (Except that it doesn't say please.)

Anyway... this would have been tons easier (like 10+ hours easier) if that damned XP val widget wasn't a potential overlap risk. The up-side to that time sink was that i learned a couple new JS tricks, namely...

For posterity's sake, since i can't just link to my private repo from here until i get permission from goblinko, he are Steve's New Tricks:

/**
   Given a DOM element (which is currently really in the DOM),
   this function calculates the distance, in pixels, from the
   bottom of the bottom-most child element to the bottom of e's
   visible portion.

   If e.scrollHeight>e.clientHeight then e is already overflowing
   and this function returns 0.

   And, now that i think about it, this might require a certain
   e.style.overflow setting, but i haven't confirmed that.

   e is modified briefly but is returned to its previous state
   before this function returns. Hopefully nobody notices the
   change (e.g. via weird DOM event handlers or some such).
*/
const calcGapToBottomOfElem = function f(e){
    if(!f.e){
        f.e = document.createElement('div');
        let s = f.e.style;
        s.margin = s.padding = 0;
        s.minWidth = s.maxWidth = '1px';
        s.whiteSpace = 'pre';
        s.overflow = 'hidden';
        f.e.innerText =
            '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n';
    }
    if(e.scrollHeight > e.clientHeight) return 0 /* already overflowing */;
    f.e.style.minHeight = f.e.style.maxHeight = (e.clientHeight+10/*arbitrary*/)+'px';
    e.appendChild(f.e);
    var rc = e.clientHeight - (e.scrollHeight - f.e.clientHeight);
    e.removeChild(f.e);
    return rc;
};

/**
   Given a relative font size (the XXX part of the font-size-XXX
   CSS classes used by this toolkit), this returns the pixel
   height of that font (or very close to it).
*/
const relativeFontSizeToPixels = function(fs){
    return Math.ceil( (fs/100 * 12/*assume .font-size-100 === 12 pts*/)
                      * 1.3333 /*convert to px (or very close to it)*/ );
};

@sgbeal
Copy link

sgbeal commented Jul 10, 2019

OK, I've got it displaying just the Skill card in one WebView, and just the Mastery card in the other WebView, and I've got them updating when a new skill is selected (using complete JSON passed in); the only thing I need to goof with is the scale (they don't fit in the available space, and although you can scroll around, you can't zoom in/out). I assume I can fix that by messing with stuff in the HTML or CSS.

Try adding this CSS to each card:

transform-origin: top left;
transform: scale(0.8); // adjust to suit

If needed, we can add a JS function which takes a number from java, and scales them appropriately.

@sgbeal
Copy link

sgbeal commented Jul 10, 2019

OK, I've got it displaying just the Skill card in one WebView, and just the Mastery card in the other WebView

Also: why two? It seems like it would be easier to just display both in the same webview.

@sgbeal
Copy link

sgbeal commented Jul 10, 2019

In the off chance that you have already updated dd-card-renderer.*, please do so again. i've just committed a version which gets a near-perfect fit (and it would be absolutely perfect if it weren't for that damned XP widget) and improves the performance a bit.

@sgbeal
Copy link

sgbeal commented Jul 11, 2019

(i'm assuming you exposed your Java object as MorebadScorepad (CamelCase) - if not, i'll need to change dd-card-renderer.js to use the name you are exposing.)

Goodness, no, I went with _Morbad_Scorepad. :P

It gets worse...

while creating a new repo for the pending public release (shorn of the skill database), i accidentally typed...

morbard-card-tools

:-/

i shall accept my punishment with grace.

@sgbeal
Copy link

sgbeal commented Jul 11, 2019

Minor forewarning: i'm re-orging and renaming the files for the upcoming public repo and the CSS file will be restructured/renamed:

morbad.d/  # all lib code/assets
    *.js, *.css # go directly in this dir
    fonts/  # font files
    glyphs/ # icons

dd-card-renderer.* will become morbad-card-tools.* and the font-loading CSS will be morbad-card-tools-fonts.css, but you are not required to use that name on the Android side, as we load a separate URL if we detect the presence of window.MorbadScorepad.

The intentions of this reorg is basically (A) to move everything out of the top-most directory, (B) simplify updates to client apps (several are in the works, and they should just have to update/replace this whole dir for new versions), and (C) standardize how the sources/CSS files are included from client code and how the URIs for the icons and dynamically-loaded CSS files are used within the library code (because they are relative to the including HTML file, not the being-executed JS file).

@sgbeal
Copy link

sgbeal commented Jul 11, 2019

Aaaaallllllrightythen....

My "migration" is complete and i've now got a public repo without any of Goblinko's copyrighted data in it (except the glyphs, for which we have a license):

https://fossil.wanderinghorse.net/r/morbad-card-tools/

There are at least 2 options for you, in terms of keeping up to date with the code:

  1. See that page for download instructions which always pull the latest trunk. If you want to use that approach, i'll email you a stable link which always downloads the latest one (i don't want to post that here because Bots may then steal all my bandwidth).

  2. Ideally, using the Fossil SCM client, but i wouldn't expect that from someone who doesn't already use fossil. For posterity's sake, that would look something like this:

    clone the repo:

    fossil clone https://fossil.wanderinghorse.net/r/morbad-card-tools bogo.fsl

    Open it:

    mkdir bogo; cd bogo; fossil open ../bogo.fsl

    Pull latest updates periodically:

    fossil pull

The Code is in the morbad.d directory: that whole directory is intended to e copied as-is for client apps. All of the files in the top-most directory are simply clients of that library-level code, and they can be run as-is by opening them from a local checkout or unzipped copy.

@kuhrusty
Copy link
Owner Author

One funny thing about using SVGs instead of rendering the cards in JS is that it probably means we could use the real Dominican font (issue #7). Its license says it's free to use; you just can't distribute the font itself. But if you use the font to generate an SVG which converts the letters to paths, you're not distributing the font...

(I do prefer to stick with the JS, though, because I think it gives us some flexibility; we could give the user some control over how we render the cards at runtime.)

@sgbeal
Copy link

sgbeal commented Jul 13, 2019

Apropos fonts... i played around today with swapping out fonts, just to see if the font size calculation could deal with it. That part worked great, but the rest of the card (headers, "REQ", req. list) were all mangled. Our font is "unusually small". That is, its 12pt is smaller than an average 12pt. Swapping fonts will require, at the least, that we include, for each font, CSS overrides which set the font size for those UI elements. Alternately, we dynamically recalc the font for every widget on the card. Unfortunately, the recalc algorithm is very dependent on the structure of ,dd-body and (you guessed it) that goddamned XP widget, so it's not something i can simply apply as-is to other UI elements. Fixing/expanding that is on my TODO list (but right now i'm working on the backend save/load infrastructure).

@kuhrusty
Copy link
Owner Author

I have the latest version from fossil, but it breaks the earlier morbadscorepad.js you sent me (it's trying to call morbad.optimizeSkillElemFontSize(), which no longer exists?). After reading the documentation & some of the code, I'm still not clear on what I should be doing--do I need to copy stuff like skill-editor.html's "timing kludge for font optimization" into the app? I was hoping to have just a page with the DOM and the JS+CSS links, and pass a JSON skill/mastery into a library function to get it to display.

@sgbeal
Copy link

sgbeal commented Jul 14, 2019

Doh - that was renamed and i think i added an optional parameter to it. i'll look at that in the next hour or so and get you a new demo app.

About the timing kludge thing: the font calculation cannot work until the element is in the DOM. Even then, depending on whether the DOM is updated with the element's current state or whether it's waiting on JS to stop processing so the DOM can update, the font calculation may or may not be complete. The "force update" is just a second pass, once the card is in full view.

That font update is not needed at all if the cards are outfitted with known-working fontSizePercent hints, but those values are very dependent on the font. i will get you an updated copy of the official skill cards with those hints pre-processed into them ASAP (the brand-spanking-new new editor makes it possible: i just have to save them one-by-one, then run an export from the db (i have a script for that part, and can possibly JS-script the first part)).

i'll be in touch in the next hour or so with a modernized demo page.

@sgbeal
Copy link

sgbeal commented Jul 14, 2019

Done with both tasks. i'll send it via email in a few minutes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants